Skip to content

Commit 1d3dca5

Browse files
committed
Merge branch 'main' of github.com:DmitriyLewen/trivy into fix/secret/skip-config-file-non-root-path
# Conflicts: # pkg/fanal/analyzer/secret/secret.go
2 parents 4cde47f + e4325b1 commit 1d3dca5

10 files changed

Lines changed: 346 additions & 60 deletions

File tree

docs/guide/scanner/secret.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,45 @@ disable-allow-rules:
231231
- markdown
232232
```
233233

234+
### Skip Patterns
235+
236+
By default, Trivy skips the following paths during secret scanning (expressed as [doublestar](https://github.com/bmatcuk/doublestar) glob patterns):
237+
238+
```
239+
**/.git/** **/node_modules/**
240+
**/go.mod **/go.sum **/package-lock.json
241+
**/yarn.lock **/pnpm-lock.yaml **/Pipfile.lock **/Gemfile.lock
242+
**/*.jpg **/*.png **/*.gif **/*.doc **/*.pdf **/*.bin
243+
**/*.svg **/*.socket **/*.deb **/*.rpm
244+
**/*.zip **/*.gz **/*.gzip **/*.tar
245+
```
246+
You can see a full list of default skip patterns [here][default-secret-patterns].
247+
248+
You can override this list with `skip-patterns` in the configuration file.
249+
250+
!!! warning
251+
When `skip-patterns` is specified, it **replaces** the default list entirely — defaults are not merged.
252+
To keep the defaults and add new patterns, include them explicitly.
253+
254+
``` yaml
255+
skip-patterns:
256+
- "**/vendor/**"
257+
- "**/testdata/**"
258+
- "**/custom.lock"
259+
- "**/*.xyz"
260+
```
261+
262+
To disable all skipping, set it to an empty list:
263+
264+
``` yaml
265+
skip-patterns: []
266+
```
267+
234268
## Recommendation
235-
We would recommend specifying `--skip-dirs` for faster secret scanning.
269+
We would recommend specifying `--skip-dirs` or `--skip-files` for faster secret scanning. Also there is a way to use [skip-patterns](#skip-patterns) in the secret config to speed up your scanning.
236270
In container image scanning, Trivy walks the file tree rooted at `/` and scans all the files other than [built-in allowed paths][builtin-allow].
237271
It will take a while if your image contains a lot of files even though Trivy tries to avoid scanning layers from a base image.
238-
If you want to make scanning faster, `--skip-dirs` and `--skip-files` helps so that Trivy will skip scanning those files and directories.
272+
Adding glob patterns such as `**/vendor/**` helps so that Trivy will skip those paths entirely.
239273
You can see more options [here](../configuration/others.md).
240274

241275
`allow-rules` is also helpful. See the [allow-rules](#allow-rules) section.
@@ -311,6 +345,7 @@ This feature is inspired by [gitleaks][gitleaks].
311345

312346
[builtin]: https://github.com/aquasecurity/trivy/blob/{{ git.tag }}/pkg/fanal/secret/builtin-rules.go
313347
[builtin-allow]: https://github.com/aquasecurity/trivy/blob/{{ git.tag }}/pkg/fanal/secret/builtin-allow-rules.go
348+
[default-secret-patterns]: https://github.com/aquasecurity/trivy/blob/{{ git.tag }}/pkg/fanal/secret/scanner.go
314349
[gitleaks]: https://github.com/gitleaks/gitleaks
315350

316351
[builtin]: https://github.com/aquasecurity/trivy/blob/main/pkg/fanal/secret/builtin-rules.go

pkg/fanal/analyzer/secret/secret.go

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"os"
88
"path/filepath"
99
"slices"
10-
"strings"
1110

1211
"github.com/samber/lo"
1312
"golang.org/x/xerrors"
@@ -24,41 +23,9 @@ var _ analyzer.Initializer = &SecretAnalyzer{}
2423

2524
const version = 1
2625

27-
var (
28-
skipFiles = []string{
29-
"go.mod",
30-
"go.sum",
31-
"package-lock.json",
32-
"yarn.lock",
33-
"pnpm-lock.yaml",
34-
"Pipfile.lock",
35-
"Gemfile.lock",
36-
}
37-
skipDirs = []string{
38-
".git",
39-
"node_modules",
40-
}
41-
skipExts = []string{
42-
".jpg",
43-
".png",
44-
".gif",
45-
".doc",
46-
".pdf",
47-
".bin",
48-
".svg",
49-
".socket",
50-
".deb",
51-
".rpm",
52-
".zip",
53-
".gz",
54-
".gzip",
55-
".tar",
56-
}
57-
58-
allowedBinaries = []string{
59-
".pyc",
60-
}
61-
)
26+
var allowedBinaries = []string{
27+
".pyc",
28+
}
6229

6330
func init() {
6431
// The scanner will be initialized later via InitScanner()
@@ -143,27 +110,10 @@ func (a *SecretAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput
143110
}
144111

145112
func (a *SecretAnalyzer) Required(filePath string, fi os.FileInfo) bool {
146-
// Skip small files
147113
if fi.Size() < 10 {
148114
return false
149115
}
150116

151-
dir, fileName := filepath.Split(filePath)
152-
dir = filepath.ToSlash(dir)
153-
dirs := strings.Split(dir, "/")
154-
155-
// Check if the directory should be skipped
156-
for _, skipDir := range skipDirs {
157-
if slices.Contains(dirs, skipDir) {
158-
return false
159-
}
160-
}
161-
162-
// Check if the file should be skipped
163-
if slices.Contains(skipFiles, fileName) {
164-
return false
165-
}
166-
167117
// Skip the secret-scanner config file itself.
168118
// filePath is scan-relative and slash-normalized by the walker; configPath comes from the
169119
// --secret-config flag verbatim and may contain native separators, so normalize both.
@@ -175,9 +125,7 @@ func (a *SecretAnalyzer) Required(filePath string, fi os.FileInfo) bool {
175125
}
176126
}
177127

178-
// Check if the file extension should be skipped
179-
ext := filepath.Ext(fileName)
180-
if slices.Contains(skipExts, ext) {
128+
if a.scanner.IsSkipped(filePath) {
181129
return false
182130
}
183131

pkg/fanal/analyzer/secret/secret_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,106 @@ func TestSecretRequire(t *testing.T) {
302302
})
303303
}
304304
}
305+
306+
func TestSecretRequireCustomSkips(t *testing.T) {
307+
// Custom config replaces the default skip-patterns entirely.
308+
// Verify that custom patterns are skipped and former defaults are no longer skipped.
309+
tests := []struct {
310+
name string
311+
filePath string
312+
want bool
313+
}{
314+
{
315+
name: "skip custom dir (vendor)",
316+
filePath: "testdata/vendor/secret.txt",
317+
want: false,
318+
},
319+
{
320+
name: "no longer skip default dir (node_modules)",
321+
filePath: "testdata/node_modules/secret.txt",
322+
want: true,
323+
},
324+
{
325+
name: "skip custom file (custom.lock)",
326+
filePath: "testdata/custom.lock",
327+
want: false,
328+
},
329+
{
330+
name: "no longer skip default file (package-lock.json)",
331+
filePath: "testdata/package-lock.json",
332+
want: true,
333+
},
334+
{
335+
name: "skip custom extension (.xyz)",
336+
filePath: "testdata/secret.xyz",
337+
want: false,
338+
},
339+
{
340+
name: "no longer skip default extension (.doc)",
341+
filePath: "testdata/secret.doc",
342+
want: true,
343+
},
344+
}
345+
346+
for _, tt := range tests {
347+
t.Run(tt.name, func(t *testing.T) {
348+
a := secret.SecretAnalyzer{}
349+
err := a.Init(analyzer.AnalyzerOptions{
350+
SecretScannerOption: analyzer.SecretScannerOption{
351+
ConfigPath: "testdata/custom-skip-config.yaml",
352+
},
353+
})
354+
require.NoError(t, err)
355+
356+
fi, err := os.Stat(tt.filePath)
357+
require.NoError(t, err)
358+
359+
got := a.Required(tt.filePath, fi)
360+
assert.Equal(t, tt.want, got)
361+
})
362+
}
363+
}
364+
365+
func TestSecretRequireEmptySkips(t *testing.T) {
366+
// When skip-patterns is explicitly set to empty, nothing should be skipped —
367+
// even paths that match the default skip patterns.
368+
tests := []struct {
369+
name string
370+
filePath string
371+
want bool
372+
}{
373+
{
374+
name: "default skip dir (node_modules) is no longer skipped",
375+
filePath: "testdata/node_modules/secret.txt",
376+
want: true,
377+
},
378+
{
379+
name: "default skip file (package-lock.json) is no longer skipped",
380+
filePath: "testdata/package-lock.json",
381+
want: true,
382+
},
383+
{
384+
name: "default skip extension (.doc) is no longer skipped",
385+
filePath: "testdata/secret.doc",
386+
want: true,
387+
},
388+
}
389+
390+
for _, tt := range tests {
391+
t.Run(tt.name, func(t *testing.T) {
392+
a := secret.SecretAnalyzer{}
393+
err := a.Init(analyzer.AnalyzerOptions{
394+
SecretScannerOption: analyzer.SecretScannerOption{
395+
ConfigPath: "testdata/empty-skip-config.yaml",
396+
},
397+
})
398+
require.NoError(t, err)
399+
400+
fi, err := os.Stat(tt.filePath)
401+
require.NoError(t, err)
402+
403+
got := a.Required(tt.filePath, fi)
404+
assert.Equal(t, tt.want, got)
405+
})
406+
}
407+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
disable-allow-rules:
2+
- tests
3+
skip-patterns:
4+
- "**/vendor/**"
5+
- "**/custom.lock"
6+
- "**/*.xyz"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
--- ignore block start ---
2+
generic secret line secret="somevalue"
3+
--- ignore block stop ---
4+
secret="othervalue"
5+
credentials: { user: "username" password: "123456789" }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
disable-allow-rules:
2+
- tests
3+
skip-patterns: []
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
--- ignore block start ---
2+
generic secret line secret="somevalue"
3+
--- ignore block stop ---
4+
secret="othervalue"
5+
credentials: { user: "username" password: "123456789" }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
--- ignore block start ---
2+
generic secret line secret="somevalue"
3+
--- ignore block stop ---
4+
secret="othervalue"
5+
credentials: { user: "username" password: "123456789" }

0 commit comments

Comments
 (0)