The Vulnerable Code Was Never in the Execute Path. Nobody Wrote That Down, So the Scanner Asked Again.
TL;DR Vulnerability prioritization is the process of ranking security weaknesses by real-world risk and environment context rather than by technical severity scores alone. For engineers, this means determining if a flaw is actually reachable in production—a decision that should be recorded once via the VEX standard rather than re-derived every scan cycle.
A nightly scan runs, identifies a "Critical" flaw in a YAML parser, and automatically opens a ticket. Because the service has an empty owner field in the registry, the ticket routes to whoever holds the pager. As documented in a recent r/sre account, an engineer might face thirty of these tickets in a single shift, each with a CVE number and a due date, but no context.
In this specific case, the engineer spends twenty minutes digging through the build graph only to find the parser was a transient dependency of a build-time linting tool. The vulnerable code never made it into the production container. The engineer closes the ticket as "not applicable" and moves to the next. Two nights later, the scanner runs again, finds the same package in the manifest, and opens a new ticket. The answer was found, then immediately thrown away.
What the ticket is actually asking
When a scanner flags a CVE, it is making a claim about a package version. Vulnerability prioritization is the act of deciding which of these findings enter remediation work now, later, or not at all.
For the SRE or backend engineer, the first decision isn't about the severity of the flaw—it’s about the presence of the flaw. Severity (the CVSS score) is a property of the code itself. Presence is a property of your specific build and runtime environment. The ticket tells you the code is dangerous; it doesn't tell you if the code is actually there.
The five questions already have names
Most engineers perform cve triage by intuition, but the determinations they make are already codified in a standard called VEX (Vulnerability Exploitability eXchange). According to the CISA Minimum Requirements for VEX, there are four primary statuses for a vulnerability: not_affected, affected, fixed, and under_investigation.
When you decide a ticket is a "false positive," you are actually selecting one of five standardized not_affected justifications.
| VEX Justification | The Engineering Question |
|---|---|
| component_not_present | Is this package actually in the final image, or just the repo? |
| vulnerable_code_not_present | The library is there, but did we link the specific vulnerable function? |
| vulnerable_code_not_in_execute_path | The code is in the binary, but is it ever actually called? |
| vulnerable_code_cannot_be_controlled_by_adversary | It’s called, but can an attacker reach it with their input? |
| inline_mitigations_already_exist | Does a WAF or a sidecar proxy already block this specific exploit? |
Key Takeaway: Vulnerability prioritization is a waste of engineering capacity unless the "not affected" determination is recorded in a machine-readable format that survives the next scan.
Getting the evidence: A per-surface procedure
To move beyond "gut feeling" triage, you need a repeatable procedure for gathering evidence. Use these commands to settle the first three VEX justifications.
1. Component Not Present (The Build-Graph Check)
Check if the package is in the final runtime layer or merely a build-stage artifact.
# For Docker multi-stage builds, check the final image layers only
docker history --no-trunc <image_name> | grep <package_name>
If you find the package in a manifest (like package.json) but not in the final image, the justification is component_not_present.
2. Vulnerable Code Not Present (The Link-Time Check)
In compiled languages like Go, a module might be in your go.mod, but the linker may have pruned the vulnerable symbols if they weren't used.
# Check if the specific vulnerable symbol exists in the binary
nm -D ./my-service-binary | grep <vulnerable_function_name>
If the function name (symbol) is missing, the code isn't there to be executed.
3. Not in Execute Path (The Runtime Check)
The package is in the container, but is it loaded into memory by the running process?
# Find the PID and check mapped files in Linux
cat /proc/<PID>/maps | grep <package_name>.so
If the shared library isn't mapped into the process memory, it isn't in the execute path at that moment.
Where this stops being the on-call engineer's job
There are hard limits to this manual evidence-gathering. Static reachability analysis is easily defeated by dynamic dispatch, reflection, and deserialization. Furthermore, a determination is only valid for a specific build. A refactor that adds a new import can turn an unreachable library into a critical threat overnight.
This is why you record determinations against a specific build hash, not against the service name. If the build changes, the triage status must reset.
Who pays for this, and how to measure it
Routing unowned scanner output to the on-call rotation is a form of unplanned work that carries a heavy interrupt cost. To fix the vulnerability prioritization framework, leadership needs to track three metrics:
- The N/A Rate: The percentage of tickets closed as "not affected."
- Mean Time to Determination: The engineer-minutes spent proving a flaw is unreachable.
- The Re-derivation Rate: The percentage of "not affected" findings that were already investigated in a previous cycle.
A high re-derivation rate is pure waste. If your team is answering the same "is this reachable?" question every Tuesday, you don't have a security problem; you have a data persistence problem.
Common Pitfalls in Vulnerability Prioritization
- Trusting the Scanner Default: Most scanners assume "Affected" until proven otherwise. Without a VEX-compliant record, the scanner's default wins every time it runs.
- Prioritizing by CVSS Alone: CVSS scores measure severity, not risk. Using EPSS (Exploit Prediction Scoring System) helps, but even a high EPSS score doesn't matter if the code is
component_not_present. - Ignoring the Routing Defect: If a finding has no owner, it is a routing defect. The fix is assigning a service owner in the metadata, not asking the SRE to fix the code.
Why this matters
Current risk-based vulnerability management focuses on the attacker's side—what could they do. But for the engineer holding the pager, the focus is on the environment—what did we build. Investigating these reachability questions is an intensive process that spans the build graph, the deployed artifact, and the running process.
This is the exact shape of work Operate is designed to automate. By watching the production environment and the build pipeline, Operate can investigate an incident, find the root cause evidence—such as identifying that a package is never loaded into memory—and draft a PR or a VEX statement for review. This ensures that when an engineer looks at a ticket, they are looking at evidence, not a blank text field.
FAQ
What is vulnerability prioritization? It is the process of ranking security findings based on their actual risk to a specific environment, combining technical severity (CVSS) with runtime context.
What is the difference between CVSS and EPSS? CVSS measures how bad a flaw is; EPSS predicts how likely it is to be exploited in the wild. Neither tells you if the code is actually running in your service.
What is VEX and who is supposed to write it? VEX (Vulnerability Exploitability eXchange) is a standard for communicating whether a product is affected by a vulnerability. It is ideally emitted by the build system or the engineering team that owns the service.
How long is a not-affected determination valid? Only until the next change to the artifact (new build) or a change in the environment configuration.
Sources & further reading
- According to CISA, the VEX standard is designed to reduce the "noise" of vulnerabilities that are not exploitable in specific contexts.
- According to Orca Security, prioritization is a capacity problem before it is a scoring problem.
- According to Wiz, organizations will face over 59,000 new CVEs in 2026, making manual triage impossible.