A vulnerable package inside an image means a flaw exists in software you ship. It does not mean an attacker can use it. Ask four questions in order: is it present, is it loaded, is the vulnerable code reachable from your application, and is that path exposed to untrusted input. Most findings drop out by the third.
The four questions
| Step | Question | How you answer it |
|---|---|---|
| Present | Is the affected version actually in the image? | SBOM and package records |
| Loaded | Does the running program load that package? | Language manifest, linking, build output |
| Reachable | Can your code call the vulnerable function? | Reachability analysis |
| Exposed | Can untrusted input reach that call? | Architecture, network exposure |
Each step narrows the set. A package installed in the base image for a tool nobody runs fails at step two. A library that your application imports but never uses the affected function from fails at step three. A reachable function that only handles trusted internal data fails at step four, though that one needs the most care, because trust boundaries change.
Why scanners over-report
Image scanners match package names and versions against advisories. That is the right first move, because it is fast and does not miss packages. But a version match says nothing about code paths. The result is a long list where most rows describe code that never runs.
This is not a flaw to complain about, it is the design. The skill is in the next step. See what a container image scan actually checks.
What reachability adds
Reachability analysis follows the call graph from your application's entry points and asks whether a path leads to the vulnerable function. When no path exists, the finding is not exploitable through your code. That is strong evidence for deprioritising it, and recordable evidence for an auditor. See can reachability analysis justify a risk acceptance.
Be careful with the limits. Reachability is a static analysis of code. Dynamic loading, reflection and plug-in systems can hide paths, so treat "unreachable" as a strong signal and record the assumptions.
A worked example in words
Say an image lists a critical flaw in an XML parsing library. Present: yes, version matches. Loaded: yes, your web framework imports it. Reachable: the flaw is in a function that parses a particular external entity syntax, and your code calls only the basic parse function. Exposed: not applicable if not reachable. Decision: lower priority, rebuild on the next scheduled cycle, record the reasoning with an expiry date. If a fix exists, taking it is still cheap and clears the item.
Do not skip the base
The same method applies to operating system packages. Many are present and not loaded. But "not loaded" for a package can change when someone adds a debugging tool or a new dependency. That is a reason to remove unneeded packages, not only to deprioritise them. Minimal images shrink this class of finding at the source.
What to record
For each deprioritised finding, store the finding, the reason, the person, and a review date. A decision with no expiry becomes a hidden risk.
Try it yourself
Click through it at your own pace, or open it full screen.
Watch how the list of findings shrinks once reachability is applied, and note which ones remain.
Where Safeguard fits
Safeguard provides reachability analysis alongside dependency scanning and SBOM data, so findings can be ranked by whether the vulnerable code can be called. Try a free scan.
Quick check
Can I ignore every unreachable finding?
Deprioritise, do not ignore. Record the reason and an expiry date, and take the fix when it is cheap.
Does reachability work for operating system packages?
It is strongest for application dependencies where a call graph exists. For system packages, use presence and loading checks and prefer removing what you do not need.
Last reviewed: 19 October 2026.