mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 12:05:52 +00:00
Add possibility to list waived (nosec) marked issues but not count them as such
This commit is contained in:
parent
5a131be2ec
commit
ba23b5e49a
8 changed files with 76 additions and 18 deletions
26
analyzer.go
26
analyzer.go
|
@ -82,6 +82,7 @@ type Analyzer struct {
|
||||||
errors map[string][]Error // keys are file paths; values are the golang errors in those files
|
errors map[string][]Error // keys are file paths; values are the golang errors in those files
|
||||||
tests bool
|
tests bool
|
||||||
excludeGenerated bool
|
excludeGenerated bool
|
||||||
|
showIgnored bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAnalyzer builds a new analyzer.
|
// 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 {
|
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
|
||||||
ignoreNoSec = enabled
|
ignoreNoSec = enabled
|
||||||
}
|
}
|
||||||
|
showIgnored := false
|
||||||
|
if enabled, err := conf.IsGlobalEnabled(ShowIgnored); err == nil {
|
||||||
|
showIgnored = enabled
|
||||||
|
}
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
|
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
|
||||||
}
|
}
|
||||||
return &Analyzer{
|
return &Analyzer{
|
||||||
ignoreNosec: ignoreNoSec,
|
ignoreNosec: ignoreNoSec,
|
||||||
|
showIgnored: showIgnored,
|
||||||
ruleset: make(RuleSet),
|
ruleset: make(RuleSet),
|
||||||
context: &Context{},
|
context: &Context{},
|
||||||
config: conf,
|
config: conf,
|
||||||
|
@ -179,7 +185,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
|
||||||
}
|
}
|
||||||
|
|
||||||
if gosec.tests {
|
if gosec.tests {
|
||||||
testsFiles := []string{}
|
testsFiles := make([]string, 0)
|
||||||
testsFiles = append(testsFiles, basePackage.TestGoFiles...)
|
testsFiles = append(testsFiles, basePackage.TestGoFiles...)
|
||||||
testsFiles = append(testsFiles, basePackage.XTestGoFiles...)
|
testsFiles = append(testsFiles, basePackage.XTestGoFiles...)
|
||||||
for _, filename := range testsFiles {
|
for _, filename := range testsFiles {
|
||||||
|
@ -279,7 +285,7 @@ func (gosec *Analyzer) AppendError(file string, err error) {
|
||||||
if r.MatchString(err.Error()) {
|
if r.MatchString(err.Error()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errors := []Error{}
|
errors := make([]Error, 0)
|
||||||
if ferrs, ok := gosec.errors[file]; ok {
|
if ferrs, ok := gosec.errors[file]; ok {
|
||||||
errors = ferrs
|
errors = ferrs
|
||||||
}
|
}
|
||||||
|
@ -364,9 +370,8 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||||
gosec.context.Imports.TrackImport(n)
|
gosec.context.Imports.TrackImport(n)
|
||||||
|
|
||||||
for _, rule := range gosec.ruleset.RegisteredFor(n) {
|
for _, rule := range gosec.ruleset.RegisteredFor(n) {
|
||||||
if _, ok := ignores[rule.ID()]; ok {
|
_, ignored := ignores[rule.ID()]
|
||||||
continue
|
|
||||||
}
|
|
||||||
issue, err := rule.Match(n, gosec.context)
|
issue, err := rule.Match(n, gosec.context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
file, line := GetLocation(n, gosec.context)
|
file, line := GetLocation(n, gosec.context)
|
||||||
|
@ -374,8 +379,15 @@ 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)
|
gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
|
||||||
}
|
}
|
||||||
if issue != nil {
|
if issue != nil {
|
||||||
gosec.issues = append(gosec.issues, issue)
|
if gosec.showIgnored {
|
||||||
gosec.stats.NumFound++
|
issue.NoSec = ignored
|
||||||
|
}
|
||||||
|
if !ignored || !gosec.showIgnored {
|
||||||
|
gosec.stats.NumFound++
|
||||||
|
}
|
||||||
|
if !ignored || gosec.showIgnored || gosec.ignoreNosec {
|
||||||
|
gosec.issues = append(gosec.issues, issue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return gosec
|
return gosec
|
||||||
|
|
|
@ -261,6 +261,32 @@ var _ = Describe("Analyzer", func() {
|
||||||
Expect(nosecIssues).Should(HaveLen(sample.Errors))
|
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() {
|
It("should be possible to use an alternative nosec tag", func() {
|
||||||
// Rule for MD5 weak crypto usage
|
// Rule for MD5 weak crypto usage
|
||||||
sample := testutils.SampleCodeG401[0]
|
sample := testutils.SampleCodeG401[0]
|
||||||
|
|
|
@ -72,6 +72,9 @@ var (
|
||||||
// #nosec flag
|
// #nosec flag
|
||||||
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
|
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
|
// format output
|
||||||
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
|
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 {
|
if *flagIgnoreNoSec {
|
||||||
config.SetGlobal(gosec.Nosec, "true")
|
config.SetGlobal(gosec.Nosec, "true")
|
||||||
}
|
}
|
||||||
|
if *flagShowIgnored {
|
||||||
|
config.SetGlobal(gosec.ShowIgnored, "true")
|
||||||
|
}
|
||||||
if *flagAlternativeNoSec != "" {
|
if *flagAlternativeNoSec != "" {
|
||||||
config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
|
config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
|
||||||
}
|
}
|
||||||
|
@ -200,7 +206,7 @@ func loadRules(include, exclude string) rules.RuleList {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRootPaths(paths []string) []string {
|
func getRootPaths(paths []string) []string {
|
||||||
rootPaths := []string{}
|
rootPaths := make([]string, 0)
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
rootPath, err := gosec.RootPath(path)
|
rootPath, err := gosec.RootPath(path)
|
||||||
if err != nil {
|
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 {
|
func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.Score) ([]*gosec.Issue, int) {
|
||||||
result := []*gosec.Issue{}
|
result := make([]*gosec.Issue, 0)
|
||||||
|
trueIssues := 0
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
if issue.Severity >= severity && issue.Confidence >= confidence {
|
if issue.Severity >= severity && issue.Confidence >= confidence {
|
||||||
result = append(result, issue)
|
result = append(result, issue)
|
||||||
|
if !issue.NoSec || !*flagShowIgnored {
|
||||||
|
trueIssues++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result, trueIssues
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -372,9 +382,10 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter the issues by severity and confidence
|
// Filter the issues by severity and confidence
|
||||||
issues = filterIssues(issues, failSeverity, failConfidence)
|
var trueIssues int
|
||||||
if metrics.NumFound != len(issues) {
|
issues, trueIssues = filterIssues(issues, failSeverity, failConfidence)
|
||||||
metrics.NumFound = len(issues)
|
if metrics.NumFound != trueIssues {
|
||||||
|
metrics.NumFound = trueIssues
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit quietly if nothing was found
|
// Exit quietly if nothing was found
|
||||||
|
@ -390,7 +401,7 @@ func main() {
|
||||||
if *flagOutput == "" || *flagStdOut {
|
if *flagOutput == "" || *flagStdOut {
|
||||||
fileFormat := getPrintedFormat(*flagFormat, *flagVerbose)
|
fileFormat := getPrintedFormat(*flagFormat, *flagVerbose)
|
||||||
if err := printReport(fileFormat, *flagColor, rootPaths, reportInfo); err != nil {
|
if err := printReport(fileFormat, *flagColor, rootPaths, reportInfo); err != nil {
|
||||||
logger.Fatal((err))
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *flagOutput != "" {
|
if *flagOutput != "" {
|
||||||
|
|
|
@ -20,6 +20,8 @@ type GlobalOption string
|
||||||
const (
|
const (
|
||||||
// Nosec global option for #nosec directive
|
// Nosec global option for #nosec directive
|
||||||
Nosec GlobalOption = "nosec"
|
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 global option which indicates that gosec runs in audit mode
|
||||||
Audit GlobalOption = "audit"
|
Audit GlobalOption = "audit"
|
||||||
// NoSecAlternative global option alternative for #nosec directive
|
// NoSecAlternative global option alternative for #nosec directive
|
||||||
|
|
1
issue.go
1
issue.go
|
@ -97,6 +97,7 @@ type Issue struct {
|
||||||
Code string `json:"code"` // Impacted code line
|
Code string `json:"code"` // Impacted code line
|
||||||
Line string `json:"line"` // Line number in file
|
Line string `json:"line"` // Line number in file
|
||||||
Col string `json:"column"` // Column number in line
|
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
|
// FileLocation point out the file path and line number in file
|
||||||
|
|
|
@ -62,6 +62,8 @@ const templateContent = `
|
||||||
level += " is-warning";
|
level += " is-warning";
|
||||||
} else if (this.props.level === "LOW") {
|
} else if (this.props.level === "LOW") {
|
||||||
level += " is-info";
|
level += " is-info";
|
||||||
|
} else if (this.props.level === "WAIVED") {
|
||||||
|
level += " is-success";
|
||||||
}
|
}
|
||||||
level +=" is-rounded";
|
level +=" is-rounded";
|
||||||
return (
|
return (
|
||||||
|
@ -96,6 +98,7 @@ const templateContent = `
|
||||||
</div>
|
</div>
|
||||||
<div className="column is-one-quarter">
|
<div className="column is-one-quarter">
|
||||||
<div className="field is-grouped is-grouped-multiline">
|
<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="Severity" level={ this.props.data.severity }/>
|
||||||
<IssueTag label="Confidence" level={ this.props.data.confidence }/>
|
<IssueTag label="Confidence" level={ this.props.data.confidence }/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@ Golang errors in file: [{{ $filePath }}]:
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ range $index, $issue := .Issues }}
|
{{ 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 }}
|
{{ printCode $issue }}
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func plainTextFuncMap(enableColor bool) template.FuncMap {
|
||||||
|
|
||||||
// by default those functions return the given content untouched
|
// by default those functions return the given content untouched
|
||||||
return template.FuncMap{
|
return template.FuncMap{
|
||||||
"highlight": func(t string, s gosec.Score) string {
|
"highlight": func(t string, s gosec.Score, ignored bool) string {
|
||||||
return t
|
return t
|
||||||
},
|
},
|
||||||
"danger": fmt.Sprint,
|
"danger": fmt.Sprint,
|
||||||
|
@ -56,7 +56,10 @@ func plainTextFuncMap(enableColor bool) template.FuncMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// highlight returns content t colored based on Score
|
// 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 {
|
switch s {
|
||||||
case gosec.High:
|
case gosec.High:
|
||||||
return errorTheme.Sprint(t)
|
return errorTheme.Sprint(t)
|
||||||
|
|
Loading…
Reference in a new issue