In Trivy 0.74, how do .trivyignore and .trivyignore.yaml differ when you must waive one CVE for one package until a date?
answer
- scope of a bare ID
- an expiry on the same line
- the YAML file is experimental
- purls, paths, expired_at, statement
basics
~20 sA .trivyignore line waives an ID for every package, file and scanner, optionally until an exp: date. The experimental .trivyignore.yaml, loaded only through --ignorefile, scopes the waiver by purls or paths and adds expired_at and a statement.
solid answer
~40 s`.trivyignore` is one ID per line, loaded automatically from the working directory; `CVE-2023-3817 exp:2026-12-31` waives that CVE in **every** package and file the scan touches, and Trivy applies plain IDs to all scanners. For one package you need `.trivyignore.yaml`, which in 0.74 is experimental and only read when passed with `--ignorefile`: under `vulnerabilities:` an entry takes `id`, `purls` (vulnerabilities only), `paths` (glob patterns), `expired_at` and a `statement` that is recorded but not used for matching. When the expiry date passes, the entry is dropped and the finding is back, failing a gated build. A malformed `exp:` date makes Trivy skip that line, so the CVE stays reported. `--show-suppressed` lists what was waived and why.
code
yaml · 11 linesvulnerabilities:
- id: CVE-2023-3817
purls:
- "pkg:deb/debian/libssl1.1"
expired_at: 2026-12-31
statement: "No affected code path; revisit at the next base-image bump"
misconfigurations:
- id: AVD-DS-0002
paths:
- "tools/legacy/Dockerfile"
statement: "Build-only image, never deployed"go deeper
Recall that .trivyignore holds one ID per line, loads from the working directory, and accepts an exp: date after the ID.
Explain why a bare ID waives the finding everywhere, how .trivyignore.yaml narrows it with purls and paths, and that the YAML file needs --ignorefile in 0.74.
Show the failure modes you have met: a job in a subdirectory losing every waiver, an expiry date re-failing builds on schedule, and --show-suppressed as the audit trail.
Weigh list-based waivers against a Rego --ignore-policy across many repositories: scoping, expiry by default, and how much a broad rule can hide from future scans.
## Two file formats, one filter step **Trivy** waives findings by ID after the severity and status filters and before any Rego `--ignore-policy` or VEX statement. Two file formats feed that step. Trivy 0.74 loads `.trivyignore` from the **current working directory** automatically; any other file, including a YAML one, has to be named with `--ignorefile` (config key `ignorefile`). A file whose extension is `.yml` or `.yaml` is parsed as YAML, anything else as the plain format. ## `.trivyignore`: one ID per line - Each non-blank line that does not start with `#` holds an ID: a CVE or other vulnerability ID, a misconfiguration check ID such as `AVD-DS-0002`, a secret rule ID such as `aws-account-id`, or a license name. - An optional `exp:yyyy-mm-dd` field on the same line sets an expiry. - Trivy cannot tell what kind of finding a bare ID refers to, so it applies every plain ID to **all four** finding types, and to every package, file and target in the scan. - If the `exp:` date does not parse, Trivy logs a warning and **skips the whole line**: the finding stays reported. That breadth is the problem the question is about. `CVE-2023-3817` waived for one library also disappears from every other package and every image scanned with the same file, and nothing in the file records why. ## `.trivyignore.yaml`: scoped waivers The YAML format is marked **EXPERIMENTAL** in 0.74 and is not loaded automatically; the docs say it will be once it is stable. It has four sections (`vulnerabilities`, `misconfigurations`, `secrets`, `licenses`), and each entry takes these fields: | Field | Required | Effect | |---|---|---| | `id` | yes | the vulnerability, check, secret rule or license to waive | | `paths` | no | glob patterns; without it the entry applies to all files | | `purls` | no | package URLs; vulnerabilities only; without it all packages match | | `expired_at` | no | `yyyy-mm-dd`; without it the entry never expires | | `statement` | no | the reason; recorded and shown, never used for matching | A purl without a version, such as `pkg:apk/alpine/libcrypto3`, scopes the waiver to that package whatever its installed version. ## What expiry does When Trivy loads either file it drops every entry whose date has passed. The finding then reappears, and a scan run with a non-zero `--exit-code` fails from that day on. That is the intended behaviour: a waiver that lapses fails loudly instead of hiding a finding forever. Both failure modes of expiry point the same way: an expired entry and a malformed date each leave the finding reported. ## Finding the file in CI 1. The default `.trivyignore` is resolved against the working directory. If it is absent, Trivy carries on with only a debug message, so a job that runs from a subdirectory quietly loses every waiver. 2. A path set explicitly with `--ignorefile` (or the `ignorefile` key) that does not exist stops Trivy with an "ignore file not found" error. 3. The official GitHub Action's `trivyignores` input takes a comma-separated list of plain files, which it concatenates, or exactly one YAML file; mixing the two kinds, or naming a missing file, fails the step. ## Choosing between them - Use `.trivyignore` for a quick, repository-wide waiver of an ID that genuinely does not apply anywhere, always with an `exp:` date. - Use `.trivyignore.yaml` when the waiver belongs to one package, one lockfile or one Dockerfile, or when the reason must travel with it. - Never rely on the YAML file being picked up by default in 0.74: name it with `--ignorefile` or the `ignorefile` config key. ## Auditing and going beyond IDs - `--show-suppressed` (experimental) prints the suppressed findings with their status, statement and source file; the JSON report carries them under `ExperimentalModifiedFindings`. - `--ignore-policy` takes a Rego file whose package is `trivy` and whose `ignore` rule is evaluated once per finding, with the finding as `input` and a `Type` field of `vulnerability`, `misconfiguration`, `secret` or `license`. Use it when the waiver is a rule (every finding with one CWE, one package across many CVEs) rather than a list. Trivy 0.74 evaluates these files with the older Rego v0 syntax, and a broad rule also hides findings that do not exist yet. Who may approve a waiver, and how waivers are reviewed, belongs to suppression governance; the file formats and how Trivy reads them are what the operator has to know.
- A team's waivers stopped working after the Trivy step moved into a monorepo job that runs from `services/api`. Why?The default `.trivyignore` is resolved against the working directory, and a missing default file is not an error; Trivy logs it at debug level and scans with no waivers. Every previously ignored finding comes back. Passing the file explicitly with `--ignorefile ../../.trivyignore` fixes it and also fails loudly if the path is ever wrong.
- When is a Rego `--ignore-policy` a better tool than an ignore file?When the waiver is a rule rather than a list of IDs, such as every finding with one CWE, or one package across all its CVEs. The Rego file uses package `trivy` and an `ignore` rule evaluated per finding. It is experimental, harder to review, and it also suppresses future findings that match, so it needs the same scrutiny as the ignore file.
- How do you see what an ignore file actually suppressed in a run?Add `--show-suppressed`, which is experimental in Trivy 0.74. The table output gains a suppressed section listing each finding with its status, the statement and the file that waived it; JSON output carries the same data under `ExperimentalModifiedFindings`. Suppressed findings never count toward `--exit-code`.
A .trivyignore line is a master key that opens every door carrying that number, at most with a date on the tag; a .trivyignore.yaml entry is a visitor badge that names the room, the reason and the day it stops working.
saying these in an interview costs you the question
- A CVE ID in .trivyignore only waives it for the package where it was first reported.
- Trivy picks up .trivyignore.yaml automatically, just like .trivyignore.
- The statement field in .trivyignore.yaml decides whether a finding matches.
- An exp: date Trivy cannot parse is dropped and the ID stays ignored.
- Once a waiver expires, Trivy keeps hiding the finding until someone deletes the line.