diff --git a/pkg/golinters/gosec.go b/pkg/golinters/gosec.go index 5441c40fae5d..a0ff9a90b408 100644 --- a/pkg/golinters/gosec.go +++ b/pkg/golinters/gosec.go @@ -26,20 +26,11 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter { var mu sync.Mutex var resIssues []goanalysis.Issue - conf := gosec.NewConfig() - var filters []rules.RuleFilter + conf := gosec.NewConfig() if settings != nil { filters = gosecRuleFilters(settings.Includes, settings.Excludes) - - for k, v := range settings.Config { - if k != gosec.Globals { - // 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. - k = strings.ToUpper(k) - } - conf.Set(k, v) - } + conf = toGosecConfig(settings) } logger := log.New(io.Discard, "", 0) @@ -140,6 +131,35 @@ func runGoSec(lintCtx *linter.Context, pass *analysis.Pass, settings *config.GoS return issues } +func toGosecConfig(settings *config.GoSecSettings) gosec.Config { + conf := gosec.NewConfig() + + for k, v := range settings.Config { + if k == gosec.Globals { + convertGosecGlobals(v, conf) + continue + } + + // 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. + conf.Set(strings.ToUpper(k), v) + } + + return conf +} + +// based on https://github.com/securego/gosec/blob/47bfd4eb6fc7395940933388550b547538b4c946/config.go#L52-L62 +func convertGosecGlobals(globalOptionFromConfig any, conf gosec.Config) { + globalOptionMap, ok := globalOptionFromConfig.(map[string]any) + if !ok { + return + } + + for k, v := range globalOptionMap { + conf.SetGlobal(gosec.GlobalOption(k), fmt.Sprintf("%v", v)) + } +} + // 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 @@ -173,10 +193,12 @@ func convertToScore(str string) (gosec.Score, error) { // code borrowed from https://github.com/securego/gosec/blob/69213955dacfd560562e780f723486ef1ca6d486/cmd/gosec/main.go#L264-L276 func filterIssues(issues []*gosec.Issue, severity, confidence gosec.Score) []*gosec.Issue { res := make([]*gosec.Issue, 0) + for _, issue := range issues { if issue.Severity >= severity && issue.Confidence >= confidence { res = append(res, issue) } } + return res } diff --git a/pkg/golinters/gosec_test.go b/pkg/golinters/gosec_test.go new file mode 100644 index 000000000000..9b1b5faeff96 --- /dev/null +++ b/pkg/golinters/gosec_test.go @@ -0,0 +1,70 @@ +package golinters + +import ( + "testing" + + "github.com/securego/gosec/v2" + "github.com/stretchr/testify/assert" + + "github.com/golangci/golangci-lint/pkg/config" +) + +func Test_toGosecConfig(t *testing.T) { + testCases := []struct { + desc string + settings *config.GoSecSettings + expected gosec.Config + }{ + { + desc: "empty config map", + settings: &config.GoSecSettings{}, + expected: gosec.Config{ + "global": map[gosec.GlobalOption]string{}, + }, + }, + { + desc: "with global settings", + settings: &config.GoSecSettings{ + Config: map[string]any{ + gosec.Globals: map[string]any{ + string(gosec.Nosec): true, + string(gosec.Audit): "true", + }, + }, + }, + expected: gosec.Config{ + "global": map[gosec.GlobalOption]string{ + "audit": "true", + "nosec": "true", + }, + }, + }, + { + desc: "rule specified setting", + settings: &config.GoSecSettings{ + Config: map[string]any{ + "g101": map[string]any{ + "pattern": "(?i)example", + }, + "G301": "0750", + }, + }, + expected: gosec.Config{ + "G101": map[string]any{"pattern": "(?i)example"}, + "G301": "0750", + "global": map[gosec.GlobalOption]string{}, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + conf := toGosecConfig(test.settings) + + assert.Equal(t, test.expected, conf) + }) + } +} diff --git a/test/testdata/configs/gosec_global_option.yml b/test/testdata/configs/gosec_global_option.yml new file mode 100644 index 000000000000..fc0c3e27ac6f --- /dev/null +++ b/test/testdata/configs/gosec_global_option.yml @@ -0,0 +1,5 @@ +linters-settings: + gosec: + config: + global: + nosec: true diff --git a/test/testdata/gosec_global_option.go b/test/testdata/gosec_global_option.go new file mode 100644 index 000000000000..0a9407ea75de --- /dev/null +++ b/test/testdata/gosec_global_option.go @@ -0,0 +1,14 @@ +//golangcitest:args -Egosec +//golangcitest:config_path testdata/configs/gosec_global_option.yml +package testdata + +import ( + "crypto/md5" // want "G501: Blocklisted import crypto/md5: weak cryptographic primitive" + "log" +) + +func Gosec() { + // #nosec G401 + h := md5.New() // want "G401: Use of weak cryptographic primitive" + log.Print(h) +}