Repository navigation
Conversation
A suppression rule with several cve, cwe or vulnerabilityName entries counts as used as soon as one of its entries suppresses a vulnerability. The UnusedSuppressionRuleAnalyzer therefore does not report the entries that suppress nothing. This test fails until that is changed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The UnusedSuppressionRuleAnalyzer only reported rules that matched nothing at all. A rule with several cve, cwe or vulnerabilityName entries counted as used as soon as one entry suppressed a vulnerability, so entries that became obsolete (e.g. because the dependency was updated) stayed unnoticed. SuppressionRule now remembers which of these entries suppressed a vulnerability and the analyzer reports the others, with the same severity and counting as for completely unused rules. The set of used entries is concurrent, as VulnerabilitySuppressionAnalyzer processes dependencies in parallel. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Value-only tracking conflates distinct suppression entries, incorrectly marking unmatched entries as used and undermining unused-entry reporting and fail-on-unused enforcement.
1 open issue
What changed in this PR
This PR extends suppression usage tracking so DependencyCheck can report obsolete entries within otherwise-used rules, rather than only completely unused rules.
Changes:
- Tracks usage of individual CVE, CWE, and vulnerability-name entries with concurrent storage.
- Reports partially unused rules and counts them toward fail-on-unused enforcement.
- Adds regression tests and updates test fixtures to exercise actual suppression.
| File | Description |
|---|---|
core/src/test/java/org/owasp/dependencycheck/xml/suppression/SuppressionRuleTest.java |
Tests unused-entry reporting after suppression. |
core/src/test/java/org/owasp/dependencycheck/analyzer/UnusedSuppressionRuleAnalyzerTest.java |
Tests partially unused rule counting and updates fixtures. |
core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java |
Records matched entries and exposes unused entries. |
core/src/main/java/org/owasp/dependencycheck/analyzer/UnusedSuppressionRuleAnalyzer.java |
Logs and counts partially unused non-base rules. |
Review effort: Balanced
Fails until `cpe` entries are tracked like `cve`, `cwe` and `vulnerabilityName`. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Track `cpe` entries like `cve`, `cwe` and `vulnerabilityName`. The `cpe` entry of the analyzer test rule never matched anything, which was not visible before; it is not needed for that test and removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fails while matches are remembered by value: a duplicate entry and an entry that has the same value as a matching one, but does not match itself, are wrongly reported as used. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Remember matches of `cpe`, `cve`, `cwe` and `vulnerabilityName` entries by their kind and position in the rule instead of by a string built from the value. Entries with the same value, such as duplicates, are no longer mixed up, so redundant ones are reported as unused. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
You're right. Tracked now in cef575a, with the test that fails without it in e054201. |
|
Cpe does act on identifiers, but it's important to understand a key distinction - the source of the CPE for a given artifact is part of the vulnerability information, it cannot be directly derived from any software dependency. It is essentially "affected versions" metadata alongside a CVE from NVD. ODC guesses a bunch of different CPE identifiers based on evidence it gathers and then tries to find any vulns that match. It removes any guessed CPEs that are not known from NVD reported data, so the CPE content shifts over time even if the dependency metadata, file etc stays the same. The biggest source of false positives and thus suppressions are bad CPE matches. This is why in the suppression schema CPE is part of the second "vulnerability info" 1..many chunk (alongside cwe, CVE, vulnerabilityName) rather than the first optional 0..many chunk (packageUrl, file, sha) which is applied to the dependency directly ("scope", essentially) I think I agree with not tracking cvssBelow as these are more like "noise/risk filters" rather than "false positive filters", as long as they are not reported as "unused" in the current logic. |
Thanks for the explanation, that makes sense:
|
|
This looks better, thanks! I'll take a deeper look later and give it a try myself on a real repo, to see how it does. I have a mild concern that if two possible |
… unused The rules are applied one after the other and the first one removes the suppressed vulnerability, so a second rule never sees it. This is existing behavior, the per-entry tracking of a single rule works the same way. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Thanks for taking a look, and no rush. On your concern: I believe that situation already exists today. On opting out: there is nothing per entry. Without |

Description of Change
The
UnusedSuppressionRuleAnalyzeronly reports a suppression rule if it did not match anything at all (SuppressionRule.isMatched()is a single flag per rule). A rule with several<cve>,<cwe>,<vulnerabilityName>or<cpe>entries counts as used as soon as one of its entries matches. Entries that became obsolete in the meantime, e.g. because the dependency was updated and the vulnerability is gone, stay unnoticed and pile up in the suppression file.This change tracks usage per entry:
SuppressionRuleremembers whichcve,cwe,vulnerabilityNameandcpeentries matched at least once. New methodgetUnusedEntries()returns the others.UnusedSuppressionRuleAnalyzerreports a rule that was used but still has unused entries (Suppression Rule had unused entries [...]: <rule>), with the same severity and counting as for completely unused rules. Rules that did not match at all are reported as before.Notes:
analyzer.suppression.unused.failenabled, rules that are only partly unused now cause a failure too.RuleEntry), not by value, so duplicate entries and entries with the same value but different matching (e.g. case-sensitive or not) are told apart.VulnerabilitySuppressionAnalyzersupports parallel processing and so processes one rule from several threads at once. The set is only read in a later analysis phase, after all analysis tasks have completed.cpe,cwe,cve,vulnerabilityName) is tracked per entry. The scope conditions (packageUrl,filePath,sha1,gav) select the dependency, andcvssBelowis a noise filter rather than a false-positive filter. None of them is reported as unused.Related issues
None.
Have test cases been added to cover the new functionality?
yes
The tests were added in separate commits before the change that makes them pass.
UnusedSuppressionRuleAnalyzerTest.testCheckUnusedEntriesOfMatchedRule: a rule that suppressed a vulnerability, but has a second obsolete entry, is counted as unused.SuppressionRuleTest.testGetUnusedEntries: lists exactly the unusedcveandvulnerabilityNameentries.SuppressionRuleTest.testGetUnusedEntriesWithCpe: lists an unusedcpeentry.SuppressionRuleTest.testGetUnusedEntriesWithDuplicates: the second of two identical entries is reported.SuppressionRuleTest.testGetUnusedEntriesWithSameValueButDifferentMatching: an entry with the same value as a matching one, but which does not match itself, is reported.UnusedSuppressionRuleAnalyzerTest.getSuppressionRulesetmatchedby hand without the entry suppressing anything. It now lets the entry suppress a vulnerability, as in a real run, and no longer adds acpeentry that never matched anything.