Allow excluding generated files

This commit is contained in:
Marc Brugger 2021-08-04 17:33:20 +02:00 committed by GitHub
parent 521e69ef66
commit 62db81342e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 104 additions and 27 deletions

View file

@ -236,7 +236,6 @@ gosec will ignore test files across all packages and any dependencies in your ve
The scanning of test files can be enabled with the following flag: The scanning of test files can be enabled with the following flag:
```bash ```bash
gosec -tests ./... gosec -tests ./...
``` ```
@ -246,6 +245,19 @@ Also additional folders can be excluded as follows:
gosec -exclude-dir=rules -exclude-dir=cmd ./... gosec -exclude-dir=rules -exclude-dir=cmd ./...
``` ```
### Excluding generated files
gosec can ignore generated go files with default generated code comment.
```
// Code generated by some generator DO NOT EDIT.
```
```bash
gosec -exclude-generated ./...
```
### Annotating code ### Annotating code
As with all automated detection tools, there will be cases of false positives. In cases where gosec reports a failure that has been manually verified as being safe, As with all automated detection tools, there will be cases of false positives. In cases where gosec reports a failure that has been manually verified as being safe,

View file

@ -43,6 +43,8 @@ const LoadMode = packages.NeedName |
packages.NeedTypesInfo | packages.NeedTypesInfo |
packages.NeedSyntax packages.NeedSyntax
var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
// 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.
// It is passed through to all rule functions as they are called. Rules may use // It is passed through to all rule functions as they are called. Rules may use
// this data in conjunction withe the encountered AST node. // this data in conjunction withe the encountered AST node.
@ -70,19 +72,20 @@ type Metrics struct {
// Analyzer object is the main object of gosec. It has methods traverse an AST // Analyzer object is the main object of gosec. It has methods traverse an AST
// and invoke the correct checking rules as on each node as required. // and invoke the correct checking rules as on each node as required.
type Analyzer struct { type Analyzer struct {
ignoreNosec bool ignoreNosec bool
ruleset RuleSet ruleset RuleSet
context *Context context *Context
config Config config Config
logger *log.Logger logger *log.Logger
issues []*Issue issues []*Issue
stats *Metrics stats *Metrics
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
} }
// NewAnalyzer builds a new analyzer. // NewAnalyzer builds a new analyzer.
func NewAnalyzer(conf Config, tests bool, logger *log.Logger) *Analyzer { func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, logger *log.Logger) *Analyzer {
ignoreNoSec := false ignoreNoSec := false
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil { if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
ignoreNoSec = enabled ignoreNoSec = enabled
@ -91,15 +94,16 @@ func NewAnalyzer(conf Config, tests bool, logger *log.Logger) *Analyzer {
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags) logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
} }
return &Analyzer{ return &Analyzer{
ignoreNosec: ignoreNoSec, ignoreNosec: ignoreNoSec,
ruleset: make(RuleSet), ruleset: make(RuleSet),
context: &Context{}, context: &Context{},
config: conf, config: conf,
logger: logger, logger: logger,
issues: make([]*Issue, 0, 16), issues: make([]*Issue, 0, 16),
stats: &Metrics{}, stats: &Metrics{},
errors: make(map[string][]Error), errors: make(map[string][]Error),
tests: tests, tests: tests,
excludeGenerated: excludeGenerated,
} }
} }
@ -202,6 +206,11 @@ func (gosec *Analyzer) Check(pkg *packages.Package) {
if filepath.Ext(checkedFile) != ".go" { if filepath.Ext(checkedFile) != ".go" {
continue continue
} }
if gosec.excludeGenerated && isGeneratedFile(file) {
gosec.logger.Println("Ignoring generated file:", checkedFile)
continue
}
gosec.logger.Println("Checking file:", checkedFile) gosec.logger.Println("Checking file:", checkedFile)
gosec.context.FileSet = pkg.Fset gosec.context.FileSet = pkg.Fset
gosec.context.Config = gosec.config gosec.context.Config = gosec.config
@ -219,6 +228,17 @@ func (gosec *Analyzer) Check(pkg *packages.Package) {
} }
} }
func isGeneratedFile(file *ast.File) bool {
for _, comment := range file.Comments {
for _, row := range comment.List {
if generatedCodePattern.MatchString(row.Text) {
return true
}
}
}
return false
}
// ParseErrors parses the errors from given package // ParseErrors parses the errors from given package
func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error { func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
if len(pkg.Errors) == 0 { if len(pkg.Errors) == 0 {

View file

@ -25,7 +25,7 @@ var _ = Describe("Analyzer", func() {
) )
BeforeEach(func() { BeforeEach(func() {
logger, _ = testutils.NewLogger() logger, _ = testutils.NewLogger()
analyzer = gosec.NewAnalyzer(nil, tests, logger) analyzer = gosec.NewAnalyzer(nil, tests, false, logger)
}) })
Context("when processing a package", func() { Context("when processing a package", func() {
@ -246,7 +246,7 @@ var _ = Describe("Analyzer", func() {
// overwrite nosec option // overwrite nosec option
nosecIgnoreConfig := gosec.NewConfig() nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true") nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger) customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders()) customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage() nosecPackage := testutils.NewTestPackage()
@ -269,7 +269,7 @@ var _ = Describe("Analyzer", func() {
// overwrite nosec option // overwrite nosec option
nosecIgnoreConfig := gosec.NewConfig() nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive") nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger) customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders()) customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage() nosecPackage := testutils.NewTestPackage()
@ -292,7 +292,7 @@ var _ = Describe("Analyzer", func() {
// overwrite nosec option // overwrite nosec option
nosecIgnoreConfig := gosec.NewConfig() nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive") nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger) customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders()) customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage() nosecPackage := testutils.NewTestPackage()
@ -308,7 +308,7 @@ var _ = Describe("Analyzer", func() {
}) })
It("should be able to analyze Go test package", func() { It("should be able to analyze Go test package", func() {
customAnalyzer := gosec.NewAnalyzer(nil, true, logger) customAnalyzer := gosec.NewAnalyzer(nil, true, false, logger)
customAnalyzer.LoadRules(rules.Generate().Builders()) customAnalyzer.LoadRules(rules.Generate().Builders())
pkg := testutils.NewTestPackage() pkg := testutils.NewTestPackage()
defer pkg.Close() defer pkg.Close()
@ -332,6 +332,48 @@ var _ = Describe("Analyzer", func() {
issues, _, _ := customAnalyzer.Report() issues, _, _ := customAnalyzer.Report()
Expect(issues).Should(HaveLen(1)) Expect(issues).Should(HaveLen(1))
}) })
It("should be able to scan generated files if NOT excluded", func() {
customAnalyzer := gosec.NewAnalyzer(nil, true, false, logger)
customAnalyzer.LoadRules(rules.Generate().Builders())
pkg := testutils.NewTestPackage()
defer pkg.Close()
pkg.AddFile("foo.go", `
package foo
// Code generated some-generator DO NOT EDIT.
func test() error {
return nil
}
func TestFoo(t *testing.T){
test()
}`)
err := pkg.Build()
Expect(err).ShouldNot(HaveOccurred())
err = customAnalyzer.Process(buildTags, pkg.Path)
Expect(err).ShouldNot(HaveOccurred())
issues, _, _ := customAnalyzer.Report()
Expect(issues).Should(HaveLen(1))
})
It("should be able to skip generated files if excluded", func() {
customAnalyzer := gosec.NewAnalyzer(nil, true, true, logger)
customAnalyzer.LoadRules(rules.Generate().Builders())
pkg := testutils.NewTestPackage()
defer pkg.Close()
pkg.AddFile("foo.go", `
package foo
// Code generated some-generator DO NOT EDIT.
func test() error {
return nil
}
func TestFoo(t *testing.T){
test()
}`)
err := pkg.Build()
Expect(err).ShouldNot(HaveOccurred())
err = customAnalyzer.Process(buildTags, pkg.Path)
Expect(err).ShouldNot(HaveOccurred())
issues, _, _ := customAnalyzer.Report()
Expect(issues).Should(HaveLen(0))
})
}) })
It("should be able to analyze Cgo files", func() { It("should be able to analyze Cgo files", func() {
analyzer.LoadRules(rules.Generate().Builders()) analyzer.LoadRules(rules.Generate().Builders())

View file

@ -93,6 +93,9 @@ var (
// rules to explicitly exclude // rules to explicitly exclude
flagRulesExclude = flag.String("exclude", "", "Comma separated list of rules IDs to exclude. (see rule list)") flagRulesExclude = flag.String("exclude", "", "Comma separated list of rules IDs to exclude. (see rule list)")
// rules to explicitly exclude
flagExcludeGenerated = flag.Bool("exclude-generated", false, "Exclude generated files")
// log to file or stderr // log to file or stderr
flagLogfile = flag.String("log", "", "Log messages to file rather than stderr") flagLogfile = flag.String("log", "", "Log messages to file rather than stderr")
@ -335,7 +338,7 @@ func main() {
} }
// Create the analyzer // Create the analyzer
analyzer := gosec.NewAnalyzer(config, *flagScanTests, logger) analyzer := gosec.NewAnalyzer(config, *flagScanTests, *flagExcludeGenerated, logger)
analyzer.LoadRules(ruleDefinitions.Builders()) analyzer.LoadRules(ruleDefinitions.Builders())
excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude) excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude)

View file

@ -25,7 +25,7 @@ var _ = Describe("gosec rules", func() {
BeforeEach(func() { BeforeEach(func() {
logger, _ = testutils.NewLogger() logger, _ = testutils.NewLogger()
config = gosec.NewConfig() config = gosec.NewConfig()
analyzer = gosec.NewAnalyzer(config, tests, logger) analyzer = gosec.NewAnalyzer(config, tests, false, logger)
runner = func(rule string, samples []testutils.CodeSample) { runner = func(rule string, samples []testutils.CodeSample) {
for n, sample := range samples { for n, sample := range samples {
analyzer.Reset() analyzer.Reset()