sa1020.go (2378B)
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 Text: `Functions such as \'net.Listen\', \'net.ListenTCP\', and similar, 25 expect a valid network address in the form of host:port. The host, the port, 26 or both, can be omitted, e.g. \'localhost:8080\', \':8080\' or \':\' are valid 27 host:port pairs. 28 See https://pkg.go.dev/net#Listen for the full documentation.`, 29 Since: "2017.1", 30 Severity: lint.SeverityError, 31 MergeIf: lint.MergeIfAny, 32 }, 33 }) 34 35 var Analyzer = SCAnalyzer.Analyzer 36 37 var checkListenAddressRules = map[string]callcheck.Check{ 38 "net/http.ListenAndServe": checkValidHostPort(0), 39 "net/http.ListenAndServeTLS": checkValidHostPort(0), 40 } 41 42 func checkValidHostPort(arg int) callcheck.Check { 43 return func(call *callcheck.Call) { 44 if !ValidHostPort(call.Args[arg].Value) { 45 const MsgInvalidHostPort = "invalid port or service name in host:port pair" 46 call.Args[arg].Invalid(MsgInvalidHostPort) 47 } 48 } 49 } 50 51 func ValidHostPort(v callcheck.Value) bool { 52 if k := callcheck.ExtractConstExpectKind(v, constant.String); k != nil { 53 s := constant.StringVal(k.Value) 54 if s == "" { 55 return true 56 } 57 _, port, err := net.SplitHostPort(s) 58 if err != nil { 59 return false 60 } 61 // TODO(dh): check hostname 62 if !validatePort(port) { 63 return false 64 } 65 } 66 return true 67 } 68 69 func validateServiceName(s string) bool { 70 if len(s) < 1 || len(s) > 15 { 71 return false 72 } 73 if s[0] == '-' || s[len(s)-1] == '-' { 74 return false 75 } 76 if strings.Contains(s, "--") { 77 return false 78 } 79 hasLetter := false 80 for _, r := range s { 81 if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') { 82 hasLetter = true 83 continue 84 } 85 if r >= '0' && r <= '9' { 86 continue 87 } 88 return false 89 } 90 return hasLetter 91 } 92 93 func validatePort(s string) bool { 94 n, err := strconv.ParseInt(s, 10, 64) 95 if err != nil { 96 return validateServiceName(s) 97 } 98 return n >= 0 && n <= 65535 99 }