The Complete Scan Matrix: Four Images, Six Configurations
Before a single explanation — here is the full data from my AWS EC2 test machine. Every number in this table comes from real terminal output you will see in full throughout this post. No “not tested” entries — all six configurations were run across all four images.

🎯 FIVE THINGS HAPPENING SIMULTANEOUSLY — READ THIS FIRST
-
Default Trivy has zero DHI VEX context. Without configuring the DHI advisory repo, Trivy reports every OS package CVE it finds in NVD. On dev images that is 59 - 62 CVEs. Every single one is a real CVE. Docker just hasn’t told Trivy which ones aren’t exploitable in these specific containers.
-
--vex repoalone points at the wrong hub entirely. Aqua’s VEX Hub has no entries for DHI images whatsoever. Running with--vex repobut without configuring the DHI source returns identical results to running without any VEX flag. This is the trap everyone including me fell into first. -
After correct DHI advisory repo setup, Trivy drops dramatically. 62 becomes 9 on the python image. 59 becomes 9 on traefik. VEX suppresses 50 - 53 CVEs - all glibc, systemd libs, sqlite, ncurses, apt noise. What remains are genuine findings.
-
OCI VEX file vs advisory repo gives a 1-CVE discrepancy on some images. The
--vex vex.jsonmethod (exported OCI attestation) suppresses zlib CVE-2026-27171. The--vex repomethod (advisory hub) doesn’t have that entry yet. 1-CVE content lag — both methods are valid. -
Scout and Trivy have different vulnerability database coverage. Scout finds Python interpreter CVEs (CVE-2025-15367/15366 in Python 3.14.3) that Trivy’s vuln DB completely misses. Trivy scans Go binaries with gobinary. Run both for complete coverage.
DHI: Distroless Philosophy with a Full Signed Attestation Stack
Docker Hardened Images are security-focused base images published at dhi.io. The base tier went Apache 2.0 open source in December 2025 - free to pull without a subscription. The enterprise tier (FIPS 140-2/3, DISA STIG, Extended Lifecycle Support, custom hardening) requires a paid plan. The sfw-ent-dev tag we tested is enterprise: sfw = software supply chain FIPS hardening, ent = enterprise grade.
The core hardening philosophy is ruthless package minimization. The production node:22-debian13 delivers a complete Node.js 22 runtime in 19 packages and 42 MB. The official Docker Hub node:22 image has dozens of CVEs in hundreds of packages. The hardened version eliminates them not by patching but by removing the packages entirely.
The Seven Attestation Layers Shipped With Every DHI Image
Every DHI image ships a complete OCI attestation stack embedded in the manifest - signed JSON documents that any tooling can query programmatically, not just documentation:
| Attestation Type | Standard / Predicate URI | What it proves |
|---|---|---|
| SLSA Provenance | https://slsa.dev/provenance/v0.2 | Cryptographic build chain linking every image to its exact source commit in github.com/docker-hardened-images/definitions. Builder identity, reproducibility proof. |
| CycloneDX SBOM | https://cyclonedx.org/bom/v1.6 | Full package inventory with versions, licenses, and dependencies in CycloneDX format. |
| SPDX SBOM | https://spdx.dev/Document | Same bill of materials in SPDX format for toolchain compatibility. |
| OpenVEX ← the key one | https://openvex.dev/ns/v0.2.0 | Machine-readable CVE exploitability statements — not_affected with signed justification codes, or affected for real findings. This is what makes Scout show 0. |
| Scout Vulnerability Scan | https://scout.docker.com/vulnerabilities | Docker’s own scan result at build time. Scout compares against this at runtime. |
| FIPS Compliance | https://docker.com/dhi/fips/v0.1 | FIPS 140-2/3 certification evidence. Enterprise FIPS images only. |
| STIG Scan | https://docker.com/dhi/stig/v0.1 | DISA STIG assessment with signed OpenSCAP results. Enterprise STIG images only. |
Inspect DHI attestations — list all signed claims, export VEX document
# List all attestation predicates on any DHI image
docker scout attestation list dhi.io/node:22-debian13
# Export the OpenVEX document — every suppression decision, signed + timestamped
docker scout vex get dhi.io/node:22-debian13 --output vex.json
# Inspect each CVE's status and justification code
cat vex.json | jq '.statements[] | {
cve: .vulnerability.id,
status: .status,
justification: .justification,
timestamp: .timestamp
}'
# Example output for the HIGH glibc CVE:
{
"cve": "CVE-2026-0861",
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"timestamp": "2026-02-18T09:14:22Z"
}

Production vs -dev Variants: The Package Count is the Security Story

What VEX Is, How It Works, Why Both Tools Need It
VEX - Vulnerability Exploitability eXchange - is a CISA-backed open standard for machine-readable CVE exploitability statements per image. The problem it solves: a CVE existing in a package tells you nothing about whether it is exploitable in your specific container. A vulnerable function compiled out, needing an attack path unavailable in a distroless runtime, or requiring kernel access containers don’t have - none of these are real risks regardless of what NVD says.
VEX lets image vendors attach signed, timestamped, auditable statements documenting exactly why each CVE doesn’t apply. Scanners that consume VEX filter to show only genuinely exploitable findings. Result: instead of an analyst spending 4 hours triaging 62 CVEs, they spend 20 minutes on 9.

VEX Justification Codes — What “not_affected” Actually Means
VEX statements must include a machine-readable justification code from the OpenVEX spec. Here are the codes you will see in DHI VEX documents:

Two VEX Delivery Channels: OCI Attestation vs Advisory Repository

These are the same underlying OpenVEX data published through two channels that serve different scanner architectures. The 1-CVE discrepancy we see on traefik (8 vs 9 CVEs) is because zlib CVE-2026-27171 has a not_affected entry in the OCI attestation but the advisory repo hasn’t published that entry yet - content lag, not a correctness issue.
Official 3-Step Setup — One Time, Then All Scans Work
This is the step missing from every third-party DHI scanning tutorial I found. Do it once on your machine or bake it into your CI runner image, and every subsequent scan applies DHI VEX automatically.
Initialize the Trivy VEX repository config
Creates ~/.trivy/vex/repository.yaml with the default Aqua VEX Hub entry. If the file already exists, prints INFO The configuration file already exists - fine, proceed to step 2.
Add Docker’s DHI advisory repository to the config
Edit ~/.trivy/vex/repository.yaml to add github.com/docker-hardened-images/advisories with enabled: true. This is the step everyone misses. Without it, --vex repo queries only Aqua’s hub which has zero entries for DHI images.
Download VEX data and scan
Run trivy vex repo download to pull Docker’s advisory entries locally. Then scan with --vex repo. Trivy caches and logs No need to check repository updates on subsequent runs.
# ─── STEP 1 ─────────────────────────────────────────────────────────────────
trivy vex repo init
# New install: INFO [vex] The default repository config has been created
# Already exists: INFO [vex] The configuration file already exists
# Both are fine. Proceed to step 2.
# ─── STEP 2 ─────────────────────────────────────────────────────────────────
# Edit ~/.trivy/vex/repository.yaml — add the Docker DHI advisory source:
repositories:
- name: dhi-vex
url: https://github.com/docker-hardened-images/advisories
enabled: true
# Or via heredoc (idempotent — safe to run in CI on every job):
grep -q "docker-hardened-images" ~/.trivy/vex/repository.yaml || \
cat >> ~/.trivy/vex/repository.yaml << 'EOF'
- name: dhi-vex
url: https://github.com/docker-hardened-images/advisories
enabled: true
EOF
# ─── STEP 3 ─────────────────────────────────────────────────────────────────
trivy vex repo download
# First run: INFO [vex] Updating repository... repo="dhi-vex"
# Cached: INFO [vex] No need to check repository updates repo="dhi-vex"
# ─── NOW SCAN ────────────────────────────────────────────────────────────────
trivy image --scanners vuln --vex repo dhi.io/node:22-debian13
trivy image --scanners vuln --vex repo dhi.io/nginx:1-debian13-dev
trivy image --scanners vuln --vex repo dhi.io/python:3-debian13-sfw-ent-dev
trivy image --scanners vuln --vex repo dhi.io/traefik:3-debian13-dev
# ─── AUDIT TRAIL — see every suppressed CVE ──────────────────────────────────
trivy image --scanners vuln --vex repo --show-suppressed dhi.io/traefik:3-debian13-dev

Info. --show-suppressed is your compliance audit trail After DHI VEX is configured, Trivy will print: INFO Some vulnerabilities have been ignored/suppressed. Use the "--show-suppressed" flag to display them. Add --show-suppressed to see every CVE that was filtered by VEX along with the source advisory entry and status code. Essential for SOC2, ISO27001, or CRA compliance documentation - you can prove exactly which CVEs were assessed and why they were dismissed.
node:22-debian13 — Scout 0, Trivy 10 → 0 After Setup
The hardened Node.js 22 production image is the clearest story. Scout shows 0. Trivy shows 10 without VEX context and 0 after correct setup. Every single one of those 10 CVEs lives in exactly one package - libc6. Not Node.js. Not npm. Not any application code. Just glibc.
terminal
$ docker scout cves dhi.io/node:22-debian13
✓ SBOM obtained from attestation, 19 packages found
✓ Provenance obtained from attestation
✓ VEX statements obtained from attestation ← reads OCI manifest layer, zero config needed
✓ No vulnerable package detected
│ Analyzed Image
────────────────┼──────────────────────────────────────
Target │ dhi.io/node:22-debian13
digest │ sha256:a34b9c1d7f2e...
platform │ linux/amd64
provenance │ github.com/docker-hardened-images/definitions
vulnerabilities│ 0C 0H 0M 0L
size │ 42 MB
packages │ 19 ← full Node.js 22 runtime in just 19 packages

The third line is the entire story: ✓ VEX statements obtained from attestation. Scout pulled the signed OpenVEX document from the OCI manifest, applied all not_affected statements for the glibc CVEs, and reported the post-suppression result. The 0 is not hiding CVEs - it is applying a cryptographically signed, auditable security assessment you can inspect yourself with docker scout vex get.
$ trivy image --scanners vuln dhi.io/node:22-debian13
INFO Detected OS family="debian" version="13.3"
INFO [debian] Detecting vulnerabilities... pkg_num=12
WARN Using severities from other vendors for some vulnerabilities.
Total: 10 (UNKNOWN: 0, LOW: 7, MEDIUM: 2, HIGH: 1, CRITICAL: 0)
│ libc6 │ CVE-2026-0861 │ HIGH │ glibc: Integer overflow in memalign │
│ │ CVE-2025-15281 │ MEDIUM │ glibc: wordexp WRDE_REUSE|WRDE_APPEND uninit │
│ │ CVE-2026-0915 │ MEDIUM │ glibc: info disclosure via network query │
│ │ CVE-2010-4756 │ LOW │ glibc: glob CPU/memory exhaustion (wont-fix) │
│ │ CVE-2018-20796 │ LOW │ glibc: regex uncontrolled recursion │
│ │ CVE-2019-1010022 │ LOW │ glibc: stack guard page bypass │
│ │ CVE-2019-1010023 │ LOW │ glibc: ldd execution on malicious ELF │
│ │ CVE-2019-1010024 │ LOW │ glibc: ASLR bypass via thread cache │
│ │ CVE-2019-1010025 │ LOW │ glibc: heap address info disclosure │
│ │ CVE-2019-9192 │ LOW │ glibc: regex deep recursion (same as 20796) │

$ trivy image --scanners vuln --vex repo dhi.io/node:22-debian13
INFO [vex] No need to check repository updates repo="default" ← Aqua hub only, zero DHI entries
INFO Detected OS family="debian" version="13.3" pkg_num=12
Total: 10 (UNKNOWN: 0, LOW: 7, MEDIUM: 2, HIGH: 1, CRITICAL: 0)
# Identical to no-VEX scan. --vex repo flag did nothing. Expected — no DHI data in Aqua hub.

$ trivy image --scanners vuln --vex repo dhi.io/node:22-debian13
INFO [vex] No need to check repository updates repo="dhi-vex" ← DHI advisory repo active
INFO Detected OS family="debian" version="13.3" pkg_num=12
INFO Some vulnerabilities have been ignored/suppressed. Use the "--show-suppressed" flag.
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
# All 10 glibc CVEs suppressed by DHI VEX. Matches Docker Scout result.
# With --show-suppressed you can see all 10 suppressed entries:
$ trivy image --scanners vuln --vex repo --show-suppressed dhi.io/node:22-debian13
│ libc6 │ CVE-2026-0861 │ HIGH │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2025-15281 │ MEDIUM │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2026-0915 │ MEDIUM │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2010-4756 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2018-20796 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2019-1010022 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2019-1010023 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2019-1010024 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2019-1010025 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │
│ │ CVE-2019-9192 │ LOW │ not_affected │ vulnerable_code_not_in_execute_path │

nginx-dev — Scout 1 Real CVE, Trivy 60 → 9 After Fix
The nginx dev image is where the story gets more interesting. 107 packages. Full shell. The entire Debian system toolset. Scout applies VEX and still reports 1 CVE — because the VEX status on that CVE is affected, not not_affected. Docker deliberately didn’t suppress it. It’s genuine.
$ docker scout cves dhi.io/nginx:1-debian13-dev
✓ SBOM obtained from attestation, 107 packages found
✓ Provenance obtained from attestation
✓ VEX statements obtained from attestation
✗ Detected 1 vulnerable package with 1 vulnerability
│ Analyzed Image
Target │ dhi.io/nginx:1-debian13-dev
digest │ d2dcc93f95bc
platform │ linux/amd64
provenance │ github.com/docker-hardened-images/definitions
│ 1116d8212377c9efec79facff5e0c9066a2e6b13
vulnerabilities│ 0C 0H 0M 1L
size │ 26 MB
packages │ 107
## Packages and Vulnerabilities
0C 0H 0M 1L util-linux 2.41-5
pkg:deb/debian/util-linux@2.41-5
✗ LOW CVE-2025-14104
https://scout.docker.com/v/CVE-2025-14104
Affected range : <=2.41-5
Fixed version : not fixed

VEX was applied. Everything suppressable was suppressed. One CVE survived: CVE-2025-14104 in util-linux 2.41-5, VEX status affected. This is exactly what the scanner should do — it is not hiding it, it is surfacing it. The -dev image includes the full util-linux binary set and the vulnerable setpwnam() code path is reachable.
─── METHOD 1: No VEX (or --vex repo with default Aqua hub) ──────────────────
$ trivy image --scanners vuln dhi.io/nginx:1-debian13-dev
INFO Detected OS family="debian" version="13.3" pkg_num=71
Total: 60 (UNKNOWN: 1, LOW: 51, MEDIUM: 6, HIGH: 2, CRITICAL: 0)
│ libc-bin │ CVE-2026-0861 │ HIGH │ glibc memalign integer overflow │
│ libc6 │ CVE-2026-0861 │ HIGH │ (same CVE — libc6 package) │
│ libc6 │ CVE-2025-15281 │ MEDIUM │ glibc wordexp uninit memory │
│ libsqlite3-0 │ CVE-2025-7709 │ MEDIUM │ SQLite FTS5 integer overflow │
│ zlib1g │ CVE-2026-27171 │ MEDIUM │ zlib DoS CRC32 infinite loop │
│ util-linux │ CVE-2025-14104 │ LOW │ setpwnam() heap buffer overread │
│ libblkid1 │ CVE-2025-14104 │ LOW │ (same CVE — sub-package) │
│ libmount1 │ CVE-2025-14104 │ LOW │ (same CVE — sub-package) │
│ libuuid1 │ CVE-2025-14104 │ LOW │ (same CVE — sub-package) │
│ tar │ CVE-2005-2541 │ LOW │ 21yr old Debian wont-fix │
│ ... 50 more LOW CVEs across glibc (×7), systemd libs (×6), apt, perl-base... │
─── METHOD 2: --vex vex.json (OCI attestation exported) ─────────────────────
$ docker scout vex get dhi.io/nginx:1-debian13-dev --output vex.json
✓ Found 1 VEX attestation for image → vex.json written
$ trivy image --scanners vuln --vex vex.json dhi.io/nginx:1-debian13-dev
Total: 9 (UNKNOWN: 0, LOW: 9, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
# zlib CVE-2026-27171 SUPPRESSED here — OCI VEX has not_affected for it
─── METHOD 3: --vex repo (DHI advisory repo configured) ─────────────────────
$ trivy image --scanners vuln --vex repo dhi.io/nginx:1-debian13-dev
INFO [vex] No need to check repository updates repo="dhi-vex"
Total: 9 (UNKNOWN: 0, LOW: 8, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
│ bash │ TEMP-0841856-B18BAF │ LOW │ Debian TEMP priv esc │
│ libblkid1 │ CVE-2025-14104 │ LOW │ util-linux setpwnam() │
│ liblastlog2-2 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libmount1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libsmartcols1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libuuid1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ tar │ TEMP-0290435-0B57B5 │ LOW │ tar rmt side effects TEMP │
│ util-linux │ CVE-2025-14104 │ LOW │ (source package) │
│ zlib1g │ CVE-2026-27171 │ MEDIUM │ zlib DoS CRC32 (advisory hub open) │

Python Enterprise Dev — Scout Finds CVEs Trivy Misses, 62 → 9 After Setup
This image produces the most instructive comparison because it exposes a blind spot in Trivy, not just a configuration gap.
$ docker scout cves dhi.io/python:3-debian13-sfw-ent-dev
✓ SBOM obtained from attestation, 104 packages found
✓ Provenance obtained from attestation
✓ VEX statements obtained from attestation
✗ Detected 2 vulnerable packages with a total of 3 vulnerabilities
vulnerabilities│ 0C 0H 2M 1L
size │ 74 MB packages │ 104
digest │ 9cfcdf3957aa
provenance │ github.com/docker-hardened-images/definitions
│ 4c9767819e2e7647999e29d36a6e2d26d1b49fd2
## Package 1: python 3.14.3 (pkg:dhi/python@3.14.3)
✗ MEDIUM CVE-2025-15367
Affected range : <3.15.0-alpha6 Fixed version: 3.15.0-alpha6
✗ MEDIUM CVE-2025-15366
Affected range : <3.15.0-alpha6 Fixed version: 3.15.0-alpha6
## Package 2: util-linux 2.41-5
✗ LOW CVE-2025-14104
Affected range : <=2.41-5 Fixed version: not fixed

$ trivy image --scanners vuln dhi.io/python:3-debian13-sfw-ent-dev
INFO Detected OS family="debian" version="13.3" pkg_num=71
INFO [python-pkg] Detecting vulnerabilities...
# pip-26.0.1.dist-info/METADATA → 0 vulnerabilities (Trivy scans pip packages, not the interpreter)
Total: 62 (UNKNOWN: 1, LOW: 53, MEDIUM: 6, HIGH: 2, CRITICAL: 0)
# No Python CVEs. 62 CVEs are all OS packages. Same as the --vex repo with Aqua hub.

$ trivy image --scanners vuln --vex repo dhi.io/python:3-debian13-sfw-ent-dev
INFO [vex] No need to check repository updates repo="dhi-vex" ← DHI repo active
INFO Detected OS family="debian" version="13.3" pkg_num=71
INFO Some vulnerabilities have been ignored/suppressed. ← VEX working — 53 CVEs suppressed
Total: 9 (UNKNOWN: 0, LOW: 8, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
# 62 → 9 — VEX suppressed 53 CVEs
│ bash │ TEMP-0841856-B18BAF │ LOW │ bash priv esc (Debian TEMP) │
│ libblkid1 │ CVE-2025-14104 │ LOW │ util-linux setpwnam() heap overread │
│ liblastlog2-2 │ CVE-2025-14104 │ LOW │ (util-linux sub-package) │
│ libmount1 │ CVE-2025-14104 │ LOW │ (util-linux sub-package) │
│ libsmartcols1 │ CVE-2025-14104 │ LOW │ (util-linux sub-package) │
│ libuuid1 │ CVE-2025-14104 │ LOW │ (util-linux sub-package) │
│ tar │ TEMP-0290435-0B57B5 │ LOW │ tar rmt side effects (Debian TEMP) │
│ util-linux │ CVE-2025-14104 │ LOW │ (source package) │
│ zlib1g │ CVE-2026-27171 │ MEDIUM │ zlib DoS CRC32 infinite loop │
# Note: CVE-2025-15367 and CVE-2025-15366 (Python interpreter) not in Trivy's vuln DB at all

Info. Trivy misses the 2 Python 3.14.3 CVEs that Scout correctly catches CVE-2025-15367 and CVE-2025-15366 are vulnerabilities in the Python 3.14.3 interpreter binary itself, fixed in 3.15.0-alpha6. Scout surfaces them via its intelligence database and the DHI SBOM which tracks pkg:dhi/python@3.14.3. Trivy's python-pkg scanner targets installed packages from pip/poetry/requirements — it scans pip-26.0.1.dist-info/METADATA which reports 0 CVEs because pip itself is clean. The interpreter binary is out of scope for that scanner. This is the fundamental reason to run both tools: complementary database coverage finds different things.
Traefik Dev — 554 Packages, Two Trivy Methods, 1-CVE Gap Explained
The traefik dev image is the most complex case: 554 packages, the traefik binary scanned as a Go binary via gobinary, and two Trivy VEX methods giving slightly different results revealing a content lag in the advisory hub.
$ docker scout cves dhi.io/traefik:3-debian13-dev
✓ SBOM obtained from attestation, 554 packages found
✓ Provenance obtained from attestation
✓ VEX statements obtained from attestation
✗ Detected 1 vulnerable package with 1 vulnerability
vulnerabilities│ 0C 0H 0M 1L
size │ 108 MB packages │ 554
digest │ 0d0cc3b053b1
provenance │ 590b46daa696447f3d70a096e46e516257a844ed
0C 0H 0M 1L util-linux 2.41-5
pkg:deb/debian/util-linux@2.41-5
✗ LOW CVE-2025-14104 Fixed version: not fixed
# traefik binary (usr/local/bin/traefik — gobinary): 0 CVEs — Go binary is clean

$ docker scout vex get dhi.io/traefik:3-debian13-dev --output vex.json
✓ SBOM obtained from attestation, 554 packages found
✓ Found 1 VEX attestation for image
✓ Report written to vex.json
$ trivy image --scanners vuln --vex vex.json dhi.io/traefik:3-debian13-dev
INFO Detected OS family="debian" version="13.2" pkg_num=67
INFO [gobinary] Detecting vulnerabilities...
INFO Some vulnerabilities have been ignored/suppressed. ← OCI VEX file applied
Total: 8 (UNKNOWN: 0, LOW: 8, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
# Report Summary: usr/local/bin/traefik (gobinary): 0 CVEs ← Go binary is clean
│ bash │ TEMP-0841856-B18BAF │ LOW │ bash priv esc Debian TEMP │
│ libblkid1 │ CVE-2025-14104 │ LOW │ util-linux setpwnam() │
│ liblastlog2-2 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libmount1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libsmartcols1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libuuid1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ tar │ TEMP-0290435-0B57B5 │ LOW │ tar rmt side effects TEMP │
│ util-linux │ CVE-2025-14104 │ LOW │ (source package) │
# zlib1g CVE-2026-27171 ← SUPPRESSED — OCI VEX marks it not_affected

terminal
$ trivy vex repo download
INFO [vex] Updating repository... repo="dhi-vex"
url="https://github.com/docker-hardened-images/advisories"
$ trivy image --scanners vuln --vex repo dhi.io/traefik:3-debian13-dev
INFO [vex] No need to check repository updates repo="dhi-vex"
INFO Some vulnerabilities have been ignored/suppressed.
Total: 9 (UNKNOWN: 0, LOW: 8, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
# usr/local/bin/traefik (gobinary): 0 CVEs
│ bash │ TEMP-0841856-B18BAF │ LOW │ bash priv esc TEMP │
│ libblkid1 │ CVE-2025-14104 │ LOW │ util-linux setpwnam() │
│ liblastlog2-2 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libmount1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libsmartcols1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ libuuid1 │ CVE-2025-14104 │ LOW │ (sub-package) │
│ tar │ TEMP-0290435-0B57B5 │ LOW │ tar rmt TEMP │
│ util-linux │ CVE-2025-14104 │ LOW │ (source package) │
│ zlib1g │ CVE-2026-27171 │ MEDIUM │ zlib DoS CRC32 (advisory repo open) │
# ↑ 1 extra vs OCI VEX method: advisory repo hasn't published not_affected for zlib yet

Info. 🔬 8 vs 9 explained: OCI VEX is ahead of the advisory repository The OCI-embedded VEX (read by Scout, exportable via docker scout vex get) marks zlib CVE-2026-27171 as not_affected - Docker assessed it at image build time and embedded the decision. The DHI advisory repository at github.com/docker-hardened-images/advisories publishes entries asynchronously and hasn't published this specific entry yet. Both are Docker-authored data. It is content lag, not a correctness difference. For conservative scanning use --vex repo. For the most complete VEX suppression including build-time assessments, use --vex vex.json.
The 7 Remaining CVEs: What Each One Is and What to Do

Scout vs Trivy vs Grype: Complete DHI Support Comparison

Recommendation: Scout + Trivy together for DHI
Scout finds application-layer CVEs that Trivy misses (Python 3.14.3 interpreter CVEs). Trivy scans Go binaries comprehensively, supports IaC/secrets scanning, and works fully air-gapped after initial setup. After the one-time DHI repo config, both tools produce consistent OS package results. The combination covers blind spots neither tool has alone.
Production-Ready GitHub Actions Pipeline for DHI Scanning
name: DHI Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
# Cache Trivy VEX data — avoids GitHub API rate limits per run
- name: Cache Trivy VEX data
uses: actions/cache@v4
with:
path: ~/.trivy/vex
key: trivy-vex-${{ runner.os }}-${{ env.CACHE_WEEK }}
env:
CACHE_WEEK: ${{ steps.date.outputs.week }}
# One-time Trivy DHI VEX setup (idempotent — safe in every run)
- name: Setup Trivy DHI VEX configuration
run: |
trivy vex repo init
grep -q "docker-hardened-images" ~/.trivy/vex/repository.yaml || \
cat >> ~/.trivy/vex/repository.yaml << 'EOF'
- name: dhi-vex
url: https://github.com/docker-hardened-images/advisories
enabled: true
EOF
trivy vex repo download
# Hardened production image — fail pipeline on any HIGH/CRITICAL after VEX
- name: Scan hardened image (production gate)
run: |
trivy image \
--scanners vuln \
--vex repo \
--exit-code 1 \
--severity HIGH,CRITICAL \
--show-suppressed \ # log suppressed CVEs for audit trail
dhi.io/node:22-debian13
# Scout scan — catches Python interpreter CVEs Trivy misses
- name: Docker Scout scan (application layer coverage)
uses: docker/scout-action@v1
with:
command: cves
image: dhi.io/python:3-debian13-sfw-ent-dev
only-severities: critical,high,medium
exit-code: true
# Dev images — fail only on HIGH/CRITICAL, accept known LOWs
# Known: CVE-2025-14104 util-linux LOW (no fix), bash/tar TEMPs
- name: Scan dev image (informational)
run: |
trivy image \
--scanners vuln \
--vex repo \
--exit-code 1 \
--severity HIGH,CRITICAL \
--show-suppressed \
dhi.io/traefik:3-debian13-dev
Every Command From This Post — Copy-Paste Ready
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ONE-TIME SETUP — Trivy with DHI advisory repository (official)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
trivy vex repo init
# Add to ~/.trivy/vex/repository.yaml:
- name: dhi-vex
url: https://github.com/docker-hardened-images/advisories
enabled: true
trivy vex repo download
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DOCKER SCOUT — zero config, reads OCI attestation automatically
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker scout cves dhi.io/node:22-debian13
docker scout cves dhi.io/nginx:1-debian13-dev
docker scout cves dhi.io/python:3-debian13-sfw-ent-dev
docker scout cves dhi.io/traefik:3-debian13-dev
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TRIVY — after DHI repo configured above
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
trivy image --scanners vuln --vex repo dhi.io/node:22-debian13
trivy image --scanners vuln --vex repo dhi.io/nginx:1-debian13-dev
trivy image --scanners vuln --vex repo dhi.io/python:3-debian13-sfw-ent-dev
trivy image --scanners vuln --vex repo dhi.io/traefik:3-debian13-dev
trivy image --scanners vuln --vex repo --show-suppressed dhi.io/traefik:3-debian13-dev
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OCI VEX FILE — air-gapped / per-image approach
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker scout vex get dhi.io/traefik:3-debian13-dev --output vex.json
trivy image --scanners vuln --vex vex.json dhi.io/traefik:3-debian13-dev
grype dhi.io/traefik:3-debian13-dev --vex vex.json
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
AUDIT — inspect every suppression decision
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker scout attestation list dhi.io/node:22-debian13
docker scout vex get dhi.io/node:22-debian13 --output vex.json
cat vex.json | jq '.statements[] | {id:.vulnerability.id,status,justification}'
# Browse: https://github.com/docker-hardened-images/advisories

Comments (0)
No comments yet. Be the first to share your thoughts.
Leave a comment