When a container scan reports a vulnerability that does not apply to you, OpenVEX gives you five fixed justification labels for a not_affected statement. They range from the component not being present to built-in mitigations. Choose the label your evidence supports, attach that evidence, and give the statement a review date.
What a VEX statement is
VEX stands for Vulnerability Exploitability eXchange. A VEX statement says whether a product is affected by a vulnerability. The OpenVEX specification defines status values including not_affected, affected, fixed and under_investigation. It says that for not_affected, a statement must include either a status justification or an impact statement explaining why the product is not affected.
That rule is the useful part for container teams. A scanner lists everything installed. VEX is how you record, in a standard form, that some of those items are harmless in your context, and why.
The five justifications in plain words
| Label | Plain meaning | Typical container case |
|---|---|---|
| component_not_present | The component is not in the product | A package was removed in a later build step, so the finding is stale |
| vulnerable_code_not_present | The component is there but the vulnerable code is not | The library was built without the affected module |
| vulnerable_code_not_in_execute_path | The vulnerable code exists but cannot run as the product uses it | A function in a bundled library is never called |
| vulnerable_code_cannot_be_controlled_by_adversary | An attacker cannot influence the vulnerable code | The affected input comes only from trusted internal configuration |
| inline_mitigations_already_exist | Built-in protections prevent exploitation | A setting or feature in the product blocks the attack path |
The OpenVEX text describes the second as the case where the vulnerable component is included but the vulnerable code is not, for example when the source was configured or built in a way that excluded it. It describes the third as code that cannot be executed as the product uses it. For the fourth, it warns that the justification can be difficult to prove conclusively. For the fifth, it says the protections cannot be subverted.
How to choose
Ask three questions in order.
- Is the component actually in the final image? If not, the label is component_not_present. Verify with the package list of the exact digest.
- If it is, is the vulnerable code in the build? If the affected module was compiled out or a feature flag removed it, use vulnerable_code_not_present.
- If the code is present, can it run, and can an attacker reach it? Use not_in_execute_path when it never runs, cannot_be_controlled_by_adversary when it runs but inputs are trusted, and inline_mitigations when something blocks the attack.
Work from the strongest, easiest to prove claim to the weakest. A claim you cannot prove should not be made. If you are not sure, the honest status is under_investigation, which the specification describes as meaning it is not yet known whether the versions are affected, with updates to follow.
A worked example
A scan of an image reports a vulnerability in an image processing library that a web framework bundles. The team investigates.
- The package is in the image, so component_not_present is out.
- The vulnerability is in a decoder for one file format. The team checks the build configuration and finds that decoder is compiled in. So vulnerable_code_not_present is out.
- The service never accepts uploaded images and never calls the decoding functions. Static analysis and a code search agree. This supports vulnerable_code_not_in_execute_path.
The team records the statement with that label, the evidence (the search result and the code review), the image digest and a review date. If a later feature adds image uploads, the statement must be revisited, so the team sets an expiry and links it to the feature's design review.
Step by step: write a good statement
- Name the product by image digest, not just by tag.
- Name the vulnerability by its identifier.
- Pick the status. For not_affected, pick one justification label.
- Add a short impact statement in plain words for the reader.
- Attach or link the evidence.
- Add an expiry or a trigger for review.
- Have someone else review it.
- Publish it where your customers or your own tools can read it.
Evidence to keep for each label
| Label | Evidence worth keeping |
|---|---|
| component_not_present | Package list of the exact digest showing the component absent |
| vulnerable_code_not_present | Build configuration or a listing showing the affected module was excluded |
| vulnerable_code_not_in_execute_path | Code search results, call graph or reachability output, plus a reviewer note |
| vulnerable_code_cannot_be_controlled_by_adversary | Data flow notes showing the input source is trusted |
| inline_mitigations_already_exist | The setting or feature that blocks the path, and a test showing it works |
Keep the evidence next to the statement, with the image digest and the date. If a customer or auditor asks months later, you should be able to answer in minutes, not rebuild the investigation.
How VEX fits into a container workflow
A practical flow looks like this. The scan runs and produces findings. Triage sorts them into fix now, fix later and does not apply. For the third group, an engineer writes a VEX statement with a label and evidence, and a second person reviews it. The statement is attached to the image digest, so it stays with the artifact. When the image is rebuilt, a new scan runs, and the statements are checked again: any statement whose conditions changed is reopened.
Two details help in practice. First, tie statements to a digest or a version range you actually checked, not to a product name in general. Second, keep a short list of who may approve each label, since the reachability based labels carry more risk than component_not_present.
What not to do
- Do not use a justification you cannot back up. A wrong VEX statement misleads customers.
- Do not apply a statement to all images by CVE alone. It applies to products and versions you checked.
- Do not treat VEX as a replacement for scanning. It annotates scan results. See does VEX replace vulnerability scanning.
- Do not let statements live forever. Code changes can make them false.
Try it yourself
Click through it at your own pace, or open it full screen.
Try to decide, for the finding shown, which of the three questions above it answers, and what evidence you would attach.
Where Safeguard fits
Safeguard offers reachability analysis, which can help you gather evidence for the execute path question, and it keeps findings and decisions with their history. You can try a free scan at gold.safeguard.sh/scan.
Quick check
Do I need a justification for every not_affected statement?
Per the OpenVEX specification, a not_affected statement must include either a status justification or an impact statement that explains why.
Which justification is the easiest to prove?
component_not_present, because you can check the package list of the exact image digest. The arguments about reachability and attacker control take more evidence.
When should I use under_investigation?
When you do not yet know whether the product is affected. The specification expects a later update when you do.
Sources
Last reviewed: 19 October 2026.