Skip to content

Commit f7ac016

Browse files
G-Rathanother-rex
andauthored
feat: warn when a config has ignored vulns that were not found during scanning (#2216)
Resolves #2206 --------- Co-authored-by: Rex P <[email protected]>
1 parent bc7386f commit f7ac016

File tree

6 files changed

+568
-16
lines changed

6 files changed

+568
-16
lines changed

cmd/osv-scanner/scan/source/__snapshots__/command_test.snap

Lines changed: 464 additions & 0 deletions
Large diffs are not rendered by default.

cmd/osv-scanner/scan/source/command_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,34 @@ func TestCommand(t *testing.T) {
327327
}
328328
}
329329

330+
func TestCommand_Config_UnusedIgnores(t *testing.T) {
331+
t.Parallel()
332+
333+
tests := []testcmd.Case{
334+
{
335+
Name: "unused_ignores_are_reported_with_specific_config_and_file",
336+
Args: []string{"", "source", "--config", "testdata/osv-scanner-partial-ignores-config.toml", "testdata/sbom-insecure/alpine.cdx.xml"},
337+
Exit: 1,
338+
},
339+
{
340+
Name: "unused_ignores_are_reported_with_specific_config_and_multiple_files",
341+
Args: []string{"", "source", "--config", "testdata/osv-scanner-partial-ignores-config.toml", "testdata/sbom-insecure/alpine.cdx.xml", "testdata/sbom-insecure/postgres-stretch.cdx.xml"},
342+
Exit: 1,
343+
},
344+
{
345+
Name: "unused_ignores_are_reported_with_specific_config_and_file",
346+
Args: []string{"", "source", "--config", "testdata/osv-scanner-partial-ignores-config.toml", "testdata/sbom-insecure"},
347+
Exit: 1,
348+
},
349+
}
350+
for _, tt := range tests {
351+
t.Run(tt.Name, func(t *testing.T) {
352+
t.Parallel()
353+
testcmd.RunAndMatchSnapshots(t, tt)
354+
})
355+
}
356+
}
357+
330358
func TestCommand_JavareachArchive(t *testing.T) {
331359
t.Parallel()
332360

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[[IgnoredVulns]]
2+
id = "CVE-2025-26519" # in alpine.cdx.xml
3+
4+
[[IgnoredVulns]]
5+
id = "CVE-2018-25032" # in alpine.cdx.xml
6+
7+
[[IgnoredVulns]]
8+
id = "GO-2022-0274" # in postgres-stretch.cdx.xml
9+
10+
[[IgnoredVulns]]
11+
id = "CVE-2019-5188"
12+
ignoreUntil = 2020-01-01
13+
14+
[[IgnoredVulns]]
15+
id = "CVE-2022-1304"
16+
ignoreUntil = 2100-01-01

internal/config/config.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,28 @@ type Config struct {
3535
LoadPath string `toml:"-"`
3636
}
3737

38+
func (c *Config) UnusedIgnoredVulns() []IgnoreEntry {
39+
unused := make([]IgnoreEntry, 0, len(c.IgnoredVulns))
40+
41+
for _, entry := range c.IgnoredVulns {
42+
if !entry.Used {
43+
unused = append(unused, entry)
44+
}
45+
}
46+
47+
return unused
48+
}
49+
3850
type IgnoreEntry struct {
3951
ID string `toml:"id"`
4052
IgnoreUntil time.Time `toml:"ignoreUntil"`
4153
Reason string `toml:"reason"`
54+
55+
Used bool `toml:"-"`
56+
}
57+
58+
func (ie *IgnoreEntry) MarkAsUsed() {
59+
ie.Used = true
4260
}
4361

4462
type PackageOverrideEntry struct {
@@ -185,6 +203,28 @@ func (c *Manager) Get(targetPath string) Config {
185203
return config
186204
}
187205

206+
func (c *Manager) GetUnusedIgnoreEntries() map[string][]IgnoreEntry {
207+
m := make(map[string][]IgnoreEntry)
208+
209+
for _, config := range c.ConfigMap {
210+
unusedEntries := config.UnusedIgnoredVulns()
211+
212+
if len(unusedEntries) > 0 {
213+
m[config.LoadPath] = unusedEntries
214+
}
215+
}
216+
217+
if c.OverrideConfig != nil {
218+
unusedEntries := c.OverrideConfig.UnusedIgnoredVulns()
219+
220+
if len(unusedEntries) > 0 {
221+
m[c.OverrideConfig.LoadPath] = unusedEntries
222+
}
223+
}
224+
225+
return m
226+
}
227+
188228
// Finds the containing folder of `target`, then appends osvScannerConfigName
189229
func normalizeConfigLoadPath(target string) (string, error) {
190230
stat, err := os.Stat(target)

pkg/osvscanner/filter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ func filterPackageVulns(pkgVulns models.PackageVulns, configToUse config.Config)
153153
cmdlogger.Infof("%s and %d aliases have been filtered out because: %s", ignoreLine.ID, len(group.Aliases)-1, reason)
154154
}
155155

156+
ignoreLine.MarkAsUsed()
157+
156158
break
157159
}
158160
}

pkg/osvscanner/osvscanner.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -259,22 +259,7 @@ func DoScan(actions ScannerActions) (models.VulnerabilityResults, error) {
259259
}
260260
}
261261

262-
vulnerabilityResults := buildVulnerabilityResults(actions, &scanResult)
263-
264-
if actions.ScanLicensesSummary {
265-
vulnerabilityResults.LicenseSummary = buildLicenseSummary(&scanResult)
266-
}
267-
268-
filtered := filterResults(&vulnerabilityResults, &scanResult.ConfigManager, actions.ShowAllPackages)
269-
if filtered > 0 {
270-
cmdlogger.Infof(
271-
"Filtered %d %s from output",
272-
filtered,
273-
output.Form(filtered, "vulnerability", "vulnerabilities"),
274-
)
275-
}
276-
277-
return vulnerabilityResults, determineReturnErr(vulnerabilityResults, actions.ShowAllVulns)
262+
return finalizeScanResult(scanResult, actions)
278263
}
279264

280265
func DoContainerScan(actions ScannerActions) (models.VulnerabilityResults, error) {
@@ -420,6 +405,10 @@ func DoContainerScan(actions ScannerActions) (models.VulnerabilityResults, error
420405

421406
scanResult.GenericFindings = scalibrSR.Inventory.GenericFindings
422407

408+
return finalizeScanResult(scanResult, actions)
409+
}
410+
411+
func finalizeScanResult(scanResult results.ScanResults, actions ScannerActions) (models.VulnerabilityResults, error) {
423412
vulnerabilityResults := buildVulnerabilityResults(actions, &scanResult)
424413

425414
if actions.ScanLicensesSummary {
@@ -435,6 +424,19 @@ func DoContainerScan(actions ScannerActions) (models.VulnerabilityResults, error
435424
)
436425
}
437426

427+
if unusedIgnoredEntries := scanResult.ConfigManager.GetUnusedIgnoreEntries(); len(unusedIgnoredEntries) != 0 {
428+
configFiles := slices.Collect(maps.Keys(unusedIgnoredEntries))
429+
slices.Sort(configFiles)
430+
431+
for _, configFile := range configFiles {
432+
cmdlogger.Warnf("%s has unused ignores:", configFile)
433+
434+
for _, iv := range unusedIgnoredEntries[configFile] {
435+
cmdlogger.Warnf(" - %s", iv.ID)
436+
}
437+
}
438+
}
439+
438440
return vulnerabilityResults, determineReturnErr(vulnerabilityResults, actions.ShowAllVulns)
439441
}
440442

0 commit comments

Comments
 (0)