Ordered by how often each one turns out to be the answer.
A new advisory landed against your base image
CommonNothing in your change caused it. An advisory was published against a package in the base image, and the same commit that passed yesterday fails today. This is the single most common reason a scan starts failing, and it is why 'what did I change' is the wrong first question here.
Confirm
git log -1 --stat -- package-lock.json go.sum requirements.txt Dockerfile
No dependency or base-image change in the failing commit. If the manifest is untouched, the advisory moved rather than your code
Fix
- Rebuild against a current base image tag first — most OS-level findings clear without touching your code at all.
- Rebuild on a schedule rather than only when code changes, so this arrives on a Tuesday rather than during a release.
- If no fixed base exists yet, record the acceptance with an expiry date rather than an open-ended ignore.
docker pull <base>:<tag> && docker build --no-cache -t <image> .
Forces a fresh base layer. Without --no-cache the old layer is reused and nothing changes.
The vulnerable code is present but never called
CommonThe package is in the image and your application does not import it, or imports it and never calls the affected function. A scanner working from the package inventory cannot tell the difference. This is the largest category by volume and the reason severity alone is a poor priority signal.
Confirm
grep -rn "<package-name>" --include='*.ts' --include='*.js' --include='*.py' --include='*.go' src/ || echo 'not imported anywhere in src'
No import at all, which makes it a transitive dependency of something else rather than code you call. Where your ecosystem supports reachability analysis, prefer that over grep
Fix
- Record the reachability finding with the evidence, so the next person does not repeat the analysis.
- Prefer scanners and flags that do reachability analysis where your ecosystem supports it — it removes this category automatically rather than one ticket at a time.
- Do not mark it fixed. It is accepted, which is a different state and should expire.
It is in a build or test dependency, not in the shipped artifact
CommonA vulnerability in a test framework or a build tool is not in production unless your build environment is itself a target — which is a real concern, but a different one with a different owner. Scanning the repository rather than the final image conflates them.
Confirm
npm ls <package> --omit=dev # or: pip list --not-required / go mod why <module>
The package absent from the production dependency tree while present in the full one
Fix
- Scan the built artifact rather than the source tree, so what ships is what is judged.
- Use a multi-stage build so build-time tooling does not reach the final image.
- Treat build-environment vulnerabilities as a pipeline-security concern in its own right rather than ignoring them outright.
There is no fixed version yet
OccasionalThe advisory is real, the code is reachable, and the maintainer has not released a fix. Assigning this to somebody produces a ticket that cannot be closed, which is how teams learn that security tickets are noise.
Confirm
trivy image --ignore-unfixed <image> # or the equivalent flag for your scanner
The finding disappearing, which confirms no fixed version is published
Fix
- Accept it explicitly, with an owner, a stated mitigation if one exists, and a date to re-check — not an indefinite ignore.
- Consider whether a control elsewhere reduces the exposure: a WAF rule, an input constraint, removing the feature that reaches the code.
- Configure the gate to distinguish fixable from unfixable. Blocking on something nobody can fix is the fastest route to the gate being disabled.
It is real, reachable and fixable
OccasionalThe case the gate exists for. Reachable from your code, plausibly reachable from untrusted input, and a fixed version is available. These are the minority of findings and the entire justification for the other four categories of work.
Confirm
npm audit --omit=dev # or: trivy image --severity HIGH,CRITICAL --ignore-unfixed <image>
A fixed version, against a package your code demonstrably calls on a path reachable from input
Fix
- Upgrade, test, and ship — and ship it as its own change rather than bundled into a release, so a regression is attributable.
- Read what changed rather than only bumping the number; a major-version fix can carry behaviour you did not want.
- If the upgrade is blocked by an incompatibility, that is a scheduling problem to escalate, not a finding to ignore.
The match itself is wrong
RarePackage naming is inconsistent across ecosystems and advisory databases, so a scanner can match the wrong component, or a version range can be recorded incorrectly upstream. Genuine, and rarer than it feels when you are the one blocked.
Confirm
# Compare the advisory's affected range against the version you actually ship
Your version falling outside the stated range, or the matched package being a different project with a similar name
Fix
- Report it upstream to the scanner or the advisory source; it is wrong for everyone else too.
- Suppress it narrowly — that CVE, that package, with a comment explaining why — never by relaxing severity thresholds.
- Re-check periodically. Advisory data gets corrected.