Skip to content

Commit

Permalink
feat: allow restricting filesystem walk to specific folders
Browse files Browse the repository at this point in the history
Co-Authored-By: David du Colombier <[email protected]>
  • Loading branch information
lebauce and 0intro committed Dec 11, 2024
1 parent 4202c4b commit 5b93a9a
Show file tree
Hide file tree
Showing 19 changed files with 147 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ trivy config [flags] DIR
--k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0)
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ trivy filesystem [flags] PATH
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ trivy image [flags] IMAGE_NAME
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ trivy kubernetes [flags] [CONTEXT]
--node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.3.1")
--node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp")
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_rootfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ trivy rootfs [flags] ROOTDIR
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_sbom.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ trivy sbom [flags] SBOM_PATH
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ trivy vm [flags] VM_IMAGE
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
--only-dirs strings specify the directories where the traversal is allowed
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
Expand Down
2 changes: 2 additions & 0 deletions pkg/cache/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func CalcKey(id string, analyzerVersions analyzer.Versions, hookVersions map[str
HookVersions map[string]int
SkipFiles []string
SkipDirs []string
OnlyDirs []string
FilePatterns []string `json:",omitempty"`
DetectionPriority types.DetectionPriority `json:",omitempty"`
}{
Expand All @@ -38,6 +39,7 @@ func CalcKey(id string, analyzerVersions analyzer.Versions, hookVersions map[str
hookVersions,
artifactOpt.WalkerOption.SkipFiles,
artifactOpt.WalkerOption.SkipDirs,
artifactOpt.WalkerOption.OnlyDirs,
artifactOpt.FilePatterns,
artifactOpt.DetectionPriority,
}
Expand Down
1 change: 1 addition & 0 deletions pkg/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
SkipDirs: flag.SkipDirsFlag.Clone(),
SkipFiles: flag.SkipFilesFlag.Clone(),
FilePatterns: flag.FilePatternsFlag.Clone(),
OnlyDirs: flag.OnlyDirsFlag.Clone(),
}

configFlags := &flag.Flags{
Expand Down
1 change: 1 addition & 0 deletions pkg/commands/artifact/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan
WalkerOption: walker.Option{
SkipFiles: opts.SkipFiles,
SkipDirs: opts.SkipDirs,
OnlyDirs: opts.OnlyDirs,
},
},
}, scanOptions, nil
Expand Down
1 change: 1 addition & 0 deletions pkg/fanal/artifact/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func (o *Option) Sort() {
})
sort.Strings(o.WalkerOption.SkipFiles)
sort.Strings(o.WalkerOption.SkipDirs)
sort.Strings(o.WalkerOption.OnlyDirs)
sort.Strings(o.FilePatterns)
}

Expand Down
26 changes: 26 additions & 0 deletions pkg/fanal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,32 @@ func SkipPath(path string, skipPaths []string) bool {
return false
}

func OnlyPath(path string, onlyPaths []string) bool {
if len(onlyPaths) == 0 {
return false
}

if path == "" || path == "." {
return false
}

path = strings.TrimLeft(path, "/")

for _, pattern := range onlyPaths {
if strings.HasPrefix(pattern, path+"/") {
return false
}
match, err := doublestar.Match(pattern, path)
if err != nil {
return false // return early if bad pattern
} else if match {
return false
}
}
log.Debug("Skipping path", log.String("path", path))
return true
}

func ExtractPrintableBytes(content xio.ReadSeekerAt) ([]byte, error) {
const minLength = 4 // Minimum length of strings to extract
var result []byte
Expand Down
6 changes: 6 additions & 0 deletions pkg/fanal/walker/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func (w *FS) Walk(root string, opt Option, fn WalkFunc) error {
opt.SkipFiles = w.BuildSkipPaths(root, opt.SkipFiles)
opt.SkipDirs = w.BuildSkipPaths(root, opt.SkipDirs)
opt.SkipDirs = append(opt.SkipDirs, defaultSkipDirs...)
opt.OnlyDirs = w.BuildSkipPaths(root, opt.OnlyDirs)

walkDirFunc := w.WalkDirFunc(root, fn, opt)
walkDirFunc = w.onError(walkDirFunc)
Expand Down Expand Up @@ -57,11 +58,16 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc {
if utils.SkipPath(relPath, opt.SkipDirs) {
return filepath.SkipDir
}
if utils.OnlyPath(relPath, opt.OnlyDirs) {
return filepath.SkipDir
}
return nil
case !d.Type().IsRegular():
return nil
case utils.SkipPath(relPath, opt.SkipFiles):
return nil
case utils.OnlyPath(relPath, opt.OnlyDirs):
return nil
}

info, err := d.Info()
Expand Down
2 changes: 2 additions & 0 deletions pkg/fanal/walker/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ var parentDir = ".." + utils.PathSeparator
type LayerTar struct {
skipFiles []string
skipDirs []string
onlyDirs []string
}

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

Expand Down
2 changes: 2 additions & 0 deletions pkg/fanal/walker/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ var defaultSkipDirs = []string{
type Option struct {
SkipFiles []string
SkipDirs []string
OnlyDirs []string
}

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

84 changes: 84 additions & 0 deletions pkg/fanal/walker/walk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,87 @@ func TestSkipDir(t *testing.T) {
})
}
}

func Test_onlyDir(t *testing.T) {
tests := []struct {
name string
onlyDirs []string
wants map[string]bool
}{
{
name: "double star",
onlyDirs: []string{"/etc/**"},
wants: map[string]bool{
"/etc/foo/bar": false,
"/var/log/bar": true,
},
},
{
name: "double star",
onlyDirs: []string{"/**"},
wants: map[string]bool{
"/foo": false,
"/etc/foo": false,
"/var/log/bar": false,
},
},
{
name: "single star",
onlyDirs: []string{"/*"},
wants: map[string]bool{
"/foo": false,
"/etc/foo": true,
"/etc/foo/bar": true,
},
},
{
name: "single star",
onlyDirs: []string{"/etc/foo/*"},
wants: map[string]bool{
"/etc/foo/bar": false,
"/etc/foo2/bar": true,
"/var/etc/foo2/bar": true,
},
},
{
name: "single star",
onlyDirs: []string{"/*/bar"},
wants: map[string]bool{
"/etc/bar/bar": true,
"/etc/foo/bar": true,
"/etc/bar/foo": true,
"/etc/foo/bar/foo": true,
},
},
{
name: "double star",
onlyDirs: []string{"**/bar"},
wants: map[string]bool{
"/etc/bar/bar": false,
"/etc/foo/bar": false,
"/etc/bar/foo": true,
"/etc/foo/bar/foo": true,
},
},
{
name: "double star and single star",
onlyDirs: []string{"**/foo/*/bar"},
wants: map[string]bool{
"/foo/foo/bar/bar": false,
"/etc/foo/bar": true,
"/etc/bar/foo": true,
"/etc/foo/bar/bar": false,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for dir, want := range tt.wants {
dir = filepath.ToSlash(filepath.Clean(dir))
got := utils.OnlyPath(dir, utils.CleanSkipPaths(tt.onlyDirs))
assert.Equal(t, want, got, "onlyDirs: %s, dir: %s", tt.onlyDirs, dir)
}
})
}
}
11 changes: 11 additions & 0 deletions pkg/flag/scan_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ var (
ConfigName: "scan.offline",
Usage: "do not issue API requests to identify dependencies",
}
OnlyDirsFlag = Flag[[]string]{
Name: "only-dirs",
ConfigName: "scan.only-dirs",
Default: []string{},
Usage: "specify the directories where the traversal is allowed",
}
ScannersFlag = Flag[[]string]{
Name: "scanners",
ConfigName: "scan.scanners",
Expand Down Expand Up @@ -123,6 +129,7 @@ var (
type ScanFlagGroup struct {
SkipDirs *Flag[[]string]
SkipFiles *Flag[[]string]
OnlyDirs *Flag[[]string]
OfflineScan *Flag[bool]
Scanners *Flag[[]string]
FilePatterns *Flag[[]string]
Expand All @@ -138,6 +145,7 @@ type ScanOptions struct {
Target string
SkipDirs []string
SkipFiles []string
OnlyDirs []string
OfflineScan bool
Scanners types.Scanners
FilePatterns []string
Expand All @@ -152,6 +160,7 @@ func NewScanFlagGroup() *ScanFlagGroup {
return &ScanFlagGroup{
SkipDirs: SkipDirsFlag.Clone(),
SkipFiles: SkipFilesFlag.Clone(),
OnlyDirs: OnlyDirsFlag.Clone(),
OfflineScan: OfflineScanFlag.Clone(),
Scanners: ScannersFlag.Clone(),
FilePatterns: FilePatternsFlag.Clone(),
Expand All @@ -172,6 +181,7 @@ func (f *ScanFlagGroup) Flags() []Flagger {
return []Flagger{
f.SkipDirs,
f.SkipFiles,
f.OnlyDirs,
f.OfflineScan,
f.Scanners,
f.FilePatterns,
Expand Down Expand Up @@ -216,6 +226,7 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
Target: target,
SkipDirs: f.SkipDirs.Value(),
SkipFiles: f.SkipFiles.Value(),
OnlyDirs: f.OnlyDirs.Value(),
OfflineScan: f.OfflineScan.Value(),
Scanners: xstrings.ToTSlice[types.Scanner](f.Scanners.Value()),
FilePatterns: f.FilePatterns.Value(),
Expand Down
3 changes: 3 additions & 0 deletions pkg/flag/scan_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
type fields struct {
skipDirs []string
skipFiles []string
onlyDirs []string
offlineScan bool
scanners string
distro string
Expand Down Expand Up @@ -134,6 +135,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
t.Cleanup(viper.Reset)
setSliceValue(flag.SkipDirsFlag.ConfigName, tt.fields.skipDirs)
setSliceValue(flag.SkipFilesFlag.ConfigName, tt.fields.skipFiles)
setSliceValue(flag.OnlyDirsFlag.ConfigName, tt.fields.onlyDirs)
setValue(flag.OfflineScanFlag.ConfigName, tt.fields.offlineScan)
setValue(flag.ScannersFlag.ConfigName, tt.fields.scanners)
setValue(flag.DistroFlag.ConfigName, tt.fields.distro)
Expand All @@ -142,6 +144,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
f := &flag.ScanFlagGroup{
SkipDirs: flag.SkipDirsFlag.Clone(),
SkipFiles: flag.SkipFilesFlag.Clone(),
OnlyDirs: flag.OnlyDirsFlag.Clone(),
OfflineScan: flag.OfflineScanFlag.Clone(),
Scanners: flag.ScannersFlag.Clone(),
DistroFlag: flag.DistroFlag.Clone(),
Expand Down

0 comments on commit 5b93a9a

Please sign in to comment.