From 57ec63392cac020f01f555654da58918b71fdd2d Mon Sep 17 00:00:00 2001 From: frozenbonito Date: Mon, 10 Mar 2025 18:09:27 +0900 Subject: [PATCH] Add support for `//gosec:disable` directive (#1314) --- README.md | 8 +- analyzer.go | 110 ++++--- analyzer_test.go | 602 ++++++++++++++++++++++++++++++++++++++ testutils/g101_samples.go | 44 +++ testutils/g112_samples.go | 32 ++ 5 files changed, 753 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5e68182..019a53e 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,13 @@ You could put the description or justification text for the annotation. The justification should be after the rule(s) to suppress and start with two or more dashes, e.g: `//#nosec G101 G102 -- This is a false positive` -In some cases you may also want to revisit places where `#nosec` annotations +Alternatively, gosec also supports the `//gosec:disable` directive, which functions similar to `#nosec`: + +```go +//gosec:disable G101 -- This is a false positive +``` + +In some cases you may also want to revisit places where `#nosec` or `//gosec:disable` annotations have been used. To run the scanner and ignore any `#nosec` annotations you can do the following: diff --git a/analyzer.go b/analyzer.go index 186cc3c..34ac82b 100644 --- a/analyzer.go +++ b/analyzer.go @@ -57,6 +57,8 @@ const externalSuppressionJustification = "Globally suppressed." const aliasOfAllRules = "*" +var directiveRegexp = regexp.MustCompile("^//gosec:disable(?: (.+))?$") + type ignore struct { start int end int @@ -582,53 +584,77 @@ func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo { } for _, group := range groups { - comment := strings.TrimSpace(group.Text()) - foundDefaultTag := strings.HasPrefix(comment, noSecDefaultTag) || regexp.MustCompile("\n *"+noSecDefaultTag).MatchString(comment) - foundAlternativeTag := strings.HasPrefix(comment, noSecAlternativeTag) || regexp.MustCompile("\n *"+noSecAlternativeTag).MatchString(comment) - - if foundDefaultTag || foundAlternativeTag { - gosec.stats.NumNosec++ - - // Discard what's in front of the nosec tag. - if foundDefaultTag { - comment = strings.SplitN(comment, noSecDefaultTag, 2)[1] - } else { - comment = strings.SplitN(comment, noSecAlternativeTag, 2)[1] - } - - // Extract the directive and the justification. - justification := "" - commentParts := regexp.MustCompile(`-{2,}`).Split(comment, 2) - directive := commentParts[0] - if len(commentParts) > 1 { - justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n")) - } - - // Pull out the specific rules that are listed to be ignored. - re := regexp.MustCompile(`(G\d{3})`) - matches := re.FindAllStringSubmatch(directive, -1) - - suppression := issue.SuppressionInfo{ - Kind: "inSource", - Justification: justification, - } - - // Find the rule IDs to ignore. - ignores := make(map[string]issue.SuppressionInfo) - for _, v := range matches { - ignores[v[1]] = suppression - } - - // If no specific rules were given, ignore everything. - if len(matches) == 0 { - ignores[aliasOfAllRules] = suppression - } - return ignores + found, args := findNoSecDirective(group, noSecDefaultTag, noSecAlternativeTag) + if !found { + continue } + + gosec.stats.NumNosec++ + + // Extract the directive and the justification. + justification := "" + commentParts := regexp.MustCompile(`-{2,}`).Split(args, 2) + directive := commentParts[0] + if len(commentParts) > 1 { + justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n")) + } + + // Pull out the specific rules that are listed to be ignored. + re := regexp.MustCompile(`(G\d{3})`) + matches := re.FindAllStringSubmatch(directive, -1) + + suppression := issue.SuppressionInfo{ + Kind: "inSource", + Justification: justification, + } + + // Find the rule IDs to ignore. + ignores := make(map[string]issue.SuppressionInfo) + for _, v := range matches { + ignores[v[1]] = suppression + } + + // If no specific rules were given, ignore everything. + if len(matches) == 0 { + ignores[aliasOfAllRules] = suppression + } + return ignores } return nil } +// findNoSecDirective checks if the comment group contains `#nosec` or `//gosec:disable` directive. +// If found, it returns true and the directive's arguments. +func findNoSecDirective(group *ast.CommentGroup, noSecDefaultTag, noSecAlternativeTag string) (bool, string) { + // Check if the comment grounp has a nosec comment. + for _, tag := range []string{noSecDefaultTag, noSecAlternativeTag} { + if found, args := findNoSecTag(group, tag); found { + return true, args + } + } + + // Check if the comment group has a directive comment. + for _, c := range group.List { + match := directiveRegexp.FindStringSubmatch(c.Text) + if len(match) > 0 { + return true, match[0] + } + } + + return false, "" +} + +func findNoSecTag(group *ast.CommentGroup, tag string) (bool, string) { + comment := strings.TrimSpace(group.Text()) + + if strings.HasPrefix(comment, tag) || regexp.MustCompile("\n *"+tag).MatchString(comment) { + // Discard what's in front of the nosec tag. + return true, strings.SplitN(comment, tag, 2)[1] + } + + return false, "" +} + // Visit runs the gosec visitor logic over an AST created by parsing go code. // Rule methods added with AddRule will be invoked as necessary. func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor { diff --git a/analyzer_test.go b/analyzer_test.go index 00c7cc4..3bec511 100644 --- a/analyzer_test.go +++ b/analyzer_test.go @@ -230,6 +230,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when a disable directive is present", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when a nosec line comment is present", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -247,6 +264,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when a disable directive is present", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when a nosec line comment is present", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -264,6 +298,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when a disable directive is present", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when a nosec block comment is present", func() { sample := testutils.SampleCodeG401[0] source := sample.Code[0] @@ -333,6 +384,24 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when an exclude comment is present for the correct rule", func() { + // Rule for MD5 weak crypto usage + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G401", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when an exclude comment is present for the correct rule", func() { // Rule for DES weak crypto usage sample := testutils.SampleCodeG405[0] @@ -351,6 +420,24 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when an exclude comment is present for the correct rule", func() { + // Rule for DES weak crypto usage + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G405", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when an exclude comment is present for the correct rule", func() { // Rule for MD4 deprecated weak crypto usage sample := testutils.SampleCodeG406[0] @@ -369,6 +456,24 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when an exclude comment is present for the correct rule", func() { + // Rule for MD4 deprecated weak crypto usage + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G406", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when a nosec block and line comment are present", func() { sample := testutils.SampleCodeG101[23] source := sample.Code[0] @@ -415,6 +520,52 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when a disable directive block and line comment are present", func() { + sample := testutils.SampleCodeG101[26] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecPackage.AddFile("g101.go", source) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when only a disable directive block is present", func() { + sample := testutils.SampleCodeG101[27] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecPackage.AddFile("g101.go", source) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when a single line nosec is present on a multi-line issue", func() { + sample := testutils.SampleCodeG112[4] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G112")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecPackage.AddFile("g112.go", source) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should report errors when an exclude comment is present for a different rule", func() { sample := testutils.SampleCodeG401[0] source := sample.Code[0] @@ -432,6 +583,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should report errors when an exclude comment is present for a different rule", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G301", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should report errors when an exclude comment is present for a different rule", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -449,6 +617,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should report errors when an exclude comment is present for a different rule", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G301", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should report errors when an exclude comment is present for a different rule", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -466,6 +651,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should report errors when an exclude comment is present for a different rule", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G301", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() { sample := testutils.SampleCodeG401[0] source := sample.Code[0] @@ -485,6 +687,25 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G301 G401", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -504,6 +725,25 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G301 G405", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -523,6 +763,25 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G301 G406", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should pass the build tags", func() { sample := testutils.SampleCodeBuildTag[0] source := sample.Code[0] @@ -573,6 +832,29 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should be possible to overwrite disable directive, and report issues", func() { + // Rule for MD5 weak crypto usage + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + + // overwrite nosec option + nosecIgnoreConfig := gosec.NewConfig() + nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true") + customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger) + customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = customAnalyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := customAnalyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should be possible to overwrite nosec comments, and report issues", func() { // Rule for DES weak crypto usage sample := testutils.SampleCodeG405[0] @@ -596,6 +878,29 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should be possible to overwrite disable directive comments, and report issues", func() { + // Rule for DES weak crypto usage + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + + // overwrite nosec option + nosecIgnoreConfig := gosec.NewConfig() + nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true") + customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger) + customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = customAnalyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := customAnalyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should be possible to overwrite nosec comments, and report issues", func() { // Rule for MD4 weak crypto usage sample := testutils.SampleCodeG406[0] @@ -619,6 +924,29 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should be possible to overwrite disable directive comments, and report issues", func() { + // Rule for MD4 weak crypto usage + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + + // overwrite nosec option + nosecIgnoreConfig := gosec.NewConfig() + nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true") + customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger) + customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = customAnalyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := customAnalyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should be possible to overwrite nosec comments, and report issues but they should not be counted", func() { // Rule for MD5 weak crypto usage sample := testutils.SampleCodeG401[0] @@ -714,6 +1042,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when disable directive is in front of a line", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "//Some description\n//gosec:disable G401\nh := md5.New()", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when nosec tag is in front of a line", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -731,6 +1076,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when disable directive is in front of a line", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//Some description\n//gosec:disable G405\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should not report errors when nosec tag is in front of a line", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -748,6 +1110,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(BeEmpty()) }) + It("should not report errors when disable directive is in front of a line", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "//Some description\n//gosec:disable G406\nh := md4.New()", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(BeEmpty()) + }) + It("should report errors when nosec tag is not in front of a line", func() { sample := testutils.SampleCodeG401[0] source := sample.Code[0] @@ -867,6 +1246,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should report errors when there are disable directives after a //gosec:disable WrongRuleList", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "//gosec:disable G301\n//gosec:disable\nh := md5.New()", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should report errors when there are nosec tags after a #nosec WrongRuleList annotation", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -884,6 +1280,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should report errors when there are disable directives after a //gosec:disable WrongRuleList", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//gosec:disable G301\n//gosec:disable\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should report errors when there are nosec tags after a #nosec WrongRuleList annotation", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -901,6 +1314,23 @@ var _ = Describe("Analyzer", func() { Expect(nosecIssues).Should(HaveLen(sample.Errors)) }) + It("should report errors when there are disable directives after a //gosec:disable WrongRuleList", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "//gosec:disable G301\n//gosec:disable\nh := md4.New()", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + nosecIssues, _, _ := analyzer.Report() + Expect(nosecIssues).Should(HaveLen(sample.Errors)) + }) + It("should be possible to use an alternative nosec tag", func() { // Rule for MD5 weak crypto usage sample := testutils.SampleCodeG401[0] @@ -1365,6 +1795,26 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification")) }) + It("should not report an error if the violation is suppressed", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G401 -- Justification", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification")) + }) + It("should not report an error if the violation is suppressed", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -1385,6 +1835,26 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification")) }) + It("should not report an error if the violation is suppressed", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G405 -- Justification", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification")) + }) + It("should not report an error if the violation is suppressed", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -1405,6 +1875,26 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification")) }) + It("should not report an error if the violation is suppressed", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G406 -- Justification", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification")) + }) + It("should not report an error if the violation is suppressed without certain rules", func() { sample := testutils.SampleCodeG401[0] source := sample.Code[0] @@ -1425,6 +1915,26 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Justification).To(Equal("")) }) + It("should not report an error if the violation is suppressed without certain rules", func() { + sample := testutils.SampleCodeG401[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable", 1) + nosecPackage.AddFile("md5.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("")) + }) + It("should not report an error if the violation is suppressed without certain rules", func() { sample := testutils.SampleCodeG405[0] source := sample.Code[0] @@ -1445,6 +1955,26 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Justification).To(Equal("")) }) + It("should not report an error if the violation is suppressed without certain rules", func() { + sample := testutils.SampleCodeG405[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable", 1) + nosecPackage.AddFile("cipher.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("")) + }) + It("should not report an error if the violation is suppressed without certain rules", func() { sample := testutils.SampleCodeG406[0] source := sample.Code[0] @@ -1465,6 +1995,26 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Justification).To(Equal("")) }) + It("should not report an error if the violation is suppressed without certain rules", func() { + sample := testutils.SampleCodeG406[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable", 1) + nosecPackage.AddFile("md4.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("")) + }) + It("should not report an error if the rule is not included", func() { sample := testutils.SampleCodeG101[0] source := sample.Code[0] @@ -1618,6 +2168,27 @@ var _ = Describe("Analyzer", func() { Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) }) + It("should not report an error if the violation is suppressed on a struct filed", func() { + sample := testutils.SampleCodeG402[0] + source := sample.Code[0] + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G402")).RulesInfo()) + + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecSource := strings.Replace(source, + "TLSClientConfig: &tls.Config{InsecureSkipVerify: true}", + "TLSClientConfig: &tls.Config{InsecureSkipVerify: true} //gosec:disable G402", 1) + nosecPackage.AddFile("tls.go", nosecSource) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(sample.Errors)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + }) + It("should not report an error if the violation is suppressed on multi-lien issue", func() { source := ` package main @@ -1632,6 +2203,37 @@ f62e5bcda4fae4f82370da0c6f20697b8f8447ef ` + "`" + "//#nosec G101 -- false positive, this is not a private data" + ` func main() { fmt.Printf("Label: %s ", TokenLabel) +} + ` + analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo()) + nosecPackage := testutils.NewTestPackage() + defer nosecPackage.Close() + nosecPackage.AddFile("pwd.go", source) + err := nosecPackage.Build() + Expect(err).ShouldNot(HaveOccurred()) + err = analyzer.Process(buildTags, nosecPackage.Path) + Expect(err).ShouldNot(HaveOccurred()) + issues, _, _ := analyzer.Report() + Expect(issues).To(HaveLen(1)) + Expect(issues[0].Suppressions).To(HaveLen(1)) + Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource")) + Expect(issues[0].Suppressions[0].Justification).To(Equal("false positive, this is not a private data")) + }) + + It("should not report an error if the violation is suppressed on multi-lien issue", func() { + source := ` +package main + +import ( + "fmt" +) + +const TokenLabel = ` + source += "`" + ` +f62e5bcda4fae4f82370da0c6f20697b8f8447ef + ` + "`" + "//gosec:disable G101 -- false positive, this is not a private data" + ` +func main() { + fmt.Printf("Label: %s ", TokenLabel) } ` analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo()) diff --git a/testutils/g101_samples.go b/testutils/g101_samples.go index a1e34fb..1dbbea8 100644 --- a/testutils/g101_samples.go +++ b/testutils/g101_samples.go @@ -321,6 +321,50 @@ func main() { fmt.Printf("%s\n", ConfigLearnerTokenAuth) } +`}, 0, gosec.NewConfig()}, + {[]string{` +package main + +import "fmt" + +//gosec:disable G101 +const ( + ConfigLearnerTokenAuth string = "learner_auth_token_config" //gosec:disable G101 +) + +func main() { + fmt.Printf("%s\n", ConfigLearnerTokenAuth) +} + +`}, 0, gosec.NewConfig()}, + {[]string{` +package main + +import "fmt" + +//gosec:disable G101 +const ( + ConfigLearnerTokenAuth string = "learner_auth_token_config" +) + +func main() { + fmt.Printf("%s\n", ConfigLearnerTokenAuth) +} + +`}, 0, gosec.NewConfig()}, + {[]string{` +package main + +import "fmt" + +const ( + ConfigLearnerTokenAuth string = "learner_auth_token_config" //gosec:disable G101 +) + +func main() { + fmt.Printf("%s\n", ConfigLearnerTokenAuth) +} + `}, 0, gosec.NewConfig()}, } diff --git a/testutils/g112_samples.go b/testutils/g112_samples.go index 4a58f6c..02e6bc6 100644 --- a/testutils/g112_samples.go +++ b/testutils/g112_samples.go @@ -98,6 +98,38 @@ func New(listenAddr string) *Server { } } +func main() { + fmt.Print("test") +} +`}, 0, gosec.NewConfig()}, + {[]string{` +package main + +import ( + "fmt" + "net/http" + "sync" +) + +type Server struct { + hs *http.Server + mux *http.ServeMux + mu sync.Mutex +} + +func New(listenAddr string) *Server { + mux := http.NewServeMux() + + return &Server{ + hs: &http.Server{ //gosec:disable G112 - Not publicly exposed + Addr: listenAddr, + Handler: mux, + }, + mux: mux, + mu: sync.Mutex{}, + } +} + func main() { fmt.Print("test") }