← Back to list

VEX = Accuracy. Cutting Trivy HIGH findings by 39% with vendor data

Practical example of vulnerability management with Trivy and VEX in the pipeline. reel vex-hub aggregates openVEX vendor statements

Simone Antolini · 2026-05-03 22:36 · 1 claps · 3.9 min read
#vex #cve #trivy #vulnerability-management #devsecops
Open on Medium ↗
Wiki topics: GEN · Genomics & Sequencing BIZ · Business Strategy 🔒 · Cybersecurity

VEX = Accuracy

Every team I’ve worked with has the same Monday-morning ritual. Open the scanner report. Scroll. Filter to HIGH and CRIT. Scroll again. Pick the first CVE, click through to the vendor advisory, read three paragraphs, conclude “yeah, that one doesn’t really apply to us”, close the tab. Move to the next one.

By Wednesday, half the pile is closed as not-actually-exploitable, the other half is in tickets fighting to get prioritised, while the next scan is already producing a fresh batch.

This isn’t a scanner problem. Scanners are doing what they were built to do — match installed packages against the public CVE feeds and flag everything that overlaps. The problem is that the public CVE feeds carry the vulnerability but not the vendor’s analysis of it. The vendor knows whether their build of openssl is actually affected. The scanner doesn’t. And I’m the engineer who ends up being the bridge of this data.

Luckily, the gap has a name now, and a format, and a growing supply of vendors publishing into it.

VEX, briefly

VEX — Vulnerability Exploitability eXchange — is the structured answer to “does this CVE actually affect this product?” Four statuses: not_affected, fixed, under_investigation, affected. When not_affected, a justification (vulnerable code not present, not in execute path, mitigations exist) is attached.

Some facts:

  • Red Hat and SUSE already publish VEX
  • Ubuntu and Debian publish the OVAL equivalent
  • Trivy added a --vex flag in 0.49. Grype is closing in
  • The CSAF 2.0 [¹] and OpenVEX specs are stable
  • CISA published guidance on it last year

What’s not uniform yet is the supply chain. Different vendors, different document formats [²], different ways of describing the products their statements apply to [³].

So what does it mean to apply VEX in my pipeline of vulnerability management and how to take advantage of the benefits of it? Here is a simple experiment end to end, to highlight the value.

The experiment

Scan without VEX

Let’s start scanning this image:

registry.access.redhat.com/ubi9/ubi:9.3. Trivy 0.70.0, DB snapshot 2026–04–21.

Baseline scan, no VEX:

trivy image --format json --output scan-no-vex.json \
  registry.access.redhat.com/ubi9/ubi:9.3

Three numbers I’ll be tracking:

jq '{
  total:  [.Results[]?.Vulnerabilities // []] | flatten | length,
  high:   [.Results[]?.Vulnerabilities // []] | flatten
            | map(select(.Severity == "HIGH")) | length,
  unique: [.Results[]?.Vulnerabilities // []] | flatten
            | map(.VulnerabilityID) | unique | length
}' scan-no-vex.json
| Metric         | No VEX | 
| -------------- | ------ | 
| Total findings | 614    | 
| High           | 51     | 
| Unique CVEs    | 437    | 

Scan with VEX

To retrieve the vex statements, I used *vex-hub at vex.getreel.dev, an open-source project I contribute to. The same result is reachable pulling individual vendor feeds and doing the merging and parsing yourself. vex-hub* ingests everything automatically and returns OpenVEX statements via a REST call.

The /v1/statements endpoint takes a CycloneDX SBOM, runs every CVE/PURL pair through its resolver, returns OpenVEX. Generate the SBOM:

trivy image --format cyclonedx --scanners vuln \
  --output sbom.json registry.access.redhat.com/ubi9/ubi:9.3

Then feed it to vex-hub:

jq '{sbom: .}' sbom.json | \
  curl -sS -X POST https://vex.getreel.dev/v1/statements \
  -H "Content-Type: application/json" \
  -d @- -o vex.json

The document came back with 111 not_affected, 3,093 fixed, 610 affected, 8 under_investigation. Re-scan with it attached:

trivy image --vex vex.json --format json \
  --output scan-with-vex.json \
  registry.access.redhat.com/ubi9/ubi:9.3
| Metric         | No VEX | With VEX | Δ      |
| -------------- | ------ | -------- | ------ |
| Total findings | 614    | 549      | −10.6% |
| High           | 51     | 31       | −39.2% |
| Unique CVEs    | 437    | 408      | −6.6%  |

Let’s take a closer look at one CVE

CVE-2024–12797. Trivy flagged it High against openssl 1:3.0.7-25.el9_3 with a fix in 1:3.2.2-6.el9_5.1. Which automatically means for us: we've got to fix this.

But looking better at Red Hat VEX feeds, we see they’ve published this statement about it:

curl -sS -X POST https://vex.getreel.dev/v1/statements \
  -H "Content-Type: application/json" \
  -d '{"cves":["CVE-2024-12797"],"products":["pkg:rpm/redhat/openssl"]}' \
  | jq '.statements[0]'

{
  "vulnerability": {"name": "CVE-2024-12797"},
  "products": [{"@id": "pkg:rpm/redhat/openssl",
                "identifiers": {"purl": "pkg:rpm/redhat/openssl"}}],
  "status": "not_affected",
  "justification": "vulnerable_code_not_present",
  "supplier": "redhat",
  "status_notes": "source_format=csaf; match_reason=direct"
}

Upstream openssl has the bug but Red Hat’s build doesn’t compile in the vulnerable code path. The advisory has been published and machine-readable for months. 28 other CVEs across this scan followed the same pattern (vendor not_affected with vulnerable_code_not_present).

Accuracy, not noise

That’s what VEX delivers, in one word: accuracy. The 20 High-severity findings that disappeared weren’t suppressed because someone tuned a filter. They disappeared because the vendor had already analysed them and the scanner finally got to read that analysis. The findings that stayed are the ones that warrant attention.

Notes

[¹] CSAF 2.0 — Common Security Advisory Framework. OASIS-standardised JSON format for security advisories. Red Hat, SUSE, Cisco, Oracle and others publish in this format.

[²] Red Hat publishes both CSAF and OVAL v2, an older XML format that covers the EUS / AUS / E4S extended-support streams the CSAF feed omits by design. Ubuntu and Debian publish OVAL as their primary advisory channel. So a scanner that wants full coverage needs to read both formats.

[³] PURL ↔ CPE — Package URLs (pkg:rpm/redhat/openssl) describe a package; CPEs (cpe:/a:redhat:enterprise_linux:9::baseos) describe a platform component. Vendor advisories often key on CPE; scanners match on PURL. Bridging requires vendor-published mapping files (Red Hat's repository-to-cpe.json) plus prefix-match rules per SECDATA-1220.

vex-hub: [getreel.dev/vex](https://getreel.dev/vex)· Github repo: [getreeldev/reel-vex](https://github.com/getreeldev/reel-vex)


메타데이터
post_id
c29dceabcff2
slug
vex-accuracy-cutting-trivy-high-findings-by-39-with-vendor-data-c29dceabcff2
url
https://medium.com/@simone_51738/vex-accuracy-cutting-trivy-high-findings-by-39-with-vendor-data-c29dceabcff2
canonical_url
https://medium.com/@simone_51738/vex-accuracy-cutting-trivy-high-findings-by-39-with-vendor-data-c29dceabcff2
author_url
https://medium.com/@simone_51738
status
ok
fetched_at
2026-06-26 03:39:16