VEX Checker
A CLI tool that works with the Red Hat CSAF VEX feed and the Hummingbird catalog API to check CVE statuses and track coverage across Jira, VEX advisories, and container image scan results.
Subcommands
| Subcommand | Purpose |
|---|---|
check |
Look up a single CVE and show Hummingbird product statuses |
reconcile |
Cross-reference Jira CVE tickets with the VEX feed |
catalog |
Cross-reference catalog API CVEs with Jira tickets |
Prerequisites
- Python 3.11+
- Internet access to
security.access.redhat.com(no VPN required) forcheck/reconcile; toapi-hummingbird.hummingbird-project.io(no VPN required) forcatalog JIRA_EMAILAtlassian account email — required forreconcileandcatalogJIRA_TOKENJira API token — required forreconcileandcatalog
check — Single CVE lookup
Fetches the CSAF VEX document for a given CVE and prints the status of all Hummingbird products, filtering out all other RHEL/OpenShift/etc. entries.
check: usage
./check_vex.py check CVE-YYYY-NNNNN [--json]
check: options
| Option | Description |
|---|---|
CVE-ID |
CVE to look up |
--json, -j |
Output results as JSON |
check: examples
$ ./check_vex.py check CVE-2014-8090
CVE-2014-8090 — CVE-2014-8090 ruby: REXML billion laughs attack (Moderate)
hummingbird-1:ruby.src known_affected none_available
hummingbird-1:ruby3.3.src known_affected none_available
hummingbird-1:ruby3.4.src known_affected none_available
hummingbird-1:ruby4.0.src known_affected none_available
$ ./check_vex.py check CVE-2014-8090 --json
{
"cve": "CVE-2014-8090",
"title": "...",
"severity": "Moderate",
"hummingbird": [
{ "product_id": "hummingbird-1:ruby.src", "status": "known_affected", "remediation": "none_available" },
...
]
}
check: exit codes
| Code | Meaning |
|---|---|
0 |
No VEX document found, no Hummingbird products listed, or no known_affected status |
1 |
One or more Hummingbird products are known_affected |
2 |
Invalid CVE ID format |
reconcile — Jira/VEX sync check
Queries the Jira HUM project (Security component) for all CVE tickets, then checks each CVE in the VEX feed. Reports two types of mismatches:
- Open in Jira but not
known_affectedin VEX — VEX says the issue is resolved but the Jira ticket is still open. - Closed in Jira but
known_affectedin VEX — the Jira ticket was closed but Red Hat’s advisory still marks Hummingbird as affected.
Jira tickets are considered closed when their status is one of:
Done, Closed, Won't Fix, Not a Bug.
VEX documents are fetched in parallel (--workers, default 20) so the
command is fast even with hundreds of CVE tickets.
reconcile: usage
export JIRA_EMAIL=you@redhat.com
export JIRA_TOKEN=<your-api-token>
./check_vex.py reconcile [--workers N] [--json]
Credentials are read exclusively from environment variables to avoid exposing them in process listings.
reconcile: options
| Option | Description |
|---|---|
--jira-url |
Jira base URL (default: https://redhat.atlassian.net) |
--workers |
Parallel VEX fetch workers (default: 20) |
--json, -j |
Output results as JSON |
reconcile: example
$ ./check_vex.py reconcile
Fetching Jira tickets... 142 ticket(s)
Fetching VEX statuses for 89 unique CVE(s)...
89/89
2 mismatch(es) found:
HUM-1234 CVE-2025-1234 closed → known_affected (VEX still open)
Jira: https://redhat.atlassian.net/browse/HUM-1234
VEX: https://security.access.redhat.com/data/csaf/v2/vex-feed/2025/cve-2025-1234.json
HUM-5678 CVE-2024-5678 open → fixed (VEX not affected)
Jira: https://redhat.atlassian.net/browse/HUM-5678
VEX: https://security.access.redhat.com/data/csaf/v2/vex-feed/2024/cve-2024-5678.json
reconcile: exit codes
| Code | Meaning |
|---|---|
0 |
No mismatches found |
1 |
One or more mismatches detected |
2 |
Missing JIRA_EMAIL or JIRA_TOKEN |
catalog — Catalog API / Jira coverage check
Fetches all CVEs currently detected in Hummingbird container images (via Grype scan results stored in the catalog API) and cross-references them with Jira HUM Security tickets. Reports CVEs that have no corresponding Jira ticket.
CVEs with a closed Jira ticket are reported separately as expected propagation delay — the typical flow is CVE fix → RPM → image rebuild → Grype DB update, and Jira tickets are closed early while the catalog API reflects the latest Grype scan (updated ~once daily).
Non-CVE vulnerability identifiers (e.g. GHSA-*) from Grype are filtered out
since they are not tracked in Jira.
catalog: usage
export JIRA_EMAIL=you@redhat.com
export JIRA_TOKEN=<your-api-token>
./check_vex.py catalog [--api-url URL] [--workers N] [--json]
Each CVE line shows severity, age since first detection, affected images, and for tracked CVEs the Jira ticket key. Components with known fix versions are shown in brackets when available.
Closed Jira tickets are cross-referenced with the VEX feed to distinguish between different root causes:
- Done-Errata, VEX still affected – fix shipped in RPM but not yet propagated to container images; needs rebuild or lockfile refresh.
- Done-Errata, VEX resolved – fix fully propagated, Grype DB just needs its daily update to stop flagging.
- Won’t Do / Not a Bug, VEX resolved – VEX already marks CVE as not affected; Grype DB delay, will self-resolve.
- Won’t Do / Not a Bug, VEX still affected – genuine known issue that will persist in scans (accepted risk, upstream won’t fix, etc.).
catalog: options
| Option | Description |
|---|---|
--api-url |
Catalog API base URL (default: https://api-hummingbird.hummingbird-project.io/v1) |
--jira-url |
Jira base URL (default: https://redhat.atlassian.net) |
--workers |
Parallel VEX fetch workers (default: 20) |
--json, -j |
Output results as JSON |
catalog: example
$ ./check_vex.py catalog
Catalog: 72 CVEs across 29 images (scanned 2026-04-28T10:45:38Z)
Jira: 748 tickets (437 unique CVEs)
11 untracked CVE(s) (no Jira ticket):
CVE-2008-2662 High 8d ruby (12 tags) [ruby3.3@3.3.10, +36 more]
CVE-2026-2950 Medium 8d aspnet-runtime, ... (30 tags) [lodash@4.17.21]
...
45 tracked CVE(s) open in Jira:
CVE-2025-68114 High 8d php (3 tags) [capstone@5.0.6] HUM-925
...
8 closed in Jira (Done-Errata) but VEX still affected -- verify fix propagated:
CVE-2026-27143 Critical 8d caddy, go-fdo-client, ... (10 tags) HUM-969
4 closed in Jira (Done-Errata), VEX resolved -- Grype DB delay:
CVE-2025-61732 High 8d go, xcaddy (6 tags) HUM-1119
3 closed in Jira (Won't Do / Not a Bug), VEX resolved -- Grype DB delay:
CVE-2026-27140 High 8d caddy, go, ... (10 tags) HUM-967
1 closed in Jira (Won't Do / Not a Bug), VEX still affected -- genuine:
CVE-2025-12781 Medium 8d postgresql, python, ... (17 tags) HUM-1387
catalog: exit codes
| Code | Meaning |
|---|---|
0 |
All catalog CVEs are tracked (have a Jira ticket) |
1 |
One or more untracked CVEs found |
2 |
Missing JIRA_EMAIL or JIRA_TOKEN |
Development
# Run tests
cd vex-checker && python3 -m unittest discover tests -v
# Run linter (from repo root)
ruff check vex-checker
License
This project is licensed under the GNU General Public License v3.0 or later - see the LICENSE file for details.