Skip to content

Commit 8bf6caf

Browse files
authored
feat(fs): optimize scanning performance by direct file access for known paths (#8525)
1 parent 8112cdf commit 8bf6caf

File tree

26 files changed

+334
-28
lines changed

26 files changed

+334
-28
lines changed

docs/docs/target/rootfs.md

+13
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,16 @@ $ trivy rootfs /path/to/rootfs
1313
Rootfs scanning works differently from the Filesystem scanning.
1414
You should use `trivy fs` to scan your local projects in CI/CD.
1515
See [here](../scanner/vulnerability.md) for the differences.
16+
17+
## Performance Optimization
18+
19+
By default, Trivy traverses all files from the specified root directory to find target files for scanning.
20+
However, when you only need to scan specific files with absolute paths, you can avoid this traversal, which makes scanning faster.
21+
For example, when scanning only OS packages, no full traversal is performed:
22+
23+
```bash
24+
$ trivy rootfs --pkg-types os --scanners vuln /
25+
```
26+
27+
When scanning language-specific packages or secrets, traversal is necessary because the location of these files is unknown.
28+
If you want to exclude specific directories from scanning for better performance, you can use the [--skip-dirs](../configuration/skipping.md) option.

pkg/fanal/analyzer/analyzer.go

+32
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ type CustomGroup interface {
118118
Group() Group
119119
}
120120

121+
// StaticPathAnalyzer is an interface for analyzers that can specify static file paths
122+
// instead of traversing the entire filesystem.
123+
type StaticPathAnalyzer interface {
124+
// StaticPaths returns a list of static file paths to analyze
125+
StaticPaths() []string
126+
}
127+
121128
type Opener func() (xio.ReadSeekCloserAt, error)
122129

123130
type AnalyzerGroup struct {
@@ -527,3 +534,28 @@ func (ag AnalyzerGroup) filePatternMatch(analyzerType Type, filePath string) boo
527534
}
528535
return false
529536
}
537+
538+
// StaticPaths collects static paths from all enabled analyzers
539+
// It returns the collected paths and a boolean indicating if all enabled analyzers implement StaticPathAnalyzer
540+
func (ag AnalyzerGroup) StaticPaths(disabled []Type) ([]string, bool) {
541+
var paths []string
542+
543+
for _, a := range ag.analyzers {
544+
// Skip disabled analyzers
545+
if slices.Contains(disabled, a.Type()) {
546+
continue
547+
}
548+
549+
// If any analyzer doesn't implement StaticPathAnalyzer, return false
550+
staticPathAnalyzer, ok := a.(StaticPathAnalyzer)
551+
if !ok {
552+
return nil, false
553+
}
554+
555+
// Collect paths from StaticPathAnalyzer
556+
paths = append(paths, staticPathAnalyzer.StaticPaths()...)
557+
}
558+
559+
// Remove duplicates
560+
return lo.Uniq(paths), true
561+
}

pkg/fanal/analyzer/buildinfo/content_manifest.go

+4
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,7 @@ func (a contentManifestAnalyzer) Type() analyzer.Type {
6363
func (a contentManifestAnalyzer) Version() int {
6464
return contentManifestAnalyzerVersion
6565
}
66+
67+
func (a contentManifestAnalyzer) StaticPaths() []string {
68+
return contentSetsDirs.Items()
69+
}

pkg/fanal/analyzer/buildinfo/dockerfile.go

+4
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,7 @@ func setKVValue(kvpo instructions.KeyValuePairOptional, values map[string]string
157157
}
158158
return kvpo
159159
}
160+
161+
func (a dockerfileAnalyzer) StaticPaths() []string {
162+
return []string{"root/buildinfo"}
163+
}

pkg/fanal/analyzer/const.go

+3
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ var (
178178
TypeGemSpec,
179179
TypeCargo,
180180
TypeComposer,
181+
TypeComposerVendor,
181182
TypeJar,
182183
TypePom,
183184
TypeGradleLock,
@@ -192,6 +193,7 @@ var (
192193
TypeCondaPkg,
193194
TypeCondaEnv,
194195
TypePythonPkg,
196+
TypePythonPkgEgg,
195197
TypePip,
196198
TypePipenv,
197199
TypePoetry,
@@ -205,6 +207,7 @@ var (
205207
TypePubSpecLock,
206208
TypeMixLock,
207209
TypeJulia,
210+
TypeSBOM,
208211
}
209212

210213
// TypeLockfiles has all lock file analyzers

pkg/fanal/analyzer/os/alpine/alpine.go

+5
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,8 @@ func (a alpineOSAnalyzer) Type() analyzer.Type {
4848
func (a alpineOSAnalyzer) Version() int {
4949
return version
5050
}
51+
52+
// StaticPaths returns the static paths of the alpine analyzer
53+
func (a alpineOSAnalyzer) StaticPaths() []string {
54+
return requiredFiles
55+
}

pkg/fanal/analyzer/os/amazonlinux/amazonlinux.go

+5
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,8 @@ func (a amazonlinuxOSAnalyzer) Type() analyzer.Type {
7373
func (a amazonlinuxOSAnalyzer) Version() int {
7474
return version
7575
}
76+
77+
// StaticPaths returns the static paths of the amazonlinux analyzer
78+
func (a amazonlinuxOSAnalyzer) StaticPaths() []string {
79+
return requiredFiles
80+
}

pkg/fanal/analyzer/os/debian/debian.go

+5
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,8 @@ func (a debianOSAnalyzer) Type() analyzer.Type {
4848
func (a debianOSAnalyzer) Version() int {
4949
return version
5050
}
51+
52+
// StaticPaths returns the static paths of the debian analyzer
53+
func (a debianOSAnalyzer) StaticPaths() []string {
54+
return requiredFiles
55+
}

pkg/fanal/analyzer/os/redhatbase/alma.go

+5
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,8 @@ func (a almaOSAnalyzer) Type() analyzer.Type {
6060
func (a almaOSAnalyzer) Version() int {
6161
return almaAnalyzerVersion
6262
}
63+
64+
// StaticPaths returns the static paths of the alma analyzer
65+
func (a almaOSAnalyzer) StaticPaths() []string {
66+
return a.requiredFiles()
67+
}

pkg/fanal/analyzer/os/redhatbase/centos.go

+5
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,8 @@ func (a centOSAnalyzer) Type() analyzer.Type {
6060
func (a centOSAnalyzer) Version() int {
6161
return centosAnalyzerVersion
6262
}
63+
64+
// StaticPaths returns the static paths of the centos analyzer
65+
func (a centOSAnalyzer) StaticPaths() []string {
66+
return a.requiredFiles()
67+
}

pkg/fanal/analyzer/os/redhatbase/fedora.go

+5
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ func (a fedoraOSAnalyzer) Type() analyzer.Type {
6262
func (a fedoraOSAnalyzer) Version() int {
6363
return fedoraAnalyzerVersion
6464
}
65+
66+
// StaticPaths returns the static paths of the fedora analyzer
67+
func (a fedoraOSAnalyzer) StaticPaths() []string {
68+
return a.requiredFiles()
69+
}

pkg/fanal/analyzer/os/redhatbase/oracle.go

+5
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,8 @@ func (a oracleOSAnalyzer) Type() analyzer.Type {
5656
func (a oracleOSAnalyzer) Version() int {
5757
return oracleAnalyzerVersion
5858
}
59+
60+
// StaticPaths returns the static paths of the oracle analyzer
61+
func (a oracleOSAnalyzer) StaticPaths() []string {
62+
return a.requiredFiles()
63+
}

pkg/fanal/analyzer/os/redhatbase/redhatbase.go

+5
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,8 @@ func (a redhatOSAnalyzer) Type() analyzer.Type {
9797
func (a redhatOSAnalyzer) Version() int {
9898
return redhatAnalyzerVersion
9999
}
100+
101+
// StaticPaths returns the static paths of the redhatbase analyzer
102+
func (a redhatOSAnalyzer) StaticPaths() []string {
103+
return a.requiredFiles()
104+
}

pkg/fanal/analyzer/os/redhatbase/rocky.go

+5
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,8 @@ func (a rockyOSAnalyzer) Type() analyzer.Type {
6060
func (a rockyOSAnalyzer) Version() int {
6161
return rockyAnalyzerVersion
6262
}
63+
64+
// StaticPaths returns the static paths of the rocky analyzer
65+
func (a rockyOSAnalyzer) StaticPaths() []string {
66+
return a.requiredFiles()
67+
}

pkg/fanal/analyzer/os/release/release.go

+5
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,8 @@ func (a osReleaseAnalyzer) Type() analyzer.Type {
9696
func (a osReleaseAnalyzer) Version() int {
9797
return version
9898
}
99+
100+
// StaticPaths returns the static paths of the os-release analyzer
101+
func (a osReleaseAnalyzer) StaticPaths() []string {
102+
return requiredFiles
103+
}

pkg/fanal/analyzer/os/ubuntu/esm.go

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ func (a ubuntuESMAnalyzer) Version() int {
5959
return ESMAnalyzerVersion
6060
}
6161

62+
// StaticPaths returns the static paths of the ubuntu ESM analyzer
63+
func (a ubuntuESMAnalyzer) StaticPaths() []string {
64+
return ESMRequiredFiles
65+
}
66+
6267
// structs to parse ESM status
6368
type status struct {
6469
Services []service `json:"services"`

pkg/fanal/analyzer/os/ubuntu/ubuntu.go

+5
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ func (a ubuntuOSAnalyzer) Type() analyzer.Type {
6262
func (a ubuntuOSAnalyzer) Version() int {
6363
return version
6464
}
65+
66+
// StaticPaths returns the static paths of the ubuntu analyzer
67+
func (a ubuntuOSAnalyzer) StaticPaths() []string {
68+
return requiredFiles
69+
}

pkg/fanal/analyzer/pkg/apk/apk.go

+5
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ func (a alpinePkgAnalyzer) Version() int {
209209
return analyzerVersion
210210
}
211211

212+
// StaticPaths returns a list of static file paths to analyze
213+
func (a alpinePkgAnalyzer) StaticPaths() []string {
214+
return requiredFiles
215+
}
216+
212217
// decodeChecksumLine decodes checksum line
213218
func (a alpinePkgAnalyzer) decodeChecksumLine(ctx context.Context, line string) digest.Digest {
214219
if len(line) < 2 {

pkg/fanal/analyzer/pkg/dpkg/copyright.go

+4
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,7 @@ func normalizeLicense(s string) string {
149149

150150
return strings.TrimSpace(s)
151151
}
152+
153+
func (a *dpkgLicenseAnalyzer) StaticPaths() []string {
154+
return []string{"usr/share/doc/"}
155+
}

pkg/fanal/analyzer/pkg/dpkg/dpkg.go

+10
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,13 @@ func (a dpkgAnalyzer) Type() analyzer.Type {
372372
func (a dpkgAnalyzer) Version() int {
373373
return analyzerVersion
374374
}
375+
376+
// StaticPaths returns a list of static file paths to analyze
377+
func (a dpkgAnalyzer) StaticPaths() []string {
378+
return []string{
379+
statusFile,
380+
availableFile,
381+
statusDir,
382+
infoDir,
383+
}
384+
}

pkg/fanal/analyzer/pkg/rpm/rpm.go

+5
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ func (a rpmPkgAnalyzer) Version() int {
208208
return version
209209
}
210210

211+
// StaticPaths returns a list of static file paths to analyze
212+
func (a rpmPkgAnalyzer) StaticPaths() []string {
213+
return requiredFiles
214+
}
215+
211216
// splitFileName returns a name, version, release, epoch, arch:
212217
//
213218
// e.g.

pkg/fanal/analyzer/pkg/rpm/rpmqa.go

+4
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,7 @@ func (a rpmqaPkgAnalyzer) Type() analyzer.Type {
9292
func (a rpmqaPkgAnalyzer) Version() int {
9393
return versionRpmqa
9494
}
95+
96+
func (a rpmqaPkgAnalyzer) StaticPaths() []string {
97+
return requiredRpmqaFiles
98+
}

pkg/fanal/analyzer/repo/apk/apk.go

+4
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,7 @@ func (a apkRepoAnalyzer) Type() analyzer.Type {
9696
func (a apkRepoAnalyzer) Version() int {
9797
return version
9898
}
99+
100+
func (a apkRepoAnalyzer) StaticPaths() []string {
101+
return requiredFiles
102+
}

0 commit comments

Comments
 (0)