Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(misconf): Support --skip-* for all included modules #7579

Merged
merged 7 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/commands/artifact/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,5 +659,7 @@ func initMisconfScannerOption(opts flag.Options) (misconf.ScannerOption, error)
TfExcludeDownloaded: opts.TfExcludeDownloaded,
FilePatterns: opts.FilePatterns,
ConfigFileSchemas: configSchemas,
SkipFiles: opts.SkipFiles,
SkipDirs: opts.SkipDirs,
}, nil
}
27 changes: 27 additions & 0 deletions pkg/fanal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"unicode"

"github.com/bmatcuk/doublestar/v4"
"github.com/samber/lo"
"golang.org/x/xerrors"

"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
)

Expand Down Expand Up @@ -98,6 +102,29 @@ func IsBinary(content xio.ReadSeekerAt, fileSize int64) (bool, error) {
return false, nil
}

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 {
log.Debug("Skipping path", log.String("path", path))
return true
}
}
return false
}

func ExtractPrintableBytes(content xio.ReadSeekerAt) ([]byte, error) {
const minLength = 4 // Minimum length of strings to extract
var result []byte
Expand Down
7 changes: 4 additions & 3 deletions pkg/fanal/walker/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"golang.org/x/xerrors"

"github.com/aquasecurity/trivy/pkg/fanal/utils"
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
)
Expand Down Expand Up @@ -53,13 +54,13 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc {
// Skip unnecessary files
switch {
case d.IsDir():
if SkipPath(relPath, opt.SkipDirs) {
if utils.SkipPath(relPath, opt.SkipDirs) {
return filepath.SkipDir
}
return nil
case !d.Type().IsRegular():
return nil
case SkipPath(relPath, opt.SkipFiles):
case utils.SkipPath(relPath, opt.SkipFiles):
return nil
}

Expand Down Expand Up @@ -148,7 +149,7 @@ func (w *FS) BuildSkipPaths(base string, paths []string) []string {
relativePaths = append(relativePaths, relPath)
}

relativePaths = CleanSkipPaths(relativePaths)
relativePaths = utils.CleanSkipPaths(relativePaths)
return relativePaths
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/fanal/walker/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ type LayerTar struct {

func NewLayerTar(opt Option) LayerTar {
return LayerTar{
skipFiles: CleanSkipPaths(opt.SkipFiles),
skipDirs: CleanSkipPaths(opt.SkipDirs),
skipFiles: utils.CleanSkipPaths(opt.SkipFiles),
skipDirs: utils.CleanSkipPaths(opt.SkipDirs),
}
}

Expand Down Expand Up @@ -63,12 +63,12 @@ func (w LayerTar) Walk(layer io.Reader, analyzeFn WalkFunc) ([]string, []string,

switch hdr.Typeflag {
case tar.TypeDir:
if SkipPath(filePath, w.skipDirs) {
if utils.SkipPath(filePath, w.skipDirs) {
skippedDirs = append(skippedDirs, filePath)
continue
}
case tar.TypeReg:
if SkipPath(filePath, w.skipFiles) {
if utils.SkipPath(filePath, w.skipFiles) {
continue
}
// symlinks and hardlinks have no content in reader, skip them
Expand Down
5 changes: 3 additions & 2 deletions pkg/fanal/walker/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/masahiro331/go-xfs-filesystem/xfs"
"golang.org/x/xerrors"

"github.com/aquasecurity/trivy/pkg/fanal/utils"
"github.com/aquasecurity/trivy/pkg/fanal/vm/filesystem"
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
Expand Down Expand Up @@ -131,13 +132,13 @@ func (w *VM) fsWalk(fsys fs.FS, path string, d fs.DirEntry, err error) error {
pathName := strings.TrimPrefix(filepath.Clean(path), "/")
switch {
case fi.IsDir():
if SkipPath(pathName, w.skipDirs) {
if utils.SkipPath(pathName, w.skipDirs) {
return filepath.SkipDir
}
return nil
case !fi.Mode().IsRegular():
return nil
case SkipPath(pathName, w.skipFiles):
case utils.SkipPath(pathName, w.skipFiles):
return nil
case fi.Mode()&0x1000 == 0x1000 ||
fi.Mode()&0x2000 == 0x2000 ||
Expand Down
29 changes: 0 additions & 29 deletions pkg/fanal/walker/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ package walker

import (
"os"
"path/filepath"
"strings"

"github.com/bmatcuk/doublestar/v4"
"github.com/samber/lo"

"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/log"
)

const defaultSizeThreshold = int64(100) << 20 // 200MB
Expand All @@ -27,26 +21,3 @@ type Option struct {
}

type WalkFunc func(filePath string, info os.FileInfo, opener analyzer.Opener) error

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 {
log.Debug("Skipping path", log.String("path", path))
return true
}
}
return false
}
6 changes: 3 additions & 3 deletions pkg/fanal/walker/walk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/aquasecurity/trivy/pkg/fanal/walker"
"github.com/aquasecurity/trivy/pkg/fanal/utils"
)

func TestSkipFile(t *testing.T) {
Expand Down Expand Up @@ -64,7 +64,7 @@ func TestSkipFile(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
for file, want := range tt.wants {
file = filepath.ToSlash(filepath.Clean(file))
got := walker.SkipPath(file, walker.CleanSkipPaths(tt.skipFiles))
got := utils.SkipPath(file, utils.CleanSkipPaths(tt.skipFiles))
assert.Equal(t, want, got, fmt.Sprintf("skipFiles: %s, file: %s", tt.skipFiles, file))
}
})
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestSkipDir(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
for dir, want := range tt.wants {
dir = filepath.ToSlash(filepath.Clean(dir))
got := walker.SkipPath(dir, walker.CleanSkipPaths(tt.skipDirs))
got := utils.SkipPath(dir, utils.CleanSkipPaths(tt.skipDirs))
assert.Equal(t, want, got, fmt.Sprintf("defaultSkipDirs: %s, dir: %s", tt.skipDirs, dir))
}
})
Expand Down
16 changes: 16 additions & 0 deletions pkg/iac/scanners/terraform/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
}
12 changes: 12 additions & 0 deletions pkg/iac/scanners/terraform/parser/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
7 changes: 7 additions & 0 deletions pkg/iac/scanners/terraform/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/zclconf/go-cty/cty"

"github.com/aquasecurity/trivy/pkg/fanal/utils"
"github.com/aquasecurity/trivy/pkg/iac/ignore"
"github.com/aquasecurity/trivy/pkg/iac/terraform"
tfcontext "github.com/aquasecurity/trivy/pkg/iac/terraform/context"
Expand Down Expand Up @@ -47,6 +48,7 @@ type Parser struct {
skipCachedModules bool
fsMap map[string]fs.FS
configsFS fs.FS
skipPaths []string
}

// New creates a new Parser
Expand Down Expand Up @@ -78,6 +80,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)
Expand Down Expand Up @@ -155,6 +158,10 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error {
if info.IsDir() {
continue
}
if utils.SkipPath(realPath, utils.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)
Expand Down
99 changes: 99 additions & 0 deletions pkg/iac/scanners/terraform/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1118,3 +1118,102 @@ 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) {
simar7 marked this conversation as resolved.
Show resolved Hide resolved
scanner := New(
options.ScannerWithIncludeDeprecatedChecks(true),
ScannerWithSkipDirs([]string{"**/modules/**"}),
ScannerWithAllDirectories(true),
)

results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
require.NoError(t, err)

assert.Empty(t, results)
})

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.Empty(t, results)
})

t.Run("non existing value for skip-files option", func(t *testing.T) {
scanner := New(
options.ScannerWithIncludeDeprecatedChecks(true),
ScannerWithSkipFiles([]string{"foo/bar*.tf"}),
ScannerWithAllDirectories(true),
)

results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
require.NoError(t, err)

assert.Len(t, results, 4)
})

t.Run("empty skip-files option", func(t *testing.T) {
scanner := New(
options.ScannerWithIncludeDeprecatedChecks(true),
ScannerWithAllDirectories(true),
)

results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
require.NoError(t, err)

assert.Len(t, results, 4)
})
}
4 changes: 4 additions & 0 deletions pkg/misconf/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type ScannerOption struct {
ConfigFileSchemas []*ConfigFileSchema

DisabledCheckIDs []string
SkipFiles []string
SkipDirs []string
}

func (o *ScannerOption) Sort() {
Expand Down Expand Up @@ -297,6 +299,8 @@ func addTFOpts(opts []options.ScannerOption, scannerOption ScannerOption) ([]opt
opts = append(opts,
terraform.ScannerWithAllDirectories(true),
terraform.ScannerWithSkipDownloaded(scannerOption.TfExcludeDownloaded),
terraform.ScannerWithSkipFiles(scannerOption.SkipFiles),
terraform.ScannerWithSkipDirs(scannerOption.SkipDirs),
)

return opts, nil
Expand Down