From 49365263dd0f091fcdcdf020a2fff3d4c3ff0c12 Mon Sep 17 00:00:00 2001 From: Simar Date: Sat, 21 Sep 2024 02:05:18 -0600 Subject: [PATCH] feat(misconf): Support `--skip-*` for all included modules Resolves: https://github.com/aquasecurity/trivy/issues/7220 --- pkg/commands/artifact/run.go | 1 + pkg/fanal/walker/walk.go | 2 - pkg/iac/scanners/terraform/options.go | 16 +++++ pkg/iac/scanners/terraform/parser/option.go | 12 ++++ pkg/iac/scanners/terraform/parser/parser.go | 34 ++++++++++ pkg/iac/scanners/terraform/scanner_test.go | 75 +++++++++++++++++++++ pkg/misconf/scanner.go | 2 + 7 files changed, 140 insertions(+), 2 deletions(-) diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index fa454c0cd276..ee697bf5bf57 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -659,5 +659,6 @@ func initMisconfScannerOption(opts flag.Options) (misconf.ScannerOption, error) TfExcludeDownloaded: opts.TfExcludeDownloaded, FilePatterns: opts.FilePatterns, ConfigFileSchemas: configSchemas, + SkipFiles: opts.SkipFiles, }, nil } diff --git a/pkg/fanal/walker/walk.go b/pkg/fanal/walker/walk.go index 52c13808d86b..ac16c47bb48b 100644 --- a/pkg/fanal/walker/walk.go +++ b/pkg/fanal/walker/walk.go @@ -9,7 +9,6 @@ import ( "github.com/samber/lo" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/log" ) const defaultSizeThreshold = int64(100) << 20 // 200MB @@ -44,7 +43,6 @@ func SkipPath(path string, skipPaths []string) bool { if err != nil { return false // return early if bad pattern } else if match { - log.Debug("Skipping path", log.String("path", path)) return true } } diff --git a/pkg/iac/scanners/terraform/options.go b/pkg/iac/scanners/terraform/options.go index d256f0a34daa..5a665a05638f 100644 --- a/pkg/iac/scanners/terraform/options.go +++ b/pkg/iac/scanners/terraform/options.go @@ -84,3 +84,19 @@ func ScannerWithConfigsFileSystem(fsys fs.FS) options.ScannerOption { } } } + +func ScannerWithSkipFiles(files []string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if tf, ok := s.(ConfigurableTerraformScanner); ok { + tf.AddParserOptions(parser.OptionWithSkipFiles(files)) + } + } +} + +func ScannerWithSkipDirs(dirs []string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if tf, ok := s.(ConfigurableTerraformScanner); ok { + tf.AddParserOptions(parser.OptionWithSkipDirs(dirs)) + } + } +} diff --git a/pkg/iac/scanners/terraform/parser/option.go b/pkg/iac/scanners/terraform/parser/option.go index 277cf2a58c61..f9ada6545225 100644 --- a/pkg/iac/scanners/terraform/parser/option.go +++ b/pkg/iac/scanners/terraform/parser/option.go @@ -49,3 +49,15 @@ func OptionWithConfigsFS(fsys fs.FS) Option { p.configsFS = fsys } } + +func OptionWithSkipFiles(files []string) Option { + return func(p *Parser) { + p.skipPaths = files + } +} + +func OptionWithSkipDirs(dirs []string) Option { + return func(p *Parser) { + p.skipPaths = dirs + } +} diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 60940d6c6241..57f62c17780e 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -11,8 +11,10 @@ import ( "sort" "strings" + "github.com/bmatcuk/doublestar/v4" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" + "github.com/samber/lo" "github.com/zclconf/go-cty/cty" "github.com/aquasecurity/trivy/pkg/iac/ignore" @@ -47,6 +49,7 @@ type Parser struct { skipCachedModules bool fsMap map[string]fs.FS configsFS fs.FS + skipPaths []string } // New creates a new Parser @@ -62,6 +65,7 @@ func New(moduleFS fs.FS, moduleSource string, opts ...Option) *Parser { configsFS: moduleFS, logger: log.WithPrefix("terraform parser").With("module", "root"), tfvars: make(map[string]cty.Value), + skipPaths: []string{}, } for _, option := range opts { @@ -78,6 +82,7 @@ func (p *Parser) newModuleParser(moduleFS fs.FS, moduleSource, modulePath, modul mp.moduleName = moduleName mp.logger = log.WithPrefix("terraform parser").With("module", moduleName) mp.projectRoot = p.projectRoot + mp.skipPaths = p.skipPaths p.children = append(p.children, mp) for _, option := range p.options { option(mp) @@ -152,9 +157,16 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { var paths []string for _, info := range fileInfos { realPath := path.Join(dir, info.Name()) + //fmt.Println(">>> realPath", realPath, "CleanSkipPaths(skipPaths)", CleanSkipPaths([]string{"../modules/**/*.tf"})) + //fmt.Println(">>> realPath", realPath, "CleanSkipPaths(skipPaths)", CleanSkipPaths(p.skipPaths)) if info.IsDir() { continue } + //if SkipPath(realPath, CleanSkipPaths([]string{"../modules/**/*.tf"})) { + if SkipPath(realPath, CleanSkipPaths(p.skipPaths)) { + p.logger.Debug("Skipping path based on input glob", log.FilePath(realPath), log.Any("glob", p.skipPaths)) + continue + } paths = append(paths, realPath) } sort.Strings(paths) @@ -358,3 +370,25 @@ func (s *paramParser) Parse(str string) bool { func (s *paramParser) Param() any { return s.params } + +func CleanSkipPaths(skipPaths []string) []string { + return lo.Map(skipPaths, func(skipPath string, index int) string { + skipPath = filepath.ToSlash(filepath.Clean(skipPath)) + return strings.TrimLeft(skipPath, "/") + }) +} + +func SkipPath(path string, skipPaths []string) bool { + path = strings.TrimLeft(path, "/") + + // skip files + for _, pattern := range skipPaths { + match, err := doublestar.Match(pattern, path) + if err != nil { + return false // return early if bad pattern + } else if match { + return true + } + } + return false +} diff --git a/pkg/iac/scanners/terraform/scanner_test.go b/pkg/iac/scanners/terraform/scanner_test.go index 39ee35e81321..45475d31274d 100644 --- a/pkg/iac/scanners/terraform/scanner_test.go +++ b/pkg/iac/scanners/terraform/scanner_test.go @@ -1118,3 +1118,78 @@ func TestSkipDeprecatedGoChecks(t *testing.T) { require.Len(t, results, 1) }) } + +func TestSkipDir(t *testing.T) { + fs := testutil.CreateFS(t, map[string]string{ + "deployments/main.tf": ` +module "use_bad_configuration" { + source = "../modules" +} + +module "use_bad_configuration_2" { + source = "../modules/modules2" +} +`, + "modules/misconfig.tf": `data "aws_iam_policy_document" "bad" { + statement { + actions = [ + "apigateway:*", + ] + + resources = [ + "*", + ] + } +} + +resource "aws_iam_policy" "bad_configuration" { + name_prefix = local.setup_role_name + policy = data.aws_iam_policy_document.bad.json +} +`, + "modules/modules2/misconfig.tf": `data "aws_iam_policy_document" "bad" { + statement { + actions = [ + "apigateway:*", + ] + + resources = [ + "*", + ] + } +} + +resource "aws_iam_policy" "bad_configuration" { + name_prefix = local.setup_role_name + policy = data.aws_iam_policy_document.bad.json +} +`, + }) + + t.Run("use skip-dir option", func(t *testing.T) { + scanner := New( + options.ScannerWithIncludeDeprecatedChecks(true), + ScannerWithSkipDirs([]string{"**/modules/**"}), + ScannerWithAllDirectories(true), + ) + + results, err := scanner.ScanFS(context.TODO(), fs, "deployments") + require.NoError(t, err) + + assert.Len(t, results, 0) + }) + + t.Run("use skip-files option", func(t *testing.T) { + scanner := New( + options.ScannerWithIncludeDeprecatedChecks(true), + ScannerWithSkipFiles([]string{"**/modules/**/*.tf"}), + ScannerWithAllDirectories(true), + ) + + results, err := scanner.ScanFS(context.TODO(), fs, "deployments") + require.NoError(t, err) + + assert.Len(t, results, 0) + }) + +} diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index db203364a3e4..1b93a0ecb2ec 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -74,6 +74,7 @@ type ScannerOption struct { FilePatterns []string ConfigFileSchemas []*ConfigFileSchema + SkipFiles []string } func (o *ScannerOption) Sort() { @@ -294,6 +295,7 @@ func addTFOpts(opts []options.ScannerOption, scannerOption ScannerOption) ([]opt opts = append(opts, terraform.ScannerWithAllDirectories(true), terraform.ScannerWithSkipDownloaded(scannerOption.TfExcludeDownloaded), + terraform.ScannerWithSkipFiles(scannerOption.SkipFiles), ) return opts, nil