sa1020.go raw
1 package sa1020
2
3 import (
4 "go/constant"
5 "net"
6 "strconv"
7 "strings"
8
9 "honnef.co/go/tools/analysis/callcheck"
10 "honnef.co/go/tools/analysis/lint"
11 "honnef.co/go/tools/internal/passes/buildir"
12
13 "golang.org/x/tools/go/analysis"
14 )
15
16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
17 Analyzer: &analysis.Analyzer{
18 Name: "SA1020",
19 Requires: []*analysis.Analyzer{buildir.Analyzer},
20 Run: callcheck.Analyzer(checkListenAddressRules),
21 },
22 Doc: &lint.RawDocumentation{
23 Title: `Using an invalid host:port pair with a \'net.Listen\'-related function`,
24 Since: "2017.1",
25 Severity: lint.SeverityError,
26 MergeIf: lint.MergeIfAny,
27 },
28 })
29
30 var Analyzer = SCAnalyzer.Analyzer
31
32 var checkListenAddressRules = map[string]callcheck.Check{
33 "net/http.ListenAndServe": checkValidHostPort(0),
34 "net/http.ListenAndServeTLS": checkValidHostPort(0),
35 }
36
37 func checkValidHostPort(arg int) callcheck.Check {
38 return func(call *callcheck.Call) {
39 if !ValidHostPort(call.Args[arg].Value) {
40 const MsgInvalidHostPort = "invalid port or service name in host:port pair"
41 call.Args[arg].Invalid(MsgInvalidHostPort)
42 }
43 }
44 }
45
46 func ValidHostPort(v callcheck.Value) bool {
47 if k := callcheck.ExtractConstExpectKind(v, constant.String); k != nil {
48 s := constant.StringVal(k.Value)
49 if s == "" {
50 return true
51 }
52 _, port, err := net.SplitHostPort(s)
53 if err != nil {
54 return false
55 }
56 // TODO(dh): check hostname
57 if !validatePort(port) {
58 return false
59 }
60 }
61 return true
62 }
63
64 func validateServiceName(s string) bool {
65 if len(s) < 1 || len(s) > 15 {
66 return false
67 }
68 if s[0] == '-' || s[len(s)-1] == '-' {
69 return false
70 }
71 if strings.Contains(s, "--") {
72 return false
73 }
74 hasLetter := false
75 for _, r := range s {
76 if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
77 hasLetter = true
78 continue
79 }
80 if r >= '0' && r <= '9' {
81 continue
82 }
83 return false
84 }
85 return hasLetter
86 }
87
88 func validatePort(s string) bool {
89 n, err := strconv.ParseInt(s, 10, 64)
90 if err != nil {
91 return validateServiceName(s)
92 }
93 return n >= 0 && n <= 65535
94 }
95