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