Add possibility to list waived (nosec) marked issues but not count them as such

This commit is contained in:
Marc Brugger 2021-08-18 13:00:38 +02:00 committed by GitHub
parent 5a131be2ec
commit ba23b5e49a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 76 additions and 18 deletions

View file

@ -82,6 +82,7 @@ type Analyzer struct {
errors map[string][]Error // keys are file paths; values are the golang errors in those files
tests bool
excludeGenerated bool
showIgnored bool
}
// NewAnalyzer builds a new analyzer.
@ -90,11 +91,16 @@ func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, logger *log.Log
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
ignoreNoSec = enabled
}
showIgnored := false
if enabled, err := conf.IsGlobalEnabled(ShowIgnored); err == nil {
showIgnored = enabled
}
if logger == nil {
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
}
return &Analyzer{
ignoreNosec: ignoreNoSec,
showIgnored: showIgnored,
ruleset: make(RuleSet),
context: &Context{},
config: conf,
@ -179,7 +185,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
}
if gosec.tests {
testsFiles := []string{}
testsFiles := make([]string, 0)
testsFiles = append(testsFiles, basePackage.TestGoFiles...)
testsFiles = append(testsFiles, basePackage.XTestGoFiles...)
for _, filename := range testsFiles {
@ -279,7 +285,7 @@ func (gosec *Analyzer) AppendError(file string, err error) {
if r.MatchString(err.Error()) {
return
}
errors := []Error{}
errors := make([]Error, 0)
if ferrs, ok := gosec.errors[file]; ok {
errors = ferrs
}
@ -364,9 +370,8 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
gosec.context.Imports.TrackImport(n)
for _, rule := range gosec.ruleset.RegisteredFor(n) {
if _, ok := ignores[rule.ID()]; ok {
continue
}
_, ignored := ignores[rule.ID()]
issue, err := rule.Match(n, gosec.context)
if err != nil {
file, line := GetLocation(n, gosec.context)
@ -374,9 +379,16 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
}
if issue != nil {
gosec.issues = append(gosec.issues, issue)
if gosec.showIgnored {
issue.NoSec = ignored
}
if !ignored || !gosec.showIgnored {
gosec.stats.NumFound++
}
if !ignored || gosec.showIgnored || gosec.ignoreNosec {
gosec.issues = append(gosec.issues, issue)
}
}
}
return gosec
}

View file

@ -261,6 +261,32 @@ var _ = Describe("Analyzer", func() {
Expect(nosecIssues).Should(HaveLen(sample.Errors))
})
XIt("should be possible to overwrite nosec comments, and report issues but the should not be counted", 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")
nosecIgnoreConfig.SetGlobal(gosec.ShowIgnored, "true")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage()
defer nosecPackage.Close()
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
nosecPackage.AddFile("md5.go", nosecSource)
err := nosecPackage.Build()
Expect(err).ShouldNot(HaveOccurred())
err = customAnalyzer.Process(buildTags, nosecPackage.Path)
Expect(err).ShouldNot(HaveOccurred())
nosecIssues, metrics, _ := customAnalyzer.Report()
Expect(nosecIssues).Should(HaveLen(sample.Errors))
Expect(metrics.NumFound).Should(Equal(0))
Expect(metrics.NumNosec).Should(Equal(1))
})
It("should be possible to use an alternative nosec tag", func() {
// Rule for MD5 weak crypto usage
sample := testutils.SampleCodeG401[0]

View file

@ -72,6 +72,9 @@ var (
// #nosec flag
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
// show ignored
flagShowIgnored = flag.Bool("show-ignored", false, "If enabled, ignored issues are printed")
// format output
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
@ -173,6 +176,9 @@ func loadConfig(configFile string) (gosec.Config, error) {
if *flagIgnoreNoSec {
config.SetGlobal(gosec.Nosec, "true")
}
if *flagShowIgnored {
config.SetGlobal(gosec.ShowIgnored, "true")
}
if *flagAlternativeNoSec != "" {
config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
}
@ -200,7 +206,7 @@ func loadRules(include, exclude string) rules.RuleList {
}
func getRootPaths(paths []string) []string {
rootPaths := []string{}
rootPaths := make([]string, 0)
for _, path := range paths {
rootPath, err := gosec.RootPath(path)
if err != nil {
@ -255,14 +261,18 @@ func convertToScore(severity string) (gosec.Score, error) {
}
}
func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.Score) []*gosec.Issue {
result := []*gosec.Issue{}
func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.Score) ([]*gosec.Issue, int) {
result := make([]*gosec.Issue, 0)
trueIssues := 0
for _, issue := range issues {
if issue.Severity >= severity && issue.Confidence >= confidence {
result = append(result, issue)
if !issue.NoSec || !*flagShowIgnored {
trueIssues++
}
}
return result
}
return result, trueIssues
}
func main() {
@ -372,9 +382,10 @@ func main() {
}
// Filter the issues by severity and confidence
issues = filterIssues(issues, failSeverity, failConfidence)
if metrics.NumFound != len(issues) {
metrics.NumFound = len(issues)
var trueIssues int
issues, trueIssues = filterIssues(issues, failSeverity, failConfidence)
if metrics.NumFound != trueIssues {
metrics.NumFound = trueIssues
}
// Exit quietly if nothing was found
@ -390,7 +401,7 @@ func main() {
if *flagOutput == "" || *flagStdOut {
fileFormat := getPrintedFormat(*flagFormat, *flagVerbose)
if err := printReport(fileFormat, *flagColor, rootPaths, reportInfo); err != nil {
logger.Fatal((err))
logger.Fatal(err)
}
}
if *flagOutput != "" {

View file

@ -20,6 +20,8 @@ type GlobalOption string
const (
// Nosec global option for #nosec directive
Nosec GlobalOption = "nosec"
// ShowIgnored defines whether nosec issues are counted as finding or not
ShowIgnored GlobalOption = "show-ignored"
// Audit global option which indicates that gosec runs in audit mode
Audit GlobalOption = "audit"
// NoSecAlternative global option alternative for #nosec directive

View file

@ -97,6 +97,7 @@ type Issue struct {
Code string `json:"code"` // Impacted code line
Line string `json:"line"` // Line number in file
Col string `json:"column"` // Column number in line
NoSec bool `json:"nosec"` // true if the issue is nosec
}
// FileLocation point out the file path and line number in file

View file

@ -62,6 +62,8 @@ const templateContent = `
level += " is-warning";
} else if (this.props.level === "LOW") {
level += " is-info";
} else if (this.props.level === "WAIVED") {
level += " is-success";
}
level +=" is-rounded";
return (
@ -96,6 +98,7 @@ const templateContent = `
</div>
<div className="column is-one-quarter">
<div className="field is-grouped is-grouped-multiline">
{this.props.data.nosec && <IssueTag label="NoSec" level="WAIVED"/>}
<IssueTag label="Severity" level={ this.props.data.severity }/>
<IssueTag label="Confidence" level={ this.props.data.confidence }/>
</div>

View file

@ -8,7 +8,7 @@ Golang errors in file: [{{ $filePath }}]:
{{end}}
{{end}}
{{ range $index, $issue := .Issues }}
[{{ highlight $issue.FileLocation $issue.Severity }}] - {{ $issue.RuleID }} ({{ $issue.Cwe.SprintID }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
[{{ highlight $issue.FileLocation $issue.Severity $issue.NoSec }}] - {{ $issue.RuleID }}{{ if $issue.NoSec }} ({{- success "NoSec" -}}){{ end }} ({{ $issue.Cwe.SprintID }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
{{ printCode $issue }}
{{ end }}

View file

@ -45,7 +45,7 @@ func plainTextFuncMap(enableColor bool) template.FuncMap {
// by default those functions return the given content untouched
return template.FuncMap{
"highlight": func(t string, s gosec.Score) string {
"highlight": func(t string, s gosec.Score, ignored bool) string {
return t
},
"danger": fmt.Sprint,
@ -56,7 +56,10 @@ func plainTextFuncMap(enableColor bool) template.FuncMap {
}
// highlight returns content t colored based on Score
func highlight(t string, s gosec.Score) string {
func highlight(t string, s gosec.Score, ignored bool) string {
if ignored {
return defaultTheme.Sprint(t)
}
switch s {
case gosec.High:
return errorTheme.Sprint(t)