mirror of
https://github.com/securego/gosec.git
synced 2024-12-24 11:35:52 +00:00
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>
This commit is contained in:
parent
7846db034c
commit
d8a6d358dc
2 changed files with 144 additions and 31 deletions
122
analyzer.go
122
analyzer.go
|
@ -57,10 +57,78 @@ const aliasOfAllRules = "*"
|
||||||
|
|
||||||
var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
|
var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
|
||||||
|
|
||||||
// ignoreLocation keeps the location of an ignored rule
|
type ignore struct {
|
||||||
type ignoreLocation struct {
|
start int
|
||||||
file string
|
end int
|
||||||
line string
|
suppressions map[string][]issue.SuppressionInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ignores map[string][]ignore
|
||||||
|
|
||||||
|
func newIgnores() ignores {
|
||||||
|
return make(map[string][]ignore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ignores) parseLine(line string) (int, int) {
|
||||||
|
parts := strings.Split(line, "-")
|
||||||
|
start, err := strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
end := start
|
||||||
|
if len(parts) > 1 {
|
||||||
|
if e, err := strconv.Atoi(parts[1]); err == nil {
|
||||||
|
end = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return start, end
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ignores) add(file string, line string, suppressions map[string]issue.SuppressionInfo) {
|
||||||
|
is := []ignore{}
|
||||||
|
if _, ok := i[file]; ok {
|
||||||
|
is = i[file]
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
start, end := i.parseLine(line)
|
||||||
|
for _, ig := range is {
|
||||||
|
if ig.start <= start && ig.end >= end {
|
||||||
|
found = true
|
||||||
|
for r, s := range suppressions {
|
||||||
|
ss, ok := ig.suppressions[r]
|
||||||
|
if !ok {
|
||||||
|
ss = []issue.SuppressionInfo{}
|
||||||
|
}
|
||||||
|
ss = append(ss, s)
|
||||||
|
ig.suppressions[r] = ss
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
ig := ignore{
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
suppressions: map[string][]issue.SuppressionInfo{},
|
||||||
|
}
|
||||||
|
for r, s := range suppressions {
|
||||||
|
ig.suppressions[r] = []issue.SuppressionInfo{s}
|
||||||
|
}
|
||||||
|
is = append(is, ig)
|
||||||
|
}
|
||||||
|
i[file] = is
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ignores) get(file string, line string) map[string][]issue.SuppressionInfo {
|
||||||
|
start, end := i.parseLine(line)
|
||||||
|
if is, ok := i[file]; ok {
|
||||||
|
for _, i := range is {
|
||||||
|
if i.start <= start && i.end >= end {
|
||||||
|
return i.suppressions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map[string][]issue.SuppressionInfo{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Context is populated with data parsed from the source code as it is scanned.
|
// The Context is populated with data parsed from the source code as it is scanned.
|
||||||
|
@ -75,7 +143,7 @@ type Context struct {
|
||||||
Root *ast.File
|
Root *ast.File
|
||||||
Imports *ImportTracker
|
Imports *ImportTracker
|
||||||
Config Config
|
Config Config
|
||||||
Ignores map[ignoreLocation]map[string][]issue.SuppressionInfo
|
Ignores ignores
|
||||||
PassedValues map[string]interface{}
|
PassedValues map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,6 +386,8 @@ func (gosec *Analyzer) CheckRules(pkg *packages.Package) {
|
||||||
gosec.context.PkgFiles = pkg.Syntax
|
gosec.context.PkgFiles = pkg.Syntax
|
||||||
gosec.context.Imports = NewImportTracker()
|
gosec.context.Imports = NewImportTracker()
|
||||||
gosec.context.PassedValues = make(map[string]interface{})
|
gosec.context.PassedValues = make(map[string]interface{})
|
||||||
|
gosec.context.Ignores = newIgnores()
|
||||||
|
gosec.updateIgnores()
|
||||||
ast.Walk(gosec, file)
|
ast.Walk(gosec, file)
|
||||||
gosec.stats.NumFiles++
|
gosec.stats.NumFiles++
|
||||||
gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount()
|
gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount()
|
||||||
|
@ -527,9 +597,6 @@ func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo {
|
||||||
// Visit runs the gosec visitor logic over an AST created by parsing go code.
|
// Visit runs the gosec visitor logic over an AST created by parsing go code.
|
||||||
// Rule methods added with AddRule will be invoked as necessary.
|
// Rule methods added with AddRule will be invoked as necessary.
|
||||||
func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||||
// Update any potentially ignored rules at the node location
|
|
||||||
gosec.updateIgnoredRules(n)
|
|
||||||
|
|
||||||
// Using ast.File instead of ast.ImportSpec, so that we can track all imports at once.
|
// Using ast.File instead of ast.ImportSpec, so that we can track all imports at once.
|
||||||
switch i := n.(type) {
|
switch i := n.(type) {
|
||||||
case *ast.File:
|
case *ast.File:
|
||||||
|
@ -548,40 +615,33 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||||
return gosec
|
return gosec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gosec *Analyzer) updateIgnoredRules(n ast.Node) {
|
func (gosec *Analyzer) updateIgnores() {
|
||||||
|
for n := range gosec.context.Comments {
|
||||||
|
gosec.updateIgnoredRulesForNode(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gosec *Analyzer) updateIgnoredRulesForNode(n ast.Node) {
|
||||||
ignoredRules := gosec.ignore(n)
|
ignoredRules := gosec.ignore(n)
|
||||||
if len(ignoredRules) > 0 {
|
if len(ignoredRules) > 0 {
|
||||||
if gosec.context.Ignores == nil {
|
if gosec.context.Ignores == nil {
|
||||||
gosec.context.Ignores = make(map[ignoreLocation]map[string][]issue.SuppressionInfo)
|
gosec.context.Ignores = newIgnores()
|
||||||
}
|
}
|
||||||
line := issue.GetLine(gosec.context.FileSet.File(n.Pos()), n)
|
line := issue.GetLine(gosec.context.FileSet.File(n.Pos()), n)
|
||||||
ignoreLocation := ignoreLocation{
|
gosec.context.Ignores.add(
|
||||||
file: gosec.context.FileSet.File(n.Pos()).Name(),
|
gosec.context.FileSet.File(n.Pos()).Name(),
|
||||||
line: line,
|
line,
|
||||||
}
|
ignoredRules,
|
||||||
current, ok := gosec.context.Ignores[ignoreLocation]
|
)
|
||||||
if !ok {
|
|
||||||
current = map[string][]issue.SuppressionInfo{}
|
|
||||||
}
|
|
||||||
for r, s := range ignoredRules {
|
|
||||||
if current[r] == nil {
|
|
||||||
current[r] = []issue.SuppressionInfo{}
|
|
||||||
}
|
|
||||||
current[r] = append(current[r], s)
|
|
||||||
}
|
|
||||||
gosec.context.Ignores[ignoreLocation] = current
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gosec *Analyzer) getSuppressionsAtLineInFile(file string, line string, id string) ([]issue.SuppressionInfo, bool) {
|
func (gosec *Analyzer) getSuppressionsAtLineInFile(file string, line string, id string) ([]issue.SuppressionInfo, bool) {
|
||||||
ignores, ok := gosec.context.Ignores[ignoreLocation{file: file, line: line}]
|
ignoredRules := gosec.context.Ignores.get(file, line)
|
||||||
if !ok {
|
|
||||||
ignores = make(map[string][]issue.SuppressionInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the rule was specifically suppressed at this location.
|
// Check if the rule was specifically suppressed at this location.
|
||||||
generalSuppressions, generalIgnored := ignores[aliasOfAllRules]
|
generalSuppressions, generalIgnored := ignoredRules[aliasOfAllRules]
|
||||||
ruleSuppressions, ruleIgnored := ignores[id]
|
ruleSuppressions, ruleIgnored := ignoredRules[id]
|
||||||
ignored := generalIgnored || ruleIgnored
|
ignored := generalIgnored || ruleIgnored
|
||||||
suppressions := append(generalSuppressions, ruleSuppressions...)
|
suppressions := append(generalSuppressions, ruleSuppressions...)
|
||||||
|
|
||||||
|
|
|
@ -798,5 +798,58 @@ var _ = Describe("Analyzer", func() {
|
||||||
Expect(issues).Should(HaveLen(sample.Errors))
|
Expect(issues).Should(HaveLen(sample.Errors))
|
||||||
Expect(issues[0].Suppressions).To(HaveLen(2))
|
Expect(issues[0].Suppressions).To(HaveLen(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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} // #nosec 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TokenLabel = `
|
||||||
|
source += "`" + `
|
||||||
|
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"))
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue