From 7851918c4f7b1c8a7c9f331a048e0f6debb6dd26 Mon Sep 17 00:00:00 2001 From: Cosmin Cojocar Date: Mon, 9 Sep 2019 14:01:36 +0200 Subject: [PATCH] Add support to exclude arbitrary folders from scanning (#353) Signed-off-by: Cosmin Cojocar --- README.md | 14 +++++++++++--- cmd/gosec/main.go | 32 +++++++++++++++++++++++--------- helpers.go | 28 ++++++++++++++++++++++++++-- helpers_test.go | 20 +++++++++++++++++++- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4e6eb24..25e28e8 100644 --- a/README.md +++ b/README.md @@ -137,13 +137,21 @@ of functions which will be skipped when auditing the not checked errors: gosec will fetch automatically the dependencies of the code which is being analyzed when go modules are turned on (e.g.` GO111MODULE=on`). If this is not the case, the dependencies need to be explicitly downloaded by running the `go get -d` command before the scan. +### Excluding test files and folders -### Excluding files +gosec will ignore test files across all packages and any dependencies in your vendor directory. -gosec will ignore tests files and any dependencies in your vendor directory. The scanning of these artifacts can be enabled with the following flags: +The scanning of test files can be enabled with the following flag: ```bash -gosec -tests -vendor ./... + +gosec -tests ./... +``` + +Also additional folders can be excluded as follows: + +```bash + gosec -exclude-dir=rules -exclude-dir=cmd ./... ``` ### Annotating code diff --git a/cmd/gosec/main.go b/cmd/gosec/main.go index c9c29d5..3a4e7d5 100644 --- a/cmd/gosec/main.go +++ b/cmd/gosec/main.go @@ -20,7 +20,6 @@ import ( "io/ioutil" "log" "os" - "regexp" "sort" "strings" @@ -58,6 +57,17 @@ USAGE: ` ) +type arrayFlags []string + +func (a *arrayFlags) String() string { + return strings.Join(*a, " ") +} + +func (a *arrayFlags) Set(value string) error { + *a = append(*a, value) + return nil +} + var ( // #nosec flag flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set") @@ -92,9 +102,6 @@ var ( // go build tags flagBuildTags = flag.String("tags", "", "Comma separated list of build tags") - // scan the vendor folder - flagScanVendor = flag.Bool("vendor", false, "Scan the vendor folder") - // fail by severity flagSeverity = flag.String("severity", "low", "Filter out the issues with a lower severity than the given value. Valid options are: low, medium, high") @@ -110,6 +117,9 @@ var ( // print version and quit with exit code 0 flagVersion = flag.Bool("version", false, "Print version and quit with exit code 0") + // exlude the folders from scan + flagDirsExclude arrayFlags + logger *log.Logger ) @@ -233,6 +243,13 @@ func main() { // Setup usage description flag.Usage = usage + // Setup the excluded folders from scan + flag.Var(&flagDirsExclude, "exclude-dir", "Exclude folder from scan (can be specified multiple times)") + err := flag.Set("exclude-dir", "vendor") + if err != nil { + fmt.Fprintf(os.Stderr, "\nError: failed to exclude the %q directory from scan", "vendor") + } + // Parse command line arguments flag.Parse() @@ -291,13 +308,10 @@ func main() { analyzer := gosec.NewAnalyzer(config, *flagScanTests, logger) analyzer.LoadRules(ruleDefinitions.Builders()) - var vendor *regexp.Regexp - if !*flagScanVendor { - vendor = regexp.MustCompile(`([\\/])?vendor([\\/])?`) - } + excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude) var packages []string for _, path := range flag.Args() { - pcks, err := gosec.PackagePaths(path, vendor) + pcks, err := gosec.PackagePaths(path, excludedDirs) if err != nil { logger.Fatal(err) } diff --git a/helpers.go b/helpers.go index 3fddae3..a0ddc1f 100644 --- a/helpers.go +++ b/helpers.go @@ -360,7 +360,7 @@ func FindVarIdentities(n *ast.BinaryExpr, c *Context) ([]*ast.Ident, bool) { } // PackagePaths returns a slice with all packages path at given root directory -func PackagePaths(root string, exclude *regexp.Regexp) ([]string, error) { +func PackagePaths(root string, excludes []*regexp.Regexp) ([]string, error) { if strings.HasSuffix(root, "...") { root = root[0 : len(root)-3] } else { @@ -370,7 +370,7 @@ func PackagePaths(root string, exclude *regexp.Regexp) ([]string, error) { err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error { if filepath.Ext(path) == ".go" { path = filepath.Dir(path) - if exclude != nil && exclude.MatchString(path) { + if isExcluded(path, excludes) { return nil } paths[path] = true @@ -388,6 +388,30 @@ func PackagePaths(root string, exclude *regexp.Regexp) ([]string, error) { return result, nil } +// isExcluded checks if a string matches any of the exclusion regexps +func isExcluded(str string, excludes []*regexp.Regexp) bool { + if excludes == nil { + return false + } + for _, exclude := range excludes { + if exclude != nil && exclude.MatchString(str) { + return true + } + } + return false +} + +// ExcludedDirsRegExp builds the regexps for a list of excluded dirs provided as strings +func ExcludedDirsRegExp(excludedDirs []string) []*regexp.Regexp { + var exps []*regexp.Regexp + for _, excludedDir := range excludedDirs { + str := fmt.Sprintf(`([\\/])?%s([\\/])?`, excludedDir) + r := regexp.MustCompile(str) + exps = append(exps, r) + } + return exps +} + // RootPath returns the absolute root path of a scan func RootPath(root string) (string, error) { if strings.HasSuffix(root, "...") { diff --git a/helpers_test.go b/helpers_test.go index f797203..f806758 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -43,7 +43,7 @@ var _ = Describe("Helpers", func() { Expect(err).ShouldNot(HaveOccurred()) exclude, err := regexp.Compile(`([\\/])?vendor([\\/])?`) Expect(err).ShouldNot(HaveOccurred()) - paths, err := gosec.PackagePaths(dir+"/...", exclude) + paths, err := gosec.PackagePaths(dir+"/...", []*regexp.Regexp{exclude}) Expect(err).ShouldNot(HaveOccurred()) Expect(paths).Should(Equal([]string{dir})) }) @@ -73,4 +73,22 @@ var _ = Describe("Helpers", func() { Expect(root).Should(Equal(filepath.Join(cwd, base))) }) }) + + Context("when excluding the dirs", func() { + It("should create a proper regexp", func() { + r := gosec.ExcludedDirsRegExp([]string{"test"}) + Expect(len(r)).Should(Equal(1)) + match := r[0].MatchString("/home/go/src/project/test/pkg") + Expect(match).Should(BeTrue()) + match = r[0].MatchString("/home/go/src/project/vendor/pkg") + Expect(match).Should(BeFalse()) + }) + + It("should create no regexp when dir list is empty", func() { + r := gosec.ExcludedDirsRegExp(nil) + Expect(len(r)).Should(Equal(0)) + r = gosec.ExcludedDirsRegExp([]string{}) + Expect(len(r)).Should(Equal(0)) + }) + }) })