From 0c8e63ed86f8fc2da42787e0a653114490078d6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ville=20Skytt=C3=A4?= <ville.skytta@upcloud.com>
Date: Tue, 2 Aug 2022 18:16:44 +0300
Subject: [PATCH] Detect use of net/http functions that have no support for
 setting timeouts (#842)

https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
https://blog.cloudflare.com/exposing-go-on-the-internet/

Closes https://github.com/securego/gosec/issues/833
---
 README.md           |  1 +
 rules/http_serve.go | 38 ++++++++++++++++++++++
 rules/rulelist.go   |  1 +
 rules/rules_test.go |  4 +++
 testutils/source.go | 78 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 122 insertions(+)
 create mode 100644 rules/http_serve.go

diff --git a/README.md b/README.md
index d174c19..cc82439 100644
--- a/README.md
+++ b/README.md
@@ -146,6 +146,7 @@ directory you can supply `./...` as the input argument.
 - G111: Potential directory traversal
 - G112: Potential slowloris attack
 - G113: Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772)
+- G114: Use of net/http serve function that has no support for setting timeouts
 - G201: SQL query construction using format string
 - G202: SQL query construction using string concatenation
 - G203: Use of unescaped data in HTML templates
diff --git a/rules/http_serve.go b/rules/http_serve.go
new file mode 100644
index 0000000..e460b3a
--- /dev/null
+++ b/rules/http_serve.go
@@ -0,0 +1,38 @@
+package rules
+
+import (
+	"go/ast"
+
+	"github.com/securego/gosec/v2"
+)
+
+type httpServeWithoutTimeouts struct {
+	gosec.MetaData
+	pkg   string
+	calls []string
+}
+
+func (r *httpServeWithoutTimeouts) ID() string {
+	return r.MetaData.ID
+}
+
+func (r *httpServeWithoutTimeouts) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
+	if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
+		return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+	}
+	return nil, nil
+}
+
+// NewHTTPServeWithoutTimeouts detects use of net/http serve functions that have no support for setting timeouts.
+func NewHTTPServeWithoutTimeouts(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+	return &httpServeWithoutTimeouts{
+		pkg:   "net/http",
+		calls: []string{"ListenAndServe", "ListenAndServeTLS", "Serve", "ServeTLS"},
+		MetaData: gosec.MetaData{
+			ID:         id,
+			What:       "Use of net/http serve function that has no support for setting timeouts",
+			Severity:   gosec.Medium,
+			Confidence: gosec.High,
+		},
+	}, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/rules/rulelist.go b/rules/rulelist.go
index af8ea0d..b97813e 100644
--- a/rules/rulelist.go
+++ b/rules/rulelist.go
@@ -76,6 +76,7 @@ func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList {
 		{"G111", "Detect http.Dir('/') as a potential risk", NewDirectoryTraversal},
 		{"G112", "Detect ReadHeaderTimeout not configured as a potential risk", NewSlowloris},
 		{"G113", "Usage of Rat.SetString in math/big with an overflow", NewUsingOldMathBig},
+		{"G114", "Use of net/http serve function that has no support for setting timeouts", NewHTTPServeWithoutTimeouts},
 
 		// injection
 		{"G201", "SQL query construction using format string", NewSQLStrFormat},
diff --git a/rules/rules_test.go b/rules/rules_test.go
index abca817..c5a276f 100644
--- a/rules/rules_test.go
+++ b/rules/rules_test.go
@@ -102,6 +102,10 @@ var _ = Describe("gosec rules", func() {
 			runner("G113", testutils.SampleCodeG113)
 		})
 
+		It("should detect uses of net/http serve functions that have no support for setting timeouts", func() {
+			runner("G114", testutils.SampleCodeG114)
+		})
+
 		It("should detect sql injection via format strings", func() {
 			runner("G201", testutils.SampleCodeG201)
 		})
diff --git a/testutils/source.go b/testutils/source.go
index c7688e7..e1124a4 100644
--- a/testutils/source.go
+++ b/testutils/source.go
@@ -1110,6 +1110,84 @@ func main() {
 		}, 1, gosec.NewConfig()},
 	}
 
+	// SampleCodeG114 - Use of net/http serve functions that have no support for setting timeouts
+	SampleCodeG114 = []CodeSample{
+		{[]string{
+			`
+package main
+
+import (
+	"log"
+	"net/http"
+)
+
+func main() {
+	err := http.ListenAndServe(":8080", nil)
+	log.Fatal(err)
+}`,
+		}, 1, gosec.NewConfig()},
+		{
+			[]string{
+				`
+package main
+
+import (
+	"log"
+	"net/http"
+)
+
+func main() {
+	err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil)
+	log.Fatal(err)
+}`,
+			}, 1, gosec.NewConfig(),
+		},
+		{
+			[]string{
+				`
+package main
+
+import (
+	"log"
+	"net"
+	"net/http"
+)
+
+func main() {
+	l, err := net.Listen("tcp", ":8080")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+	err = http.Serve(l, nil)
+	log.Fatal(err)
+}`,
+			}, 1, gosec.NewConfig(),
+		},
+		{
+			[]string{
+				`
+package main
+
+import (
+	"log"
+	"net"
+	"net/http"
+)
+
+func main() {
+	l, err := net.Listen("tcp", ":8443")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+	err = http.ServeTLS(l, nil, "cert.pem", "key.pem")
+	log.Fatal(err)
+}`,
+			}, 1, gosec.NewConfig(),
+		},
+	}
+
 	// SampleCodeG201 - SQL injection via format string
 	SampleCodeG201 = []CodeSample{
 		{[]string{`