DEV Community

Cover image for One CVE, four ignore files: unifying Trivy, Grype, Snyk and osv-scanner
opscanopy
opscanopy

Posted on

One CVE, four ignore files: unifying Trivy, Grype, Snyk and osv-scanner

You triaged the CVE. A scanner flagged CVE-2023-45853 in zlib, you read the advisory, confirmed the vulnerable code path isn’t reachable from your image, and made the call: suppress it, with a reason and a review date. One decision.

Now encode that one decision four times.

If your pipeline runs more than one scanner — and plenty do, because Trivy, Grype, Snyk and osv-scanner each catch things the others miss — every accepted-risk suppression has to be written into every tool’s ignore file. Same CVE, same rationale, four different files with four different schemas, four different capabilities. Get one wrong and that scanner keeps failing the build (or worse, silently stops failing on something you never meant to ignore).

The same suppression, four ways

Here’s the simplest version of that decision — “ignore CVE-2023-45853” — in each format.

.trivyignore is a flat newline-delimited list. One ID per line, # for comments:

# zlib MiniZip — not reachable from our build, re-review 2026-09-01
CVE-2023-45853
Enter fullscreen mode Exit fullscreen mode

.grype.yaml uses a structured ignore array of match rules. You can scope by vulnerability ID and, optionally, by package:

ignore:
  - vulnerability: CVE-2023-45853
    # zlib MiniZip — not reachable, re-review 2026-09-01
    package:
      name: zlib
      version: 1.2.13
Enter fullscreen mode Exit fullscreen mode

.snyk is Snyk’s policy file: a YAML map keyed by issue ID, where each entry is a list of path-scoped objects carrying reason and expires as first-class fields:

version: v1.25.0
ignore:
  CVE-2023-45853:
    - "*":
        reason: zlib MiniZip not reachable from our build
        expires: 2026-09-01T00:00:00.000Z
Enter fullscreen mode Exit fullscreen mode

osv-scanner.toml uses a TOML [[IgnoredVulns]] array of tables, with id, reason and an RFC 3339 ignoreUntil:

[[IgnoredVulns]]
id = "CVE-2023-45853"
ignoreUntil = 2026-09-01T00:00:00Z
reason = "zlib MiniZip not reachable from our build"
Enter fullscreen mode Exit fullscreen mode

Four files. The CVE ID is the only thing that survives verbatim across all of them.

What maps cleanly

A handful of concepts are genuinely portable, and a converter can move them without loss:

  • The identifier. A CVE-… ID is a CVE-… ID everywhere. The one wrinkle: osv-scanner is happiest with OSV IDs (GHSA-…, PYSEC-…) but accepts CVEs and resolves aliases, while the others are CVE-first. Most of the time the ID is a clean copy.
  • A reason string. Grype, Snyk and osv-scanner all have somewhere to put “why.” Trivy historically did not — in a bare .trivyignore the only home for a rationale is a # comment on the line above, which no tool parses but every reviewer reads.
  • Expiry, in spirit. Snyk’s expires and osv-scanner’s ignoreUntil are both real, enforced timestamps: once the date passes, the suppression lapses and the finding comes back. That’s the same idea, and it converts directly between the two.

What is lossy

The interesting part is everything that doesn’t round-trip. A converter has to be honest about these instead of silently dropping them.

Expiry is asymmetric. Snyk and osv-scanner enforce a date. Plain .trivyignore has no expiry field at all — a suppression there is forever until someone deletes the line. (Trivy’s newer YAML ignore file supports a statement and richer matching, but the classic .trivyignore most repos still use does not.) Convert a Snyk ignore with an expiry into .trivyignore and that expiry can only survive as a comment — a note to a human, not a rule the scanner enforces. That is a real loss of safety, and it should be flagged, not hidden.

Package scope varies. Grype lets you pin package.name and package.version, so the suppression only applies to that exact dependency. .trivyignore (classic) keys purely on the vulnerability ID — it ignores that CVE everywhere it appears in the scan, which is broader than you may have intended. Narrowing a Grype rule down to .trivyignore widens the blast radius; you can’t express “only this package” in a flat ID list.

Path scope is mostly a Snyk idea. Snyk’s ignore entries are lists keyed by path — the "*" above means “everywhere,” but Snyk can also scope a suppression to a specific dependency path so it applies in one place and not another. None of Trivy, Grype or osv-scanner model path-level scope this way, so a path-scoped Snyk ignore flattens to “this CVE, project-wide” in the others. The intent (“only on this transitive path”) is gone.

Reason has nowhere native to land in classic Trivy. As above — converting into .trivyignore demotes a first-class reason to a comment. Converting out of it, there’s usually no machine-readable reason to recover at all, only whatever a human left in a # line.

The pattern across all of these: converting toward a more expressive format is safe, and converting toward a flatter one quietly broadens scope and drops metadata. The only responsible way to do the flat conversion is to surface exactly what was lost so a human can decide whether the wider suppression is acceptable.

Do it once, emit four

Maintaining four hand-written ignore files in lockstep is exactly the kind of repetitive, error-prone bookkeeping that should be mechanical. Describe a suppression once — ID, reason, expiry, package and path scope — and let a tool emit the correct shape for each scanner, with explicit warnings wherever a target format can’t hold a field.

That’s what the CVE-Ignore Converter does: paste any one of .trivyignore, .grype.yaml, .snyk or osv-scanner.toml, get the other three back, and see precisely which parts were lossy in the round-trip. It runs entirely in your browser — your suppression policy never leaves the page.

Try the CVE-Ignore Converter →


Originally published on OpsCanopy. Try it free, in your browser: CVE-Ignore Converter.

Top comments (0)