From 8f3ad45e1cda497195b2ae745444ad8237d8a3e1 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Sat, 24 Apr 2021 23:20:12 +0200 Subject: [PATCH] gosec: add configuration (#1930) --- .golangci.example.yml | 24 +++++++++++++++++++ go.sum | 5 ---- pkg/config/linters_settings.go | 7 ++++++ pkg/golinters/gosec.go | 37 ++++++++++++++++++++++++++--- pkg/lint/lintersdb/manager.go | 4 +++- test/testdata/configs/gosec.yml | 13 ++++++++++ test/testdata/gosec_rules_config.go | 12 ++++++++++ 7 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 test/testdata/configs/gosec.yml create mode 100644 test/testdata/gosec_rules_config.go diff --git a/.golangci.example.yml b/.golangci.example.yml index 5194c4049777..dad9449e2fa4 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -334,6 +334,30 @@ linters-settings: # reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional) local_replace_directives: false # Set to true to raise lint issues for packages that are loaded from a local path via replace directive + gosec: + # To select a subset of rules to run. + # Available rules: https://github.com/securego/gosec#available-rules + includes: + - G401 + - G306 + - G101 + # To specify a set of rules to explicitly exclude. + # Available rules: https://github.com/securego/gosec#available-rules + excludes: + - G204 + # To specify the configuration of rules. + # The configuration of rules is not fully documented by gosec: + # https://github.com/securego/gosec#configuration + # https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/rules/rulelist.go#L60-L102 + config: + G306: "0600" + G101: + pattern: "(?i)example" + ignore_entropy: false + entropy_threshold: "80.0" + per_char_threshold: "3.0" + truncate: "32" + govet: # report about shadowed variables check-shadowing: true diff --git a/go.sum b/go.sum index 1ff01e100a59..187ded4a59fb 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= -github.com/bombsimon/wsl/v3 v3.2.0 h1:x3QUbwW7tPGcCNridvqmhSRthZMTALnkg5/1J+vaUas= -github.com/bombsimon/wsl/v3 v3.2.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -123,7 +121,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -623,8 +620,6 @@ github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1g github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomarrell/wrapcheck v1.1.0 h1:zMmCL57nBBeB375MZ5i91Vde8utQTdyOn7ZId6WFIEo= -github.com/tomarrell/wrapcheck v1.1.0/go.mod h1:Bd3i1FaEKe3XmcPoHhNQ+HM0S8P6eIXoQIoGj/ndJkU= github.com/tomarrell/wrapcheck v1.2.0 h1:N1PWGT8l+6jZVTcm00kGjx9IEA8oDMSjipqY73ye5c0= github.com/tomarrell/wrapcheck v1.2.0/go.mod h1:Bd3i1FaEKe3XmcPoHhNQ+HM0S8P6eIXoQIoGj/ndJkU= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 0c3fe7e73f38..2d12c209cbcd 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -103,6 +103,7 @@ type LintersSettings struct { Gomnd GoMndSettings GoModDirectives GoModDirectivesSettings Gomodguard GoModGuardSettings + Gosec GoSecSettings Govet GovetSettings Ifshort IfshortSettings ImportAs ImportAsSettings @@ -268,6 +269,12 @@ type GoModGuardSettings struct { } `mapstructure:"blocked"` } +type GoSecSettings struct { + Includes []string + Excludes []string + Config map[string]interface{} `mapstructure:"config"` +} + type GovetSettings struct { CheckShadowing bool `mapstructure:"check-shadowing"` Settings map[string]map[string]interface{} diff --git a/pkg/golinters/gosec.go b/pkg/golinters/gosec.go index 08b3e660f8cf..328ba5ccc7c5 100644 --- a/pkg/golinters/gosec.go +++ b/pkg/golinters/gosec.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "strconv" + "strings" "sync" "github.com/securego/gosec/v2" @@ -13,6 +14,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/packages" + "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/result" @@ -20,12 +22,25 @@ import ( const gosecName = "gosec" -func NewGosec() *goanalysis.Linter { +func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter { var mu sync.Mutex var resIssues []goanalysis.Issue gasConfig := gosec.NewConfig() - enabledRules := rules.Generate() + + var filters []rules.RuleFilter + if settings != nil { + filters = gosecRuleFilters(settings.Includes, settings.Excludes) + + for k, v := range settings.Config { + // Uses ToUpper because the parsing of the map's key change the key to lowercase. + // The value is not impacted by that: the case is respected. + gasConfig.Set(strings.ToUpper(k), v) + } + } + + ruleDefinitions := rules.Generate(filters...) + logger := log.New(ioutil.Discard, "", 0) analyzer := &analysis.Analyzer{ @@ -40,7 +55,8 @@ func NewGosec() *goanalysis.Linter { ).WithContextSetter(func(lintCtx *linter.Context) { analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { gosecAnalyzer := gosec.NewAnalyzer(gasConfig, true, logger) - gosecAnalyzer.LoadRules(enabledRules.Builders()) + gosecAnalyzer.LoadRules(ruleDefinitions.Builders()) + pkg := &packages.Package{ Fset: pass.Fset, Syntax: pass.Files, @@ -95,3 +111,18 @@ func NewGosec() *goanalysis.Linter { return resIssues }).WithLoadMode(goanalysis.LoadModeTypesInfo) } + +// based on https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/cmd/gosec/main.go#L170-L188 +func gosecRuleFilters(includes, excludes []string) []rules.RuleFilter { + var filters []rules.RuleFilter + + if len(includes) > 0 { + filters = append(filters, rules.NewRuleFilter(false, includes...)) + } + + if len(excludes) > 0 { + filters = append(filters, rules.NewRuleFilter(true, excludes...)) + } + + return filters +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 0b8eda617d1b..80101224e1f5 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -112,6 +112,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { var importAsCfg *config.ImportAsSettings var goModDirectivesCfg *config.GoModDirectivesSettings var tagliatelleCfg *config.TagliatelleSettings + var gosecCfg *config.GoSecSettings if m.cfg != nil { govetCfg = &m.cfg.LintersSettings.Govet @@ -127,6 +128,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { importAsCfg = &m.cfg.LintersSettings.ImportAs goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle + gosecCfg = &m.cfg.LintersSettings.Gosec } const megacheckName = "megacheck" @@ -190,7 +192,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle). WithURL("https://github.com/dominikh/go-tools/tree/master/stylecheck"), - linter.NewConfig(golinters.NewGosec()). + linter.NewConfig(golinters.NewGosec(gosecCfg)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetBugs). diff --git a/test/testdata/configs/gosec.yml b/test/testdata/configs/gosec.yml new file mode 100644 index 000000000000..41ea1cea5a51 --- /dev/null +++ b/test/testdata/configs/gosec.yml @@ -0,0 +1,13 @@ +linters-settings: + gosec: + includes: + - G306 + - G101 + config: + G306: "0666" + G101: + pattern: "(?i)simple" + ignore_entropy: false + entropy_threshold: "80.0" + per_char_threshold: "3.0" + truncate: "32" diff --git a/test/testdata/gosec_rules_config.go b/test/testdata/gosec_rules_config.go new file mode 100644 index 000000000000..e2b2b4555680 --- /dev/null +++ b/test/testdata/gosec_rules_config.go @@ -0,0 +1,12 @@ +//args: -Egosec +//config_path: testdata/configs/gosec.yml +package testdata + +import "io/ioutil" + +const gosecToken = "62ebc7a03d6ca24dca1258fd4b48462f6fed1545" +const gosecSimple = "62ebc7a03d6ca24dca1258fd4b48462f6fed1545" // ERROR "G101: Potential hardcoded credentials" + +func gosecCustom() { + ioutil.WriteFile("filename", []byte("test"), 0755) // ERROR "G306: Expect WriteFile permissions to be 0666 or less" +}