Guide · Vulnerability management
Vulnerability waivers, risk acceptance & VEX
Run software composition analysis long enough and you’ll hit a finding you can’t fix today — no upstream patch, or it isn’t exploitable in your context. Ignoring it is negligent; blanket-muting it is worse. Here’s the responsible middle: risk acceptance, waivers, and VEX.
Updated 29 Jul 2026 · by the depproof team
Your real options for an unfixable finding
When a scan flags a dependency CVE, you have four moves — in order of preference:
- Upgrade to a fixed version — the real fix, when one exists.
- Remove or replace the dependency — if you don’t truly need it.
- Mitigate — configuration or code that neutralizes the path.
- Accept the risk, deliberately — when none of the above is possible yet. This is where waivers and VEX come in.
The failure mode isn’t choosing to accept a risk — it’s accepting it silently, with no justification, owner, or expiry, so nobody ever revisits it.
Waivers: risk acceptance you can defend
A waiver is a documented decision to accept a specific finding for now, and to stop it from failing your build. A defensible waiver has four things:
- A justification — why it’s acceptable (no fix yet, not reachable, false positive, compensating control).
- An owner — who accepted the risk (ideally security, not whoever was unblocking a build).
- An expiry — a date it’s re-reviewed, so “temporary” doesn’t become forever.
- A scope — which finding, which repo/product — and the raw finding stays in your inventory. You suppress the gate, not the record.
What is VEX (Vulnerability Exploitability eXchange)?
VEX stands for Vulnerability Exploitability eXchange. It is a machine-readable statement, published by whoever knows the product best — usually its vendor or maintainer — saying whether a specific vulnerability actually affects that product. Not whether the vulnerable component is present: whether it matters.
That distinction is the entire point. A scanner can tell you a CVE exists somewhere in your dependency tree. Only the people who built the product know whether the vulnerable function is ever called, whether an attacker could reach it, or whether a configuration already neutralises it. VEX is the format for saying so in a way tools can consume, instead of a PDF advisory or an email thread.
It answers a different question than your SBOM. An SBOM says what components you ship; VEX says which of their vulnerabilities actually affect you. A VEX statement carries one of four statuses:
| VEX status | Meaning |
|---|---|
not_affected | The CVE is present in a component but doesn’t affect your product — with a justification (e.g. the vulnerable code isn’t present, isn’t reachable, or can’t be triggered by an attacker). |
affected | Your product is affected; action or a mitigation is recommended. |
fixed | A release has already remediated it. |
under_investigation | You’re still determining whether it affects you. |
VEX exists to cut false-positive noise: instead of a customer seeing a scary
CVE in your SBOM and filing a ticket, you hand them a not_affected
statement that explains why.
“Not affected” needs a reason — and the reasons are fixed
This is the part most summaries skip, and it is what makes VEX more than an
opinion. You cannot simply declare not_affected; the
specification requires a justification, chosen from a defined
set. The
OpenVEX
specification permits five:
| Justification | What you are asserting |
|---|---|
component_not_present | The vulnerable component isn’t in the product at all. |
vulnerable_code_not_present | The component is there, but the specific vulnerable code isn’t included. |
vulnerable_code_not_in_execute_path | The vulnerable code ships but is never executed. |
vulnerable_code_cannot_be_controlled_by_adversary | It executes, but an attacker can’t influence it. |
inline_mitigations_already_exist | Something already in the product blocks exploitation. |
Being made to pick one is a discipline, not a formality. “We looked and it’s fine” is not expressible in VEX — you have to commit to which kind of fine, and that claim is auditable later. It is the same rigour a good waiver justification demands, in a form you can hand to someone outside your company.
The three VEX formats
VEX is a concept with several encodings. They express the same core idea and differ mainly in weight and in what else they travel with:
- OpenVEX — a minimal, standalone JSON format. The easiest to generate and consume, and the usual starting point.
- CycloneDX VEX — embedded in, or shipped alongside, a CycloneDX SBOM. Natural if you already produce CycloneDX; note it defines its own status and justification labels rather than reusing OpenVEX’s.
- CSAF VEX — a profile of the OASIS CSAF standard. The heaviest, and the one large vendors and regulators tend to expect.
Pick by audience, not by preference: emit what the party asking for it can actually ingest.
Waiver vs VEX — related, not the same
A waiver is your internal, operational decision: don’t fail our build on this, here’s why, expires then. VEX is a formal, shareable statement about exploitability, often for downstream consumers. They reinforce each other — a “not affected” determination can both suppress your own gate and generate the VEX you publish. The best workflow makes one flow into the other.
None of this works without a trustworthy finding underneath
A VEX statement is a claim about a specific component at a specific version.
That means its quality is capped by the scan that produced the finding — you
cannot honestly declare vulnerable_code_not_present for a
dependency you didn’t know you shipped.
Two properties of the underlying scan decide whether your VEX is worth anything:
- The full transitive tree, not just direct dependencies. Most vulnerable components arrive several levels down, through packages you never chose. A VEX built on a partial tree is confidently wrong about the part it never saw — and an incomplete scan looks identical to a clean one.
- Resolved input, not declared intent. A lockfile or a resolved dependency graph states what you actually ship; a manifest states what someone asked for. Version-specific claims need the former.
There is a confidentiality angle too, and it matters for exactly the audience that asks for VEX. Exploitability analysis means reasoning about your own source and configuration — which is a strong argument for producing this evidence with a self-hosted scan that runs inside your infrastructure, rather than shipping the code to a vendor cloud to be told what is in it. The Maven and Gradle and npm guides show what that scan looks like in practice.
Doing it well
- Per-finding, not blanket — accept individual findings with reasons, never “ignore all mediums.”
- Security-owned — risk acceptance is a security decision, not a build-unblocking shortcut.
- Time-bound — expiries force re-review, catching the “not reachable” that became reachable.
- Audited — an append-only trail of who waived what, when, and why: the evidence an audit needs.
- Exportable — produce VEX (OpenVEX/CycloneDX/CSAF) so downstream teams and customers get the answer, not just the noise.
How depproof handles it
The depproof hub is built exactly around this: waive a finding from the repo page with a justification and expiry, keep an append-only audit trail with posture snapshots, and export OpenVEX or CSV. And because the hub enforces waivers centrally, a finding your security team has accepted stops failing every repo’s CI gate — fail-closed, so an unreachable hub never silently turns a red build green. See the hub release for the details.
Frequently asked questions
What is a vulnerability waiver?
A vulnerability waiver is a documented decision to accept a known finding for now instead of fixing it — because there is no upstream fix, it isn’t exploitable in your context, or the risk is acceptable. Done well, a waiver records who accepted it, why, its scope, and an expiry date, and it suppresses that finding from your CI gate so the build stops failing — without deleting the finding from your inventory. It’s risk acceptance with an owner and a clock, not a permanent mute.
What is VEX?
VEX (Vulnerability Exploitability eXchange) is a machine-readable statement about whether a specific vulnerability actually affects a product. Rather than “this CVE is present,” VEX answers “are we affected?” with a status — not_affected, affected, fixed, or under_investigation — and, for not_affected, a justification (for example, the vulnerable code isn’t present, isn’t reachable, or can’t be triggered by an attacker). It exists to cut through false-positive noise: an SBOM says what components you ship; VEX says which of their vulnerabilities matter.
What does VEX stand for?
VEX stands for Vulnerability Exploitability eXchange. It is a machine-readable statement, published by the party that knows the product best — usually the vendor or maintainer — declaring whether a specific vulnerability actually affects that product, rather than merely whether the vulnerable component is present. The point is to communicate exploitability in a form tools can consume, instead of a PDF advisory or an email thread.
What is the difference between VEX and an SBOM?
They answer different questions and are designed to be used together. An SBOM is an inventory: it lists the components a product contains. VEX is an assessment: for a given vulnerability in one of those components, it states whether the product is actually affected, and if not, why. An SBOM alone tends to generate false-positive alarm — a customer sees a CVE against a listed component and raises a ticket. VEX is what lets the vendor answer "we ship that component, and this CVE does not affect us, for this specific reason."
Who is supposed to produce a VEX document?
Whoever has the knowledge to make the exploitability determination — in practice the software producer: the vendor, the maintainer, or an internal team for software their own organisation ships. It is not something a consumer can generate about someone else’s product, because the judgement depends on knowing whether the vulnerable code is reachable in that product. Consumers receive VEX; producers publish it.
Can you declare a vulnerability "not affected" without giving a reason?
No. Under the OpenVEX specification a not_affected status requires a justification drawn from a defined set: component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, or inline_mitigations_already_exist. That constraint is what gives VEX its value — a vague "we checked and it is fine" cannot be expressed, so every claim commits to a specific, auditable kind of not-affected.
What is the difference between a waiver and VEX?
A waiver is your internal operational decision — “don’t fail our build on this finding, here’s why, expires on this date.” VEX is a formal, shareable statement — often given to downstream consumers or customers — about whether your product is exploitable for a given vulnerability. They’re complementary: a “not affected” determination can drive both a waiver (suppress it in your own gate) and a VEX document (communicate it outward). A good tool lets you produce VEX from the waiver decisions you’ve already made.
What is OpenVEX?
OpenVEX is a minimal, open VEX format from the OpenSSF — a lightweight JSON way to express the same not_affected / affected / fixed / under_investigation statements and justifications. Other VEX formats include CycloneDX VEX (embedded in or alongside a CycloneDX SBOM) and CSAF VEX (the OASIS standard). They carry the same core idea; OpenVEX is the easiest to generate and consume.
Is it safe to suppress a vulnerability finding?
It’s safe when the suppression is deliberate, justified, time-bound, and audited — and dangerous when it’s a blanket mute nobody revisits. The risk of blanket suppression is that a finding you accepted as “not reachable” becomes reachable after a refactor and no longer alerts. That’s why good practice is: accept per-finding with a written justification, set an expiry so it’s re-reviewed, keep an audit trail, and keep the raw finding in your inventory (suppress the gate, not the record).
Sources
- OpenVEX
specification — the status values and the five
not_affectedjustification labels quoted above. - CISA — Minimum Requirements for VEX — what a VEX document must carry to be usable.
- CycloneDX VEX and OASIS CSAF — the two alternative encodings.
Specification details last checked 16 August 2026. Formats evolve — verify against the primary sources above before building against them.
General information on handling dependency risk — pairs with scanning your dependencies for the findings in the first place.