github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/gosec.go (about) 1 package golinters 2 3 import ( 4 "fmt" 5 "go/token" 6 "io/ioutil" 7 "log" 8 "strconv" 9 "strings" 10 "sync" 11 12 "github.com/securego/gosec/v2" 13 "github.com/securego/gosec/v2/rules" 14 "golang.org/x/tools/go/analysis" 15 "golang.org/x/tools/go/packages" 16 17 "github.com/elek/golangci-lint/pkg/config" 18 "github.com/elek/golangci-lint/pkg/golinters/goanalysis" 19 "github.com/elek/golangci-lint/pkg/lint/linter" 20 "github.com/elek/golangci-lint/pkg/result" 21 ) 22 23 const gosecName = "gosec" 24 25 func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter { 26 var mu sync.Mutex 27 var resIssues []goanalysis.Issue 28 29 gasConfig := gosec.NewConfig() 30 31 var filters []rules.RuleFilter 32 if settings != nil { 33 filters = gosecRuleFilters(settings.Includes, settings.Excludes) 34 35 for k, v := range settings.Config { 36 // Uses ToUpper because the parsing of the map's key change the key to lowercase. 37 // The value is not impacted by that: the case is respected. 38 gasConfig.Set(strings.ToUpper(k), v) 39 } 40 } 41 42 ruleDefinitions := rules.Generate(filters...) 43 44 logger := log.New(ioutil.Discard, "", 0) 45 46 analyzer := &analysis.Analyzer{ 47 Name: gosecName, 48 Doc: goanalysis.TheOnlyanalyzerDoc, 49 } 50 return goanalysis.NewLinter( 51 gosecName, 52 "Inspects source code for security problems", 53 []*analysis.Analyzer{analyzer}, 54 nil, 55 ).WithContextSetter(func(lintCtx *linter.Context) { 56 analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { 57 gosecAnalyzer := gosec.NewAnalyzer(gasConfig, true, logger) 58 gosecAnalyzer.LoadRules(ruleDefinitions.Builders()) 59 60 pkg := &packages.Package{ 61 Fset: pass.Fset, 62 Syntax: pass.Files, 63 Types: pass.Pkg, 64 TypesInfo: pass.TypesInfo, 65 } 66 gosecAnalyzer.Check(pkg) 67 issues, _, _ := gosecAnalyzer.Report() 68 if len(issues) == 0 { 69 return nil, nil 70 } 71 72 res := make([]goanalysis.Issue, 0, len(issues)) 73 for _, i := range issues { 74 text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence 75 var r *result.Range 76 line, err := strconv.Atoi(i.Line) 77 if err != nil { 78 r = &result.Range{} 79 if n, rerr := fmt.Sscanf(i.Line, "%d-%d", &r.From, &r.To); rerr != nil || n != 2 { 80 lintCtx.Log.Warnf("Can't convert gosec line number %q of %v to int: %s", i.Line, i, err) 81 continue 82 } 83 line = r.From 84 } 85 86 column, err := strconv.Atoi(i.Col) 87 if err != nil { 88 lintCtx.Log.Warnf("Can't convert gosec column number %q of %v to int: %s", i.Col, i, err) 89 continue 90 } 91 92 res = append(res, goanalysis.NewIssue(&result.Issue{ 93 Pos: token.Position{ 94 Filename: i.File, 95 Line: line, 96 Column: column, 97 }, 98 Text: text, 99 LineRange: r, 100 FromLinter: gosecName, 101 }, pass)) 102 } 103 104 mu.Lock() 105 resIssues = append(resIssues, res...) 106 mu.Unlock() 107 108 return nil, nil 109 } 110 }).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { 111 return resIssues 112 }).WithLoadMode(goanalysis.LoadModeTypesInfo) 113 } 114 115 // based on https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/cmd/gosec/main.go#L170-L188 116 func gosecRuleFilters(includes, excludes []string) []rules.RuleFilter { 117 var filters []rules.RuleFilter 118 119 if len(includes) > 0 { 120 filters = append(filters, rules.NewRuleFilter(false, includes...)) 121 } 122 123 if len(excludes) > 0 { 124 filters = append(filters, rules.NewRuleFilter(true, excludes...)) 125 } 126 127 return filters 128 }