Skip to content

Commit d8a6d35

Browse files
committed
Ensure the ignores are parsed before analysing the package
In addition this handles the ignores for multi-line issues Signed-off-by: Cosmin Cojocar <gcojocar@adobe.com>
1 parent 7846db0 commit d8a6d35

File tree

2 files changed

+144
-31
lines changed

2 files changed

+144
-31
lines changed

analyzer.go

Lines changed: 91 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,78 @@ const aliasOfAllRules = "*"
5757

5858
var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
5959

60-
// ignoreLocation keeps the location of an ignored rule
61-
type ignoreLocation struct {
62-
file string
63-
line string
60+
type ignore struct {
61+
start int
62+
end int
63+
suppressions map[string][]issue.SuppressionInfo
64+
}
65+
66+
type ignores map[string][]ignore
67+
68+
func newIgnores() ignores {
69+
return make(map[string][]ignore)
70+
}
71+
72+
func (i ignores) parseLine(line string) (int, int) {
73+
parts := strings.Split(line, "-")
74+
start, err := strconv.Atoi(parts[0])
75+
if err != nil {
76+
start = 0
77+
}
78+
end := start
79+
if len(parts) > 1 {
80+
if e, err := strconv.Atoi(parts[1]); err == nil {
81+
end = e
82+
}
83+
}
84+
return start, end
85+
}
86+
87+
func (i ignores) add(file string, line string, suppressions map[string]issue.SuppressionInfo) {
88+
is := []ignore{}
89+
if _, ok := i[file]; ok {
90+
is = i[file]
91+
}
92+
found := false
93+
start, end := i.parseLine(line)
94+
for _, ig := range is {
95+
if ig.start <= start && ig.end >= end {
96+
found = true
97+
for r, s := range suppressions {
98+
ss, ok := ig.suppressions[r]
99+
if !ok {
100+
ss = []issue.SuppressionInfo{}
101+
}
102+
ss = append(ss, s)
103+
ig.suppressions[r] = ss
104+
}
105+
break
106+
}
107+
}
108+
if !found {
109+
ig := ignore{
110+
start: start,
111+
end: end,
112+
suppressions: map[string][]issue.SuppressionInfo{},
113+
}
114+
for r, s := range suppressions {
115+
ig.suppressions[r] = []issue.SuppressionInfo{s}
116+
}
117+
is = append(is, ig)
118+
}
119+
i[file] = is
120+
}
121+
122+
func (i ignores) get(file string, line string) map[string][]issue.SuppressionInfo {
123+
start, end := i.parseLine(line)
124+
if is, ok := i[file]; ok {
125+
for _, i := range is {
126+
if i.start <= start && i.end >= end {
127+
return i.suppressions
128+
}
129+
}
130+
}
131+
return map[string][]issue.SuppressionInfo{}
64132
}
65133

66134
// The Context is populated with data parsed from the source code as it is scanned.
@@ -75,7 +143,7 @@ type Context struct {
75143
Root *ast.File
76144
Imports *ImportTracker
77145
Config Config
78-
Ignores map[ignoreLocation]map[string][]issue.SuppressionInfo
146+
Ignores ignores
79147
PassedValues map[string]interface{}
80148
}
81149

@@ -318,6 +386,8 @@ func (gosec *Analyzer) CheckRules(pkg *packages.Package) {
318386
gosec.context.PkgFiles = pkg.Syntax
319387
gosec.context.Imports = NewImportTracker()
320388
gosec.context.PassedValues = make(map[string]interface{})
389+
gosec.context.Ignores = newIgnores()
390+
gosec.updateIgnores()
321391
ast.Walk(gosec, file)
322392
gosec.stats.NumFiles++
323393
gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount()
@@ -527,9 +597,6 @@ func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo {
527597
// Visit runs the gosec visitor logic over an AST created by parsing go code.
528598
// Rule methods added with AddRule will be invoked as necessary.
529599
func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
530-
// Update any potentially ignored rules at the node location
531-
gosec.updateIgnoredRules(n)
532-
533600
// Using ast.File instead of ast.ImportSpec, so that we can track all imports at once.
534601
switch i := n.(type) {
535602
case *ast.File:
@@ -548,40 +615,33 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
548615
return gosec
549616
}
550617

551-
func (gosec *Analyzer) updateIgnoredRules(n ast.Node) {
618+
func (gosec *Analyzer) updateIgnores() {
619+
for n := range gosec.context.Comments {
620+
gosec.updateIgnoredRulesForNode(n)
621+
}
622+
}
623+
624+
func (gosec *Analyzer) updateIgnoredRulesForNode(n ast.Node) {
552625
ignoredRules := gosec.ignore(n)
553626
if len(ignoredRules) > 0 {
554627
if gosec.context.Ignores == nil {
555-
gosec.context.Ignores = make(map[ignoreLocation]map[string][]issue.SuppressionInfo)
628+
gosec.context.Ignores = newIgnores()
556629
}
557630
line := issue.GetLine(gosec.context.FileSet.File(n.Pos()), n)
558-
ignoreLocation := ignoreLocation{
559-
file: gosec.context.FileSet.File(n.Pos()).Name(),
560-
line: line,
561-
}
562-
current, ok := gosec.context.Ignores[ignoreLocation]
563-
if !ok {
564-
current = map[string][]issue.SuppressionInfo{}
565-
}
566-
for r, s := range ignoredRules {
567-
if current[r] == nil {
568-
current[r] = []issue.SuppressionInfo{}
569-
}
570-
current[r] = append(current[r], s)
571-
}
572-
gosec.context.Ignores[ignoreLocation] = current
631+
gosec.context.Ignores.add(
632+
gosec.context.FileSet.File(n.Pos()).Name(),
633+
line,
634+
ignoredRules,
635+
)
573636
}
574637
}
575638

576639
func (gosec *Analyzer) getSuppressionsAtLineInFile(file string, line string, id string) ([]issue.SuppressionInfo, bool) {
577-
ignores, ok := gosec.context.Ignores[ignoreLocation{file: file, line: line}]
578-
if !ok {
579-
ignores = make(map[string][]issue.SuppressionInfo)
580-
}
640+
ignoredRules := gosec.context.Ignores.get(file, line)
581641

582642
// Check if the rule was specifically suppressed at this location.
583-
generalSuppressions, generalIgnored := ignores[aliasOfAllRules]
584-
ruleSuppressions, ruleIgnored := ignores[id]
643+
generalSuppressions, generalIgnored := ignoredRules[aliasOfAllRules]
644+
ruleSuppressions, ruleIgnored := ignoredRules[id]
585645
ignored := generalIgnored || ruleIgnored
586646
suppressions := append(generalSuppressions, ruleSuppressions...)
587647

analyzer_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,5 +798,58 @@ var _ = Describe("Analyzer", func() {
798798
Expect(issues).Should(HaveLen(sample.Errors))
799799
Expect(issues[0].Suppressions).To(HaveLen(2))
800800
})
801+
802+
It("should not report an error if the violation is suppressed on a struct filed", func() {
803+
sample := testutils.SampleCodeG402[0]
804+
source := sample.Code[0]
805+
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G402")).RulesInfo())
806+
807+
nosecPackage := testutils.NewTestPackage()
808+
defer nosecPackage.Close()
809+
nosecSource := strings.Replace(source,
810+
"TLSClientConfig: &tls.Config{InsecureSkipVerify: true}",
811+
"TLSClientConfig: &tls.Config{InsecureSkipVerify: true} // #nosec G402", 1)
812+
nosecPackage.AddFile("tls.go", nosecSource)
813+
err := nosecPackage.Build()
814+
Expect(err).ShouldNot(HaveOccurred())
815+
err = analyzer.Process(buildTags, nosecPackage.Path)
816+
Expect(err).ShouldNot(HaveOccurred())
817+
issues, _, _ := analyzer.Report()
818+
Expect(issues).To(HaveLen(sample.Errors))
819+
Expect(issues[0].Suppressions).To(HaveLen(1))
820+
Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
821+
})
822+
823+
It("should not report an error if the violation is suppressed on multi-lien issue", func() {
824+
source := `
825+
package main
826+
827+
import (
828+
"fmt"
829+
)
830+
831+
const TokenLabel = `
832+
source += "`" + `
833+
f62e5bcda4fae4f82370da0c6f20697b8f8447ef
834+
` + "`" + "//#nosec G101 -- false positive, this is not a private data" + `
835+
func main() {
836+
fmt.Printf("Label: %s ", TokenLabel)
837+
}
838+
`
839+
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
840+
nosecPackage := testutils.NewTestPackage()
841+
defer nosecPackage.Close()
842+
nosecPackage.AddFile("pwd.go", source)
843+
err := nosecPackage.Build()
844+
Expect(err).ShouldNot(HaveOccurred())
845+
err = analyzer.Process(buildTags, nosecPackage.Path)
846+
Expect(err).ShouldNot(HaveOccurred())
847+
issues, _, _ := analyzer.Report()
848+
Expect(issues).To(HaveLen(1))
849+
Expect(issues[0].Suppressions).To(HaveLen(1))
850+
Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
851+
Expect(issues[0].Suppressions[0].Justification).To(Equal("false positive, this is not a private data"))
852+
})
853+
801854
})
802855
})

0 commit comments

Comments
 (0)