How do you handle false positives from dependency-check, and what goes into a suppression file?
answer
- CPE guessing -> false positives
- <suppress> by sha1/GAV/cpe + <cve>
- suppressionFiles config
- report 'suppress' button generates XML
- justify + date + narrow scope
basics
~20 sYou write a suppression.xml file listing specific CVEs to ignore for specific dependencies, then point the plugin at it. It tells the scanner that a particular finding is a false positive (or accepted risk) so it stops failing the build.
solid answer
~40 sDependency-check infers each library's identity by guessing a CPE from JAR metadata, and that guessing produces false positives — a CVE for an unrelated product gets matched to your JAR. You suppress these in an XML suppression file referenced via `<suppressionFiles>`. Each `<suppress>` entry scopes itself to a dependency (by sha1, GAV regex, file path, or CPE) and lists the `<cve>` or `<vulnerabilityName>` to ignore. The discipline matters: every suppression should be justified (false positive vs. accepted risk), ideally dated, and reviewed, because a stale or overly broad suppression silently hides real vulnerabilities. The HTML report has a 'suppress' button that generates the exact XML snippet for a finding, which you copy into your committed suppression file rather than ignoring globally.
code
xml · 5 lines<suppress>
<notes>Accepted risk: no fix yet, mitigated by network controls. 2026-06-21.</notes>
<packageUrl regex="true">^pkg:maven/org\.example/legacy-lib@1\.2\..*$</packageUrl>
<cve>CVE-2024-99999</cve>
</suppress>go deeper
Know there is a suppression.xml that tells the scanner to ignore specific findings.
Know how to scope a <suppress> entry to a dependency and a CVE and wire it via suppressionFiles.
Enforce narrow scoping, justification notes, and the false-positive vs. accepted-risk distinction.
Set suppression governance: review cadence, ownership, and tooling so suppressions cannot rot into hidden risk across the org.
## Why false positives happen The plugin does not have a perfect map from JAR to product. It collects **evidence** (groupId, artifactId, manifest entries, embedded pom, package names) and guesses a **CPE** (the standardized product name). Guessing means it sometimes matches a CVE for a *different* product that shares a name — e.g. matching a Spring CVE to an unrelated library called 'spring-something'. These are **false positives**. ## The suppression file A suppression file is XML that tells the scanner: 'for this specific dependency, ignore this specific finding.' You reference it in config: ```xml <configuration> <suppressionFiles> <suppressionFile>dependency-check-suppressions.xml</suppressionFile> </suppressionFiles> </configuration> ``` The file itself: ```xml <?xml version="1.0" encoding="UTF-8"?> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <suppress> <notes>False positive: CPE mis-match, this is acme-utils not Acme CMS. Reviewed 2026-06-21 by KD.</notes> <packageUrl regex="true">^pkg:maven/com\.acme/acme-utils@.*$</packageUrl> <cve>CVE-2023-12345</cve> </suppress> </suppressions> ``` ## How to scope a suppression You can target by: - **sha1** of the exact JAR (narrowest, most precise), - **packageUrl / GAV** (with `regex="true"` for version ranges), - **filePath**, - **cpe** or **gav**, and then suppress specific `<cve>`, `<vulnerabilityName>`, or `<cpe>` entries. Prefer the narrowest scope so you do not accidentally silence real CVEs on the same artifact. ## Generating entries The HTML report shows a **suppress** link next to each finding that emits a ready-made `<suppress>` block. Copy it into your committed file and add a justification note. Never blanket-suppress whole artifacts unless you truly accept all their risk. ## Governance - **Justify every entry** in `<notes>` (false positive vs. accepted risk + who/when). - **Review periodically** — a suppression added for one CVE may hide a new real CVE if scoped too broadly. - **Commit it** to the repo so the gate is reproducible. False positive (mis-matched CVE) and accepted risk (real CVE you cannot fix yet) are different decisions; document which one each suppression is.
- What is the danger of suppressing by artifact name without scoping to a CVE?It silences every current and future vulnerability for that artifact, so a new genuine CVE would slip through undetected.
- Why distinguish a false positive from an accepted risk in the notes?A false positive is permanent (wrong match); an accepted risk is temporary and should be revisited and removed once a fix exists, so the notes drive future review.
saying these in an interview costs you the question
- Suppressing globally by GAV with no CVE scope, hiding future real findings.
- Adding suppressions with no justification, making review impossible.
- Treating every finding as a false positive instead of triaging real CVEs.