A not affected VEX statement names the vulnerability, the product, a status of not_affected, a timestamp, and a justification. Under OpenVEX, you pick one of five justification labels or write an impact statement, and the spec recommends the machine readable label.
What VEX is for
A scanner flags every package that contains a vulnerable component. VEX, the Vulnerability Exploitability eXchange, lets a producer say whether a given vulnerability actually matters for a given product. OpenVEX is one open format for it. For a library maintainer, it is a way to answer "your library depends on the vulnerable package, are you affected?" once, in a form tools can read.
What a statement must contain
The OpenVEX specification lists the required parts:
- A vulnerability with a name, such as its CVE or GHSA identifier.
- A status. The four values are
not_affected,affected,fixedandunder_investigation. - At least one product, which can be inherited from the document.
- A timestamp, explicit or inherited. Statements without one are incomplete.
- For
not_affected: a justification label, an impact statement, or both. - For
affected: an action statement describing remediation or mitigation steps (the spec says it should).
The five justification labels
| Label | Use when |
|---|---|
component_not_present | The package is not in the product at all, for example it is only in a build step that is not shipped |
vulnerable_code_not_present | The package is present but the vulnerable code was removed or not compiled in |
vulnerable_code_not_in_execute_path | The code exists but your product never runs it |
vulnerable_code_cannot_be_controlled_by_adversary | The code runs, but attackers cannot supply the dangerous input |
inline_mitigations_already_exist | Something in the product already prevents exploitation |
Choose the narrowest true label. The execute path label is the one most library maintainers reach for, and it is also the one you should be most careful with. You need evidence, such as a code search or a reachability result, not an assumption.
A worked example
Say a library acme-lib version 3.2.0 bundles parser 2.3.0, and an advisory affects a function called parseExtended. You searched and never call it.
Your statement would say, in plain terms: vulnerability GHSA-xxxx for product acme-lib 3.2.0 is not_affected, justification vulnerable_code_not_in_execute_path, impact statement "acme-lib does not call parseExtended", dated today. Use a real advisory identifier from the real advisory when you write yours.
The OpenVEX project documents the exact JSON structure, and tools exist to generate files. Read the spec before publishing, and keep the file in your repository or release assets.
Habits that keep it honest
- Put the date on every statement and update it if your code changes. A refactor can start calling the function.
- Re-check when you add features near the dependency.
- Reverse the status to
affectedorfixedif you learn you were wrong, and say so. - Link the VEX file from your security policy.
When not to use VEX
If your library is affected, a VEX statement is not enough. Publish an advisory and ship a patch. Use affected plus an action statement only as a bridge until the fix lands.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how the same finding can be exploitable in one setup and not in another.
Quick check
Is a VEX statement a guarantee?
No. It is the producer's assessment at a point in time. That is why the timestamp and justification matter.
Do I need to write VEX by hand?
Tools can generate files, but you still supply the judgment and the evidence.
Safeguard can assess exploitability and reachability for findings in your dependencies, which gives you evidence for the justification. The free scan is a way in.
Sources
- OpenVEX specification: https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md
Last reviewed: 19 October 2026.