Evidence-backed VEX · deep dive

How to justify a not_affected VEX to an auditor

Marking a vulnerability not_affected is how you take it off your reporting queue — and exactly the decision an authority is most likely to challenge. The difference between a not_affected that holds and one that doesn’t is the evidence behind the justification code.

Updated 7 Jul 2026·~6 min read·For product security engineers

Of the four VEX statuses, not_affected is the one that does the useful work — it's how a scary CVE in a dependency comes off your queue. It's also the one a regulator scrutinises hardest, because it's the decision not to act. Getting it right isn't about picking the status; it's about being able to say why.

Why not_affected is the risky one

An affected commits you to remediation and possibly reporting — nobody argues with your caution. A not_affected is the opposite: it's a claim that a known vulnerability doesn't matter here, and it removes an obligation. If an authority later disagrees — the vulnerability was actively exploited and did reach your product — the not_affected is the record they'll examine. "Our engineer judged it" is not a position; the justification code and its evidence are.

The five justification codes

VEX doesn't let you just say not_affected — it asks why, from a fixed set of standard justifications:

JustificationMeans
component_not_presentThe component isn't in this build at all.
vulnerable_code_not_presentThe component is present, but the vulnerable code isn't in it.
vulnerable_code_not_in_execute_pathThe vulnerable code is present but never reached.
vulnerable_code_cannot_be_controlled_by_adversaryReachable, but an attacker can't drive it into the vulnerable state.
inline_mitigations_already_existA mitigation in your product neutralises it.

The evidence each one needs

Choosing the code is half the job; backing it is the other half. Each justification points at a different kind of evidence:

  • component_not_present → the SBOM for the exact build shows the component absent.
  • vulnerable_code_not_present → build/config evidence that the vulnerable module or function isn't compiled in.
  • vulnerable_code_not_in_execute_path → a reachability trace showing no path from an entry point to the vulnerable code.
  • vulnerable_code_cannot_be_controlled_by_adversary → an exploitation attempt that reached the code and could not trigger the condition.
  • inline_mitigations_already_exist → identification of the mitigation and why it neutralises this vulnerability.

Notice the escalation: the first two are answered by inventory and build facts, the middle by reachability analysis, and the hardest — reachable but not exploitable — only by an actual exploitation attempt. Which code you can honestly claim depends on how deep your evidence goes; the distinction is the subject of proof-of-exploitability vs. reachability.

Anatomy of a defensible not_affected

A defensible not_affected names the justification, and points at the artifact that proves it.

Put concretely, a not_affected that holds up has four parts: the vulnerability and the product version it's about; the justification code; a plain-language rationale; and a reference to the evidence artifact — the SBOM entry, the reachability trace, the exploitation log. Export it in CycloneDX VEX or OpenVEX; the format is incidental. What makes it survive a reasoned request is that the justification isn't asserted, it's supported — which is the whole argument of evidence-backed VEX.

Frequently asked

What are the VEX not_affected justification codes?

VEX defines five: component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, and inline_mitigations_already_exist. Each states a different reason the product is unaffected, and each calls for different supporting evidence.

Is a not_affected VEX enough without evidence?

It exports fine, but it won’t hold up. Under the CRA a market-surveillance authority can ask why a vulnerability wasn’t reported; a not_affected justified only by a human’s judgement is an assertion, not a defence. Backing the justification code with evidence is what makes it defensible.

Which justification is strongest?

None is inherently strongest — the right code is the one that’s true for your product. What varies is the evidence: component_not_present is backed by the SBOM, vulnerable_code_not_in_execute_path by a reachability trace, and vulnerable_code_cannot_be_controlled_by_adversary by an exploitation attempt.

Design partners

Every not_affected, with its proof attached.

See not_affected determinations that carry the reachability trace or exploitation attempt behind the justification — ready for a reasoned request.