Skip to content

Commit c0e8da3

Browse files
simar7nikpivkin
andauthored
feat(misconf): Support --skip-* for all included modules (#7579)
Signed-off-by: nikpivkin <[email protected]> Co-authored-by: nikpivkin <[email protected]>
1 parent 3562529 commit c0e8da3

File tree

12 files changed

+181
-41
lines changed

12 files changed

+181
-41
lines changed

pkg/commands/artifact/run.go

+2
Original file line numberDiff line numberDiff line change
@@ -659,5 +659,7 @@ func initMisconfScannerOption(opts flag.Options) (misconf.ScannerOption, error)
659659
TfExcludeDownloaded: opts.TfExcludeDownloaded,
660660
FilePatterns: opts.FilePatterns,
661661
ConfigFileSchemas: configSchemas,
662+
SkipFiles: opts.SkipFiles,
663+
SkipDirs: opts.SkipDirs,
662664
}, nil
663665
}

pkg/fanal/utils/utils.go

+27
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ import (
99
"os"
1010
"os/exec"
1111
"path/filepath"
12+
"strings"
1213
"unicode"
1314

15+
"github.com/bmatcuk/doublestar/v4"
16+
"github.com/samber/lo"
1417
"golang.org/x/xerrors"
1518

19+
"github.com/aquasecurity/trivy/pkg/log"
1620
xio "github.com/aquasecurity/trivy/pkg/x/io"
1721
)
1822

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

105+
func CleanSkipPaths(skipPaths []string) []string {
106+
return lo.Map(skipPaths, func(skipPath string, index int) string {
107+
skipPath = filepath.ToSlash(filepath.Clean(skipPath))
108+
return strings.TrimLeft(skipPath, "/")
109+
})
110+
}
111+
112+
func SkipPath(path string, skipPaths []string) bool {
113+
path = strings.TrimLeft(path, "/")
114+
115+
// skip files
116+
for _, pattern := range skipPaths {
117+
match, err := doublestar.Match(pattern, path)
118+
if err != nil {
119+
return false // return early if bad pattern
120+
} else if match {
121+
log.Debug("Skipping path", log.String("path", path))
122+
return true
123+
}
124+
}
125+
return false
126+
}
127+
101128
func ExtractPrintableBytes(content xio.ReadSeekerAt) ([]byte, error) {
102129
const minLength = 4 // Minimum length of strings to extract
103130
var result []byte

pkg/fanal/walker/fs.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"golang.org/x/xerrors"
1111

12+
"github.com/aquasecurity/trivy/pkg/fanal/utils"
1213
"github.com/aquasecurity/trivy/pkg/log"
1314
xio "github.com/aquasecurity/trivy/pkg/x/io"
1415
)
@@ -53,13 +54,13 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc {
5354
// Skip unnecessary files
5455
switch {
5556
case d.IsDir():
56-
if SkipPath(relPath, opt.SkipDirs) {
57+
if utils.SkipPath(relPath, opt.SkipDirs) {
5758
return filepath.SkipDir
5859
}
5960
return nil
6061
case !d.Type().IsRegular():
6162
return nil
62-
case SkipPath(relPath, opt.SkipFiles):
63+
case utils.SkipPath(relPath, opt.SkipFiles):
6364
return nil
6465
}
6566

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

151-
relativePaths = CleanSkipPaths(relativePaths)
152+
relativePaths = utils.CleanSkipPaths(relativePaths)
152153
return relativePaths
153154
}
154155

pkg/fanal/walker/tar.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ type LayerTar struct {
2727

2828
func NewLayerTar(opt Option) LayerTar {
2929
return LayerTar{
30-
skipFiles: CleanSkipPaths(opt.SkipFiles),
31-
skipDirs: CleanSkipPaths(opt.SkipDirs),
30+
skipFiles: utils.CleanSkipPaths(opt.SkipFiles),
31+
skipDirs: utils.CleanSkipPaths(opt.SkipDirs),
3232
}
3333
}
3434

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

6464
switch hdr.Typeflag {
6565
case tar.TypeDir:
66-
if SkipPath(filePath, w.skipDirs) {
66+
if utils.SkipPath(filePath, w.skipDirs) {
6767
skippedDirs = append(skippedDirs, filePath)
6868
continue
6969
}
7070
case tar.TypeReg:
71-
if SkipPath(filePath, w.skipFiles) {
71+
if utils.SkipPath(filePath, w.skipFiles) {
7272
continue
7373
}
7474
// symlinks and hardlinks have no content in reader, skip them

pkg/fanal/walker/vm.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/masahiro331/go-xfs-filesystem/xfs"
1818
"golang.org/x/xerrors"
1919

20+
"github.com/aquasecurity/trivy/pkg/fanal/utils"
2021
"github.com/aquasecurity/trivy/pkg/fanal/vm/filesystem"
2122
"github.com/aquasecurity/trivy/pkg/log"
2223
xio "github.com/aquasecurity/trivy/pkg/x/io"
@@ -131,13 +132,13 @@ func (w *VM) fsWalk(fsys fs.FS, path string, d fs.DirEntry, err error) error {
131132
pathName := strings.TrimPrefix(filepath.Clean(path), "/")
132133
switch {
133134
case fi.IsDir():
134-
if SkipPath(pathName, w.skipDirs) {
135+
if utils.SkipPath(pathName, w.skipDirs) {
135136
return filepath.SkipDir
136137
}
137138
return nil
138139
case !fi.Mode().IsRegular():
139140
return nil
140-
case SkipPath(pathName, w.skipFiles):
141+
case utils.SkipPath(pathName, w.skipFiles):
141142
return nil
142143
case fi.Mode()&0x1000 == 0x1000 ||
143144
fi.Mode()&0x2000 == 0x2000 ||

pkg/fanal/walker/walk.go

-29
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,8 @@ package walker
22

33
import (
44
"os"
5-
"path/filepath"
6-
"strings"
7-
8-
"github.com/bmatcuk/doublestar/v4"
9-
"github.com/samber/lo"
105

116
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
12-
"github.com/aquasecurity/trivy/pkg/log"
137
)
148

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

2923
type WalkFunc func(filePath string, info os.FileInfo, opener analyzer.Opener) error
30-
31-
func CleanSkipPaths(skipPaths []string) []string {
32-
return lo.Map(skipPaths, func(skipPath string, index int) string {
33-
skipPath = filepath.ToSlash(filepath.Clean(skipPath))
34-
return strings.TrimLeft(skipPath, "/")
35-
})
36-
}
37-
38-
func SkipPath(path string, skipPaths []string) bool {
39-
path = strings.TrimLeft(path, "/")
40-
41-
// skip files
42-
for _, pattern := range skipPaths {
43-
match, err := doublestar.Match(pattern, path)
44-
if err != nil {
45-
return false // return early if bad pattern
46-
} else if match {
47-
log.Debug("Skipping path", log.String("path", path))
48-
return true
49-
}
50-
}
51-
return false
52-
}

pkg/fanal/walker/walk_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
"github.com/stretchr/testify/assert"
99

10-
"github.com/aquasecurity/trivy/pkg/fanal/walker"
10+
"github.com/aquasecurity/trivy/pkg/fanal/utils"
1111
)
1212

1313
func TestSkipFile(t *testing.T) {
@@ -64,7 +64,7 @@ func TestSkipFile(t *testing.T) {
6464
t.Run(tt.name, func(t *testing.T) {
6565
for file, want := range tt.wants {
6666
file = filepath.ToSlash(filepath.Clean(file))
67-
got := walker.SkipPath(file, walker.CleanSkipPaths(tt.skipFiles))
67+
got := utils.SkipPath(file, utils.CleanSkipPaths(tt.skipFiles))
6868
assert.Equal(t, want, got, fmt.Sprintf("skipFiles: %s, file: %s", tt.skipFiles, file))
6969
}
7070
})
@@ -138,7 +138,7 @@ func TestSkipDir(t *testing.T) {
138138
t.Run(tt.name, func(t *testing.T) {
139139
for dir, want := range tt.wants {
140140
dir = filepath.ToSlash(filepath.Clean(dir))
141-
got := walker.SkipPath(dir, walker.CleanSkipPaths(tt.skipDirs))
141+
got := utils.SkipPath(dir, utils.CleanSkipPaths(tt.skipDirs))
142142
assert.Equal(t, want, got, fmt.Sprintf("defaultSkipDirs: %s, dir: %s", tt.skipDirs, dir))
143143
}
144144
})

pkg/iac/scanners/terraform/options.go

+16
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,19 @@ func ScannerWithConfigsFileSystem(fsys fs.FS) options.ScannerOption {
8484
}
8585
}
8686
}
87+
88+
func ScannerWithSkipFiles(files []string) options.ScannerOption {
89+
return func(s options.ConfigurableScanner) {
90+
if tf, ok := s.(ConfigurableTerraformScanner); ok {
91+
tf.AddParserOptions(parser.OptionWithSkipFiles(files))
92+
}
93+
}
94+
}
95+
96+
func ScannerWithSkipDirs(dirs []string) options.ScannerOption {
97+
return func(s options.ConfigurableScanner) {
98+
if tf, ok := s.(ConfigurableTerraformScanner); ok {
99+
tf.AddParserOptions(parser.OptionWithSkipDirs(dirs))
100+
}
101+
}
102+
}

pkg/iac/scanners/terraform/parser/option.go

+12
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,15 @@ func OptionWithConfigsFS(fsys fs.FS) Option {
4949
p.configsFS = fsys
5050
}
5151
}
52+
53+
func OptionWithSkipFiles(files []string) Option {
54+
return func(p *Parser) {
55+
p.skipPaths = files
56+
}
57+
}
58+
59+
func OptionWithSkipDirs(dirs []string) Option {
60+
return func(p *Parser) {
61+
p.skipPaths = dirs
62+
}
63+
}

pkg/iac/scanners/terraform/parser/parser.go

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/hashicorp/hcl/v2/hclparse"
1616
"github.com/zclconf/go-cty/cty"
1717

18+
"github.com/aquasecurity/trivy/pkg/fanal/utils"
1819
"github.com/aquasecurity/trivy/pkg/iac/ignore"
1920
"github.com/aquasecurity/trivy/pkg/iac/terraform"
2021
tfcontext "github.com/aquasecurity/trivy/pkg/iac/terraform/context"
@@ -47,6 +48,7 @@ type Parser struct {
4748
skipCachedModules bool
4849
fsMap map[string]fs.FS
4950
configsFS fs.FS
51+
skipPaths []string
5052
}
5153

5254
// New creates a new Parser
@@ -78,6 +80,7 @@ func (p *Parser) newModuleParser(moduleFS fs.FS, moduleSource, modulePath, modul
7880
mp.moduleName = moduleName
7981
mp.logger = log.WithPrefix("terraform parser").With("module", moduleName)
8082
mp.projectRoot = p.projectRoot
83+
mp.skipPaths = p.skipPaths
8184
p.children = append(p.children, mp)
8285
for _, option := range p.options {
8386
option(mp)
@@ -155,6 +158,10 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error {
155158
if info.IsDir() {
156159
continue
157160
}
161+
if utils.SkipPath(realPath, utils.CleanSkipPaths(p.skipPaths)) {
162+
p.logger.Debug("Skipping path based on input glob", log.FilePath(realPath), log.Any("glob", p.skipPaths))
163+
continue
164+
}
158165
paths = append(paths, realPath)
159166
}
160167
sort.Strings(paths)

pkg/iac/scanners/terraform/scanner_test.go

+99
Original file line numberDiff line numberDiff line change
@@ -1118,3 +1118,102 @@ func TestSkipDeprecatedGoChecks(t *testing.T) {
11181118
require.Len(t, results, 1)
11191119
})
11201120
}
1121+
1122+
func TestSkipDir(t *testing.T) {
1123+
fs := testutil.CreateFS(t, map[string]string{
1124+
"deployments/main.tf": `
1125+
module "use_bad_configuration" {
1126+
source = "../modules"
1127+
}
1128+
1129+
module "use_bad_configuration_2" {
1130+
source = "../modules/modules2"
1131+
}
1132+
`,
1133+
"modules/misconfig.tf": `data "aws_iam_policy_document" "bad" {
1134+
statement {
1135+
actions = [
1136+
"apigateway:*",
1137+
]
1138+
1139+
resources = [
1140+
"*",
1141+
]
1142+
}
1143+
}
1144+
1145+
resource "aws_iam_policy" "bad_configuration" {
1146+
name_prefix = local.setup_role_name
1147+
policy = data.aws_iam_policy_document.bad.json
1148+
}
1149+
`,
1150+
"modules/modules2/misconfig.tf": `data "aws_iam_policy_document" "bad" {
1151+
statement {
1152+
actions = [
1153+
"apigateway:*",
1154+
]
1155+
1156+
resources = [
1157+
"*",
1158+
]
1159+
}
1160+
}
1161+
1162+
resource "aws_iam_policy" "bad_configuration" {
1163+
name_prefix = local.setup_role_name
1164+
policy = data.aws_iam_policy_document.bad.json
1165+
}
1166+
`,
1167+
})
1168+
1169+
t.Run("use skip-dir option", func(t *testing.T) {
1170+
scanner := New(
1171+
options.ScannerWithIncludeDeprecatedChecks(true),
1172+
ScannerWithSkipDirs([]string{"**/modules/**"}),
1173+
ScannerWithAllDirectories(true),
1174+
)
1175+
1176+
results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
1177+
require.NoError(t, err)
1178+
1179+
assert.Empty(t, results)
1180+
})
1181+
1182+
t.Run("use skip-files option", func(t *testing.T) {
1183+
scanner := New(
1184+
options.ScannerWithIncludeDeprecatedChecks(true),
1185+
ScannerWithSkipFiles([]string{"**/modules/**/*.tf"}),
1186+
ScannerWithAllDirectories(true),
1187+
)
1188+
1189+
results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
1190+
require.NoError(t, err)
1191+
1192+
assert.Empty(t, results)
1193+
})
1194+
1195+
t.Run("non existing value for skip-files option", func(t *testing.T) {
1196+
scanner := New(
1197+
options.ScannerWithIncludeDeprecatedChecks(true),
1198+
ScannerWithSkipFiles([]string{"foo/bar*.tf"}),
1199+
ScannerWithAllDirectories(true),
1200+
)
1201+
1202+
results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
1203+
require.NoError(t, err)
1204+
1205+
assert.Len(t, results, 4)
1206+
})
1207+
1208+
t.Run("empty skip-files option", func(t *testing.T) {
1209+
scanner := New(
1210+
options.ScannerWithIncludeDeprecatedChecks(true),
1211+
ScannerWithAllDirectories(true),
1212+
)
1213+
1214+
results, err := scanner.ScanFS(context.TODO(), fs, "deployments")
1215+
require.NoError(t, err)
1216+
1217+
assert.Len(t, results, 4)
1218+
})
1219+
}

pkg/misconf/scanner.go

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ type ScannerOption struct {
7676
ConfigFileSchemas []*ConfigFileSchema
7777

7878
DisabledCheckIDs []string
79+
SkipFiles []string
80+
SkipDirs []string
7981
}
8082

8183
func (o *ScannerOption) Sort() {
@@ -297,6 +299,8 @@ func addTFOpts(opts []options.ScannerOption, scannerOption ScannerOption) ([]opt
297299
opts = append(opts,
298300
terraform.ScannerWithAllDirectories(true),
299301
terraform.ScannerWithSkipDownloaded(scannerOption.TfExcludeDownloaded),
302+
terraform.ScannerWithSkipFiles(scannerOption.SkipFiles),
303+
terraform.ScannerWithSkipDirs(scannerOption.SkipDirs),
300304
)
301305

302306
return opts, nil

0 commit comments

Comments
 (0)