Skip to content

Commit

Permalink
Add possibility to list waived (nosec) marked issues but not count th…
Browse files Browse the repository at this point in the history
…em as such
  • Loading branch information
bakito authored Aug 18, 2021
1 parent 5a131be commit ba23b5e
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 18 deletions.
26 changes: 19 additions & 7 deletions analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -364,18 +370,24 @@ 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)
file = path.Base(file)
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)
gosec.stats.NumFound++
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
Expand Down
26 changes: 26 additions & 0 deletions analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
27 changes: 19 additions & 8 deletions cmd/gosec/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand All @@ -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 != "" {
Expand Down
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions report/html/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion report/text/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
7 changes: 5 additions & 2 deletions report/text/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down

0 comments on commit ba23b5e

Please sign in to comment.