Skip to content

Commit 57ec633

Browse files
authored
Add support for //gosec:disable directive (#1314)
1 parent e5fee17 commit 57ec633

File tree

5 files changed

+750
-40
lines changed

5 files changed

+750
-40
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,13 @@ You could put the description or justification text for the annotation. The
304304
justification should be after the rule(s) to suppress and start with two or
305305
more dashes, e.g: `//#nosec G101 G102 -- This is a false positive`
306306

307-
In some cases you may also want to revisit places where `#nosec` annotations
307+
Alternatively, gosec also supports the `//gosec:disable` directive, which functions similar to `#nosec`:
308+
309+
```go
310+
//gosec:disable G101 -- This is a false positive
311+
```
312+
313+
In some cases you may also want to revisit places where `#nosec` or `//gosec:disable` annotations
308314
have been used. To run the scanner and ignore any `#nosec` annotations you
309315
can do the following:
310316

analyzer.go

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ const externalSuppressionJustification = "Globally suppressed."
5757

5858
const aliasOfAllRules = "*"
5959

60+
var directiveRegexp = regexp.MustCompile("^//gosec:disable(?: (.+))?$")
61+
6062
type ignore struct {
6163
start int
6264
end int
@@ -582,53 +584,77 @@ func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo {
582584
}
583585

584586
for _, group := range groups {
585-
comment := strings.TrimSpace(group.Text())
586-
foundDefaultTag := strings.HasPrefix(comment, noSecDefaultTag) || regexp.MustCompile("\n *"+noSecDefaultTag).MatchString(comment)
587-
foundAlternativeTag := strings.HasPrefix(comment, noSecAlternativeTag) || regexp.MustCompile("\n *"+noSecAlternativeTag).MatchString(comment)
588-
589-
if foundDefaultTag || foundAlternativeTag {
590-
gosec.stats.NumNosec++
591-
592-
// Discard what's in front of the nosec tag.
593-
if foundDefaultTag {
594-
comment = strings.SplitN(comment, noSecDefaultTag, 2)[1]
595-
} else {
596-
comment = strings.SplitN(comment, noSecAlternativeTag, 2)[1]
597-
}
587+
found, args := findNoSecDirective(group, noSecDefaultTag, noSecAlternativeTag)
588+
if !found {
589+
continue
590+
}
598591

599-
// Extract the directive and the justification.
600-
justification := ""
601-
commentParts := regexp.MustCompile(`-{2,}`).Split(comment, 2)
602-
directive := commentParts[0]
603-
if len(commentParts) > 1 {
604-
justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n"))
605-
}
592+
gosec.stats.NumNosec++
606593

607-
// Pull out the specific rules that are listed to be ignored.
608-
re := regexp.MustCompile(`(G\d{3})`)
609-
matches := re.FindAllStringSubmatch(directive, -1)
594+
// Extract the directive and the justification.
595+
justification := ""
596+
commentParts := regexp.MustCompile(`-{2,}`).Split(args, 2)
597+
directive := commentParts[0]
598+
if len(commentParts) > 1 {
599+
justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n"))
600+
}
610601

611-
suppression := issue.SuppressionInfo{
612-
Kind: "inSource",
613-
Justification: justification,
614-
}
602+
// Pull out the specific rules that are listed to be ignored.
603+
re := regexp.MustCompile(`(G\d{3})`)
604+
matches := re.FindAllStringSubmatch(directive, -1)
615605

616-
// Find the rule IDs to ignore.
617-
ignores := make(map[string]issue.SuppressionInfo)
618-
for _, v := range matches {
619-
ignores[v[1]] = suppression
620-
}
606+
suppression := issue.SuppressionInfo{
607+
Kind: "inSource",
608+
Justification: justification,
609+
}
621610

622-
// If no specific rules were given, ignore everything.
623-
if len(matches) == 0 {
624-
ignores[aliasOfAllRules] = suppression
625-
}
626-
return ignores
611+
// Find the rule IDs to ignore.
612+
ignores := make(map[string]issue.SuppressionInfo)
613+
for _, v := range matches {
614+
ignores[v[1]] = suppression
627615
}
616+
617+
// If no specific rules were given, ignore everything.
618+
if len(matches) == 0 {
619+
ignores[aliasOfAllRules] = suppression
620+
}
621+
return ignores
628622
}
629623
return nil
630624
}
631625

626+
// findNoSecDirective checks if the comment group contains `#nosec` or `//gosec:disable` directive.
627+
// If found, it returns true and the directive's arguments.
628+
func findNoSecDirective(group *ast.CommentGroup, noSecDefaultTag, noSecAlternativeTag string) (bool, string) {
629+
// Check if the comment grounp has a nosec comment.
630+
for _, tag := range []string{noSecDefaultTag, noSecAlternativeTag} {
631+
if found, args := findNoSecTag(group, tag); found {
632+
return true, args
633+
}
634+
}
635+
636+
// Check if the comment group has a directive comment.
637+
for _, c := range group.List {
638+
match := directiveRegexp.FindStringSubmatch(c.Text)
639+
if len(match) > 0 {
640+
return true, match[0]
641+
}
642+
}
643+
644+
return false, ""
645+
}
646+
647+
func findNoSecTag(group *ast.CommentGroup, tag string) (bool, string) {
648+
comment := strings.TrimSpace(group.Text())
649+
650+
if strings.HasPrefix(comment, tag) || regexp.MustCompile("\n *"+tag).MatchString(comment) {
651+
// Discard what's in front of the nosec tag.
652+
return true, strings.SplitN(comment, tag, 2)[1]
653+
}
654+
655+
return false, ""
656+
}
657+
632658
// Visit runs the gosec visitor logic over an AST created by parsing go code.
633659
// Rule methods added with AddRule will be invoked as necessary.
634660
func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {

0 commit comments

Comments
 (0)