Free, open source VEX resolution service.
Aggregates vendor VEX statements from CSAF 2.0, OVAL, and OpenVEX feeds, translates across identifier schemes (PURL ↔ CPE), and serves the result via HTTP API.
Web UI · API vex.getreel.dev · API reference
Vulnerability scanners produce long lists of CVEs. Many of those CVEs don't actually affect you — the vendor already confirmed the vulnerable code isn't present, a fix is available, or it's still under investigation. This information is published as VEX (Vulnerability Exploitability eXchange) statements. Red Hat and SUSE publish CSAF 2.0 JSON; Red Hat, Ubuntu, and Debian publish OVAL XML; Canonical also publishes OpenVEX 0.2.0 covering Ubuntu releases beyond what their OVAL feed surfaces. The same vendor often publishes multiple formats with intentionally different coverage between them.
reel-vex pulls from all three formats, normalizes the statements into one database, and bridges identifier schemes so a scanner querying with a package PURL matches statements vendors published against a platform CPE. One query, unified answer.
config.yaml
│
▼
┌──────────────────────────────────────────────────────────┐
│ Ingest Pipeline │
│ │
│ Adapters Alias fetchers │
│ ──────── ────────────── │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ CSAF │ │ repo → CPE │ │
│ │ (RH, SUSE) │ │ (Red Hat) │ │
│ └──────┬──────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────┴──────┐ │ │
│ │ OVAL │ │ │
│ │ (RH, Ubuntu,│ │ │
│ │ Debian) │ │ │
│ └──────┬──────┘ │ │
│ │ │ │
│ ┌──────┴──────┐ │ │
│ │ OpenVEX │ │ │
│ │ (Ubuntu) │ │ │
│ └──────┬──────┘ │ │
│ │ │ │
│ ▼ ▼ │
│ statements + adapter_state product_aliases │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ SQLite vex.db │
│ statements │ vendors │ product_aliases │ adapter_state │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ HTTP API │
│ │
│ POST /v1/statements (CVE / product / vendor / │
│ status / since filters) │
│ POST /v1/analyze (annotated SBOM + user VEX) │
│ GET /v1/stats │
│ GET /v1/ingest (status) · POST /v1/ingest │
│ GET /healthz │
│ │
│ Resolver: PURL ↔ CPE translation + CPE prefix match │
└──────────────────────────────────────────────────────────┘
Single Go binary. Single SQLite file. No external dependencies at runtime.
For deeper reading: the ingest pipeline and project layout live in docs/architecture.md, the database schema in docs/data-model.md, and the field-level API reference (every endpoint, every enum value, OpenVEX 0.2.0 output) in docs/api.md.
| Vendor | Format | Feed | Documents | Identifiers |
|---|---|---|---|---|
| Red Hat | CSAF VEX | security.access.redhat.com/data/csaf/v2/vex/ | ~313K | PURL + CPE |
| Red Hat | OVAL | security.access.redhat.com/data/oval/v2/ | 1 per stream (EUS/AUS/E4S/…) | CPE (incl. stream variants) |
| SUSE | CSAF VEX | ftp.suse.com/pub/projects/security/csaf-vex/ | ~54K | CPE |
| Ubuntu | OpenVEX 0.2.0 | security-metadata.canonical.com/vex/ | ~54K (per-CVE in vex-all.tar.xz) |
PURL (pkg:deb/ubuntu/<name>?distro=ubuntu-<v>) |
| Ubuntu | OVAL | security-metadata.canonical.com/oval/ | 1 per LTS release (focal / jammy / noble) | PURL (pkg:deb/ubuntu/<name>?distro=ubuntu-<v>) |
| Debian | OVAL | www.debian.org/security/oval/ | 1 per release (bullseye / bookworm / trixie) | PURL (pkg:deb/debian/<name>?distro=debian-<n>) |
Ubuntu has dual sources by design. Canonical's OpenVEX feed is broad and includes pre-USN triage; their OVAL feed covers ~10% of identifier shapes the OpenVEX feed expresses under different naming conventions (versioned binary packages like golang-1.20-go, source vs binary package families). The two are not strict supersets — keeping both gives consumers the union.
Alias sources:
| Vendor | File | Purpose |
|---|---|---|
| Red Hat | repository-to-cpe.json | Maps RPM repository_id qualifiers → platform CPEs |
config.yaml has two top-level lists, adapters: and aliases::
adapters:
- type: csaf
id: redhat
name: Red Hat
url: https://security.access.redhat.com/data/csaf/v2/provider-metadata.json
- type: csaf
id: suse
name: SUSE
url: https://www.suse.com/.well-known/csaf/provider-metadata.json
- type: redhat-oval
id: redhat-oval-rhel-9.6-eus
url: https://security.access.redhat.com/data/oval/v2/RHEL9/rhel-9.6-eus.oval.xml.bz2
- type: ubuntu-vex
id: ubuntu-vex
name: Ubuntu (OpenVEX)
url: https://security-metadata.canonical.com/vex/vex-all.tar.xz
- type: ubuntu-oval
id: ubuntu-oval-noble
name: Ubuntu 24.04 LTS
url: https://security-metadata.canonical.com/oval/com.ubuntu.noble.usn.oval.xml.bz2
- type: debian-oval
id: debian-oval-bookworm
name: Debian 12 (bookworm)
url: https://www.debian.org/security/oval/oval-definitions-bookworm.xml.bz2
aliases:
- type: redhat-repository-to-cpe
id: redhat
# url: defaults to security.access.redhat.com/data/metrics/repository-to-cpe.jsonAdapter types currently registered:
csaf— any CSAF 2.0 provider (generic)redhat-oval— Red Hat OVAL (one URL per adapter entry)ubuntu-vex— Canonical OpenVEX 0.2.0 (single tarball atvex-all.tar.xz)ubuntu-oval— Canonical USN OVAL (one URL per LTS release)debian-oval— Debian Security Tracker OVAL (one URL per release)
Alias fetcher types currently registered:
redhat-repository-to-cpe— Red Hat's repository → CPE mapping
Each adapter id must be unique across the config (used as the watermark key in adapter_state); the vendor written onto statements comes from Adapter.Vendor(). For CSAF adapters, Vendor() returns the same value as id. For OVAL adapters, Vendor() returns the canonical vendor name (redhat, ubuntu, debian) regardless of which OVAL file the adapter targets, so all statements from one vendor live under one vendor string and are distinguished by source_format and (for deb-shaped products) the ?distro= qualifier on product_id.
At query time, user-supplied product identifiers get expanded into candidate base IDs that are matched against statements.base_id. Three expansion rules apply:
- Direct: the base form of the input (PURL stripped of
@version+ qualifiers; CPE as-is). - via_alias: for PURLs carrying a
repository_id=qualifier, the CPEs stored inproduct_aliasesfor that repository. - via_cpe_prefix: for CPE inputs, the first 5 CPE 2.2 URI parts (
part:vendor:product:version:update) with trailing variants dropped. Implements Red Hat's SECDATA-1220 matching contract.
Each returned statement carries a match_reason field (direct, via_alias, via_cpe_prefix) so consumers can see which rule fired. Stronger reasons win when the same candidate is produced by multiple rules.
Base URL: https://vex.getreel.dev. All VEX-statement-emitting endpoints return OpenVEX 0.2.0. Full field-level reference — every endpoint, every enum value, user-VEX merge semantics — lives in docs/api.md.
Quick batch query:
curl -X POST https://vex.getreel.dev/v1/statements \
-H "Content-Type: application/json" \
-d '{
"cves": ["CVE-2021-44228"],
"products": ["pkg:rpm/redhat/log4j@2.14.0?repository_id=rhel-8-for-x86_64-appstream-rpms"]
}'{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://openvex.dev/docs/public/vex-...",
"author": "reel-vex aggregator <vex@getreel.dev>",
"role": "aggregator",
"version": 1,
"statements": [
{
"vulnerability": {"name": "CVE-2021-44228"},
"products": [{"@id": "pkg:rpm/redhat/log4j", "identifiers": {"purl": "pkg:rpm/redhat/log4j"}}],
"status": "not_affected",
"status_notes": "source_format=csaf; match_reason=via_alias",
"justification": "vulnerable_code_not_present",
"supplier": "redhat"
}
]
}status_notes carries source_format= (which feed) and match_reason= (which rule fired) for diagnostic traceability without inventing custom OpenVEX fields. See docs/api.md for /v1/statements filter shapes, /v1/analyze (SBOM annotation + user-VEX merge), /v1/stats, /v1/ingest, and the full field reference.
Prebuilt images are published to Docker Hub on every release, scanned for vulnerabilities before publishing (see .github/workflows/release.yml):
getreel/vex-hub:<version>— pinned to a specific release taggetreel/vex-hub:v0— latest in the 0.x seriesgetreel/vex-hub:latest— latest release
Minimal run:
docker run -d \
--name vex-hub \
--restart unless-stopped \
-p 8080:8080 \
-v $(pwd)/data:/data \
-v $(pwd)/config.yaml:/config.yaml:ro \
getreel/vex-hub:latest \
-db /data/vex.db \
-config /config.yaml \
-admin-token your-secret-token \
serveThe SQLite database lives in the mounted data/ directory so it survives container restarts. First boot runs a full ingest (hours for Red Hat's CSAF; minutes for OVAL adapters); subsequent scheduled runs are incremental.
go build -o reel-vex ./cmd/server
./reel-vex -config config.yaml -db vex.db serve-addr, -ingest-interval, -admin-token, and -limit are also available; ./reel-vex --help for the full list. ./reel-vex -db vex.db query CVE-2021-44228 and ./reel-vex -db vex.db stats query a local database without starting the server.
# Unit + package tests
go test ./...
# Integration tests (builds binary, seeds DB, hits HTTP)
go test -tags integration ./test/integration/All adapters have httptest-backed tests serving committed fixtures, so the test suite doesn't require network access.
The most impactful contributions:
- New CSAF providers. If a vendor publishes CSAF 2.0 VEX feeds with a
provider-metadata.json+changes.csv, add an entry toconfig.yamlunderadapters:and open a PR. - New OVAL sources. Each new OVAL source means: extending
oval-to-vexwith atranslator.FromXOVAL()for that vendor, plus a new adapter package underpkg/source/xoval/. - New alias mappings. Vendor-published identifier translation files (similar to Red Hat's
repository-to-cpe.json) plug intopkg/aliases/as newFetcherimplementations.
For other formats (upstream .vex/ repos, OCI attestations, OpenVEX files), open an issue to discuss before implementing.
Apache 2.0