Intelligent CVE validation for Red Hat container images and OpenShift CoreOS.
Tired of chasing false positives from security scanners? This project delivers a set of AI agent skills and Claude Code commands that give you accurate, context-aware CVE validation for Red Hat container images — backed by official Red Hat security data, not third-party guesswork.
- Validates CVEs against container images using official build-time and release-time SPDX SBOMs from container attestations — no synthetic SBOMs, no third-party tools manipulating the data. If no official SBOM is available, agents can optionally fall back to syft to generate an analyzed SBOM and continue validation on a best-effort basis (analyzed SBOMs have lower quality and accuracy than official build-time or release-time SBOMs and this is clearly noted in the report)
- Checks Red Hat VEX and CSAF files directly from
security.access.redhat.comto determine if a CVE truly affects your image or is a false positive - Verifies CoreOS (RHCOS) in specific OCP releases — extracts the full RPM package list and validates against CVE metadata and VEX data with RHEL EUS stream awareness
- Resolves RHSA advisories when scanners report advisory IDs instead of CVEs
- Provides clear remediation steps including layered product relationships — if a CVE affects an RPM in your container image, a dedicated agent checks whether newer images with the fixed RPM have already been published
- Detects VEX data gaps where Red Hat hasn't assessed a specific component, and recommends reporting to
secalert@redhat.com
Most SCA scanners flag every CVE that matches a package name, producing noisy reports full of false positives. This project does what scanners can't — it reads the actual SBOM attestation, checks Red Hat's official VEX statements, understands product stream versioning (RHEL EUS vs. latest), and tells you whether a reported CVE is a true positive, false positive, or uncertain. When a fix exists, it points you to the exact RHSA advisory and, for container images, checks if a rebuilt image is already available in the registry.
This project is actively evolving. The core validation pipeline is functional and tested against real CVEs and Red Hat images. New features (CoreOS support, RHSA advisory input, VEX discrepancy detection) are being added regularly. Contributions and feedback are welcome.
See REFERENCE.md for a complete list of all skills, commands, scripts, and their options.
Interactive (Claude Code skills & commands) — AI agent skills that run inside Claude Code. You provide a CVE ID and image reference, the agent orchestrates the validation pipeline, and delivers a structured security report. Best for interactive investigation and security review.
Fully automated (CrewAI framework) — A Python-based multi-agent pipeline that runs without human interaction. Designed for CI/CD integration, batch scanning, and automated security workflows. Uses the same helper scripts and validation logic as the interactive approach.
Both approaches use a shared set of helper scripts for deterministic data fetching, with the AI agent handling all interpretation and matching logic.
| Artifact | Path | Invocation |
|---|---|---|
| Skill | Claude/.claude/skills/container-cve-validator/SKILL.md |
Via /skills dialog or natural language request |
| Command | Claude/.claude/commands/container-cve-validator.md |
/container-cve-validator <CVE-ID> <IMAGE_REFERENCE> |
| CoreOS Skill | Claude/.claude/skills/coreos-cve-validator/SKILL.md |
Via /skills dialog or natural language request |
| CoreOS Command | Claude/.claude/commands/coreos-cve-validator.md |
/coreos-cve-validator <CVE-ID> <OCP-VERSION> |
Markdown-based prompt files that instruct Claude to act as a security validator directly inside the Claude CLI. No Python or application code — Claude uses its built-in terminal tool to execute shell commands. The skill and command share identical logic; they differ only in how they receive inputs and how they are triggered.
Skill vs Command:
- The skill is designed for agentic and multi-step workflows where Claude autonomously decides to apply it, or it can be selected via the
/skillsUI dialog. Input is taken from conversation context. - The command is designed for explicit, predictable, user-triggered invocation via
/container-cve-validator. Arguments are parsed directly from the command line ($ARGUMENTS).
| Tool | Purpose |
|---|---|
regctl |
Remote image metadata inspection — ~1.5s per image vs ~5s for skopeo |
cosign |
SBOM extraction (attestation and build-time) |
jq |
JSON parsing |
All tools are verified before execution begins. Missing tools cause an immediate stop with installation guidance.
- CVE ID validated against
CVE-YYYY-NNNNNformat - Image reference parsed into registry, name, tag/digest components
- Image metadata fetched remotely via
regctl image config(no pull needed, ~1.5s vs ~5s for skopeo) to extract:cpe(product CPE),name(public VEX name),vendor,maintainer,org.opencontainers.image.created— labels at.config.Labelsin regctl output.version/releaselabels are intentionally excluded — they contain base image metadata, not the actual container tag - Registry ownership validated via
vendor/maintainerlabels — non-Red Hat images stopped immediately - Mirrored images (e.g., on
quay.io) accepted ifvendor/maintainerconfirms Red Hat; canonicalregistry.redhat.ioreference recorded for VEX matching - Digest-based references resolved to human-readable tags via image labels; digest form retained for SBOM commands
- Queries MITRE CVE API with HTTP status handling (404, non-200, empty
affected) - For Go ecosystem: additionally queries OSV API to resolve
GO-YYYY-NNNNID, then fetches full module-level data fromvuln.go.dev(module path, semver ranges, affected symbols) - GitHub Security Advisories accepted as secondary source only if they carry an official CVE ID
- Attempt A: Release-time attestation SBOM via
cosign download attestation --predicate-type=spdx- Iterates all payload lines, selects SPDX envelope by
predicateType, prefers most recent bycreationInfo.created - Detects image index SBOMs (
SPDXRef-image-index): extractslinux/amd64digest fromchecksumValue/referenceLocatorPURL and re-fetches with architecture-specific digest
- Iterates all payload lines, selects SPDX envelope by
- Attempt B: Build-time SBOM via
cosign download sbom(fallback)- Same image index detection and re-fetch logic applies
- Container tag/digest extraction: reads the
pkg:oci/PURL from the SBOM image entry to obtain the correct tag(s) and digest — one image can have multiple tags. For digest-based input the tag is resolved from the PURL; for tag-based input the digest is collected as additional reference.version/releaselabels are never used for tag reconstruction. - Package matching: PURL-based search across all ecosystems (
pkg:rpm,pkg:golang,pkg:pypi,pkg:npm, etc.); also checks SPDX relationships to identify non-RPM packages linked to parent RPM deliverables - Version comparison: ecosystem-aware (RPM EVR ordering, Go semver, PyPI/npm per CVE
versions[]); three outcomes: vulnerable / patched / inconclusive
- Fetches CSAF VEX from
security.access.redhat.com/data/csaf/v2/vex/[YEAR]/[cve-id].json - Generic blanket detection: VEX files containing only
"All currently supported Red Hat products"undercpe:/a:redhatrepresent an unanalysed placeholder — treated as equivalent to no VEX coverage - Two-level
product_treematching: products identified by CPE, components by PURL inproduct_identification_helper; matched viaproduct_tree.relationships[]- 4a: Exact match on image
cpelabel +namelabel - 4b: Name-only match across CPE versions — surfaces patches available in newer product streams without applying their status to the scanned image
- 4c: No match → potential VEX data gap
- 4a: Exact match on image
- Status extraction from
vulnerabilities[].product_status[]:fixed,known_affected,known_not_affected,under_investigation - Remediation details from
vulnerabilities[].remediations[]: all RHSA advisory URLs forvendor_fix;no_fix_planned/none_availablefor affected products - Not-affected justification from
vulnerabilities[].flags[].label: verbatim flag (e.g.,component_not_present,vulnerable_code_not_present) - Severity from
vulnerabilities[].threats[]: product-scoped first, global fallback, explicitN/Aif absent - Parent RPM patch status (triggered when SBOM identifies a non-RPM package linked to a parent RPM via SPDX relationship):
- Searches VEX
product_tree.relationships[]for the parent RPM across ALL product streams, focusing on RHEL CPE patterns (cpe:/o:redhat:enterprise_linux:*,cpe:/a:redhat:rhel_eus:*, etc.) - If parent RPM is
fixedin RHEL: extracts RHSA advisory URLs and records that the container must be rebuilt to include the patched RPM - Key distinction: a patched RPM in RHEL does NOT mean the container is fixed — the container image must be rebuilt before it is no longer affected
- Searches VEX
Trigger: RPM or parent-RPM vulnerable package AND a fixed RPM version + RHSA release date are known from Step 3. Skipped for non-RPM packages with no parent RPM, or when no fixed version is known.
- Extracts the image repository (strips tag/digest from the scanned image reference)
- Uses
regctl tag ls+xargs -P 15parallel date checks to efficiently find all image tags built after the scanned image'screatedtimestamp — ISO 8601 string comparison, 15 concurrent regctl processes - Filters out
sha*,source, andlatesttags; deduplicates by digest - For each newer candidate: downloads its SBOM and checks whether the fixed RPM version (or higher) is present using RPM EVR ordering
- Compares the candidate image's
cpelabel against the scanned image's CPE:- Same CPE → patched image in the same product stream
- Different CPE → patched image in a newer/different product stream (upgrading changes the product stream)
- Reports all patched images found, or records "no patched image released yet" if none found
Three data gap conditions evaluated before writing the report:
| Condition | Trigger | Status |
|---|---|---|
| A | VEX exists, specific entries present, component fully absent (4c) + package in SBOM + version vulnerable | Potentially vulnerable — Red Hat VEX data gap |
| B | VEX returns 404 + package in SBOM + version vulnerable | Potentially vulnerable — No Red Hat VEX data |
| C | VEX exists but contains only generic blanket statement + package in SBOM + version vulnerable | Potentially vulnerable — Generic Red Hat VEX, CVE not individually reviewed |
All three gap conditions recommend reporting to secalert@redhat.com with CVE ID, image reference, and SBOM evidence.
Both the skill and command support single-scan and batch-scan modes, three output formats, and two verbosity modes:
# Single scan
/container-cve-validator [--format markdown|json|csv] [--output full|summary] <CVE-ID> <IMAGE_REFERENCE>
# Batch scan from CSV file
/container-cve-validator [--format markdown|json|csv] [--output full|summary] --file <PATH>
| Flag | Values | Default | Description |
|---|---|---|---|
--format |
markdown, json, csv |
markdown |
Controls the output serialisation format |
--output |
full, summary |
full |
Controls Markdown verbosity; not applicable to json or csv |
--file |
file path | — | CSV file for batch scanning; mutually exclusive with positional CVE ID and image reference |
--output full— complete Markdown report with all nine sections (default)--output summary— condensed Markdown: executive summary paragraph + numbered recommended actions only--format json— machine-readable JSON object with 6 structured fields (see schema below); a JSON array when used with--file--format csv— CSV with a single header row followed by one data row per scan--file PATH— CSV input file with onecve_id,image_refpair per line; optionalcve_idheader row is skipped; blank lines and#comment lines are ignored
Batch file format:
cve_id,image_ref
CVE-2023-38039,registry.redhat.io/ubi9/ubi:latest
CVE-2025-15467,registry.redhat.io/ubi8/ubi:8.10Batch file validation (all checks run before any scan starts):
- UTF-8 encoding; maximum file size 1 MB; maximum 500 entries
- Each row must have exactly 2 columns; CVE ID must match
CVE-YYYY-NNNNNformat - Image reference must contain only safe characters (
[a-zA-Z0-9._/:\-@]) — shell metacharacters are rejected; must include at least one/; maximum 512 characters
JSON / CSV schema (field order matches the intended reading order):
| Field | Type | Description |
|---|---|---|
scanned_image |
string | Fully qualified image reference |
cve_id |
string | CVE identifier |
status |
true_positive | false_positive | uncertain |
Whether the image is affected (see semantics below) |
status_source |
vex_confirmed | sbom_analysis | vex_not_affected |
How the status was determined |
vendor_patch_available |
"true" | "false" | "" |
Whether a fixed container image is available ("" = unknown or only base RPM patched) |
patch_path.advisory_name |
string | Advisory that fixes the container image; "" if no container-level fix |
patch_path.advisory_url |
string | URL to the container fix advisory; "" if none |
related_rpm_patch.advisory_name |
string | Base RPM advisory (informational — cannot be applied to container directly); "" if none |
related_rpm_patch.advisory_url |
string | URL to the RPM advisory; "" if none |
recommendation |
string | Concise action for the user (1-2 sentences) |
Missing values: JSON uses "" (empty string) — never null. CSV leaves the field empty — never the literal word null.
Status semantics: true_positive = package present in SBOM AND version confirmed vulnerable — regardless of VEX coverage (includes VEX data gap scenarios where SBOM evidence confirms the vulnerability); false_positive = VEX known_not_affected; uncertain = genuinely inconclusive (version comparison failed, under_investigation with no SBOM confirmation).
Status source semantics: vex_confirmed = VEX directly matched the product and stated its status; sbom_analysis = determined from SBOM + CVE metadata (VEX data gap, missing, or generic blanket); vex_not_affected = VEX explicitly says known_not_affected (used only with false_positive).
vendor_patch_available and patch_path refer to container image fixes only. A base RPM patch (e.g., openssl-libs fixed in RHEL) does NOT count — the user cannot apply it to a container image independently. When a base RPM is patched but the container has not been rebuilt, vendor_patch_available is "" (unknown), patch_path is empty, and the RPM advisory goes into related_rpm_patch.
## CVE Validation Report
- CVE ID / Image
- Executive Summary
- Image Metadata (registry, vendor, name, CPE, version, build date, SBOM method)
- CVE Details (package, ecosystem, versions, Go module/ID if applicable)
- SBOM Findings (found, installed version, version range verdict, SPDX entry type)
- Red Hat VEX Status (status, severity, CPE match, remediation, RHSA advisories,
not-affected justification, patch availability note, caution,
upstream patch note)
- Parent RPM Patch Status (parent RPM name, RPM fix status in RHEL, RHSA URLs,
container rebuild required flag)
- Newer Image Availability (repository scanned, patched image in same/newer product stream,
recommendation)
- VEX Data Gap (gap type, analysis basis, action required)
Both approaches (Claude skill/command and CrewAI) use a shared set of standalone Python scripts in scripts/ for all deterministic data-fetching steps. Each script takes CLI arguments, outputs structured JSON to stdout, and handles errors gracefully.
| Script | Purpose | Usage |
|---|---|---|
validate_input.py |
Validate CVE ID, image reference, and batch CSV files. Classifies registries (registry.redhat.io, quay.io/openshift-release-dev/, etc.) | python scripts/validate_input.py --cve CVE-2024-45490 --image registry.redhat.io/ubi9/ubi:latest |
fetch_cve_metadata.py |
Query MITRE CVE API, OSV.dev, and Go vuln DB (conditional). Returns merged affected packages with ecosystem, version ranges, and source | python scripts/fetch_cve_metadata.py CVE-2024-45490 |
fetch_redhat_vex.py |
Fetch Red Hat CSAF VEX data. Default: deduped summary with all products, CPEs, statuses, and remediation URLs. With --raw: full CSAF JSON |
python scripts/fetch_redhat_vex.py CVE-2024-45490 |
download_sbom.py |
Download SBOM via cosign (attestation first, build-time fallback). Handles image index detection and amd64 re-fetching automatically | python scripts/download_sbom.py registry.redhat.io/ubi9/ubi:latest |
generate_sbom_syft.py |
Generate an analyzed SBOM using syft when no official SBOM is available. Same output schema as download_sbom.py |
python scripts/generate_sbom_syft.py registry.redhat.io/ubi9/ubi-minimal:latest |
inspect_image.py |
Extract image metadata (labels, digest, architecture) via regctl without downloading the image | python scripts/inspect_image.py registry.redhat.io/ubi9/ubi:latest |
scan_newer_images.py |
Scan a container image repository for images built after a given date. Returns tags, digests, build dates, CPEs. Uses parallel regctl checks. | python scripts/scan_newer_images.py registry.redhat.io/ubi9/ubi --since 2026-01-01T00:00:00Z |
fetch_coreos_metadata.py |
Fetch OCP release info via oc, extract CoreOS RPM list via podman, classify RPMs by source (RHEL/OCP/Fast Datapath), determine RHEL version. Handles both legacy (416.94.202410071428-0) and new (9.6.20260314-0) CoreOS versioning schemes |
python scripts/fetch_coreos_metadata.py 4.20.17 |
fetch_rhsa_advisory.py |
Fetch Red Hat Security Advisory (RHSA) CSAF data and extract CVE references. Used when scanners report RHSA IDs instead of CVE IDs | python scripts/fetch_rhsa_advisory.py RHSA-2026:3337 |
Why scripts? The LLM agent runs one script per step and gets structured JSON back, instead of multiple LLM-to-tool round-trips for curl/cosign/jq commands. This significantly reduces execution time for both approaches — especially CrewAI, where each tool call previously involved a separate LLM reasoning cycle.
Dependencies: All scripts require Python 3 and requests. download_sbom.py requires cosign, generate_sbom_syft.py requires syft, inspect_image.py requires regctl, and fetch_coreos_metadata.py requires oc and podman (plus a pull secret for quay.io/openshift-release-dev/). Each script checks for its tools and returns a clear error if missing.
Path: CrewAI/cve_ai_validation.py
A Python-based multi-agent framework using CrewAI. Three specialized agents run sequentially, each with a dedicated tool and role. The LLM backend is Claude accessed via Google Vertex AI.
- Python virtual environment with CrewAI dependencies (see setup below)
ANTHROPIC_VERTEX_PROJECT_IDenvironment variable setANTHROPIC_VERTEX_REGIONenvironment variable (optional, defaults tous-east5)- Google Cloud Application Default Credentials (
gcloud auth application-default login)
cd CrewAI
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtRun from within the activated venv:
cd CrewAI
source .venv/bin/activate
export CREWAI_TELEMETRY_OPT_OUT=true
python cve_ai_validation.py <CVE-ID> <IMAGE_REFERENCE>Or without activating:
CrewAI/.venv/bin/python CrewAI/cve_ai_validation.py <CVE-ID> <IMAGE_REFERENCE>python cve_ai_validation.py [--format markdown|json|csv] [--output full|summary] <CVE-ID> <IMAGE_REFERENCE>
python cve_ai_validation.py [--format markdown|json|csv] [--output full|summary] --file <PATH>
--format markdown --output full— complete Markdown report (default)--format markdown --output summary— condensed Markdown: executive summary + recommended actions--format json— machine-readable JSON with 6 fields; JSON array for batch; report agent skips Markdown generation entirely to save tokens and time--format csv— single header row + one data row per scan--file PATH— batch CSV input; same format and validation rules as the Claude skill/command
JSON/CSV schema is identical to the Claude skill/command — see the schema table above.
Six sequential agents, each with dedicated tools:
run_security_scan(cve_id, image_ref, output_mode="full", fmt="markdown")
│
▼
[Agent 1] Input Validator & Image Inspector
tools: validate_input, inspect_image_metadata
│
▼
[Agent 2] CVE Reconnaissance Specialist
tools: fetch_cve_metadata
│
▼
[Agent 3] SBOM Analyst & Package Verifier
tools: download_sbom, generate_sbom_syft
│
▼
[Agent 4] VEX Security Analyst
tool: fetch_vex
│
▼
[Agent 5] Container Image Update Scanner
tools: list_newer_image_tags, inspect_image_metadata,
download_sbom
│
▼
[Agent 6] Report Writer
(no tools — synthesis only)
│
▼
format_output() → Markdown (full/summary) | JSON | CSV
Full architecture detail: see
CrewAI/README.md
- Model:
claude-sonnet-4-6(default — configurable viaANTHROPIC_VERTEX_MODELenv var) - Provider: Google Vertex AI (
ANTHROPIC_VERTEX_PROJECT_ID— same backend as local Claude CLI) - Region:
ANTHROPIC_VERTEX_REGION(defaults tous-east5) - Temperature:
0.2 - Requires: GCP project with Vertex AI enabled and Application Default Credentials
- Clear separation of concerns across 6 specialised Python agents
- 8 focused tools cover all data retrieval; agents handle reasoning
- Full parity with the Claude skill validation logic, including output format options
- Unit-testable Python tools
- Easily extensible — add agents, swap models, integrate into pipelines
- Programmatic entry point:
run_security_scan(cve_id, image_ref, output_mode, fmt)
- Higher setup complexity (Python environment, pip dependencies)
- Requires
regctlandcosignon PATH (same as skill) - More verbose output (6 agents × verbose=True)
| Dimension | Claude Skill | Claude Command | CrewAI |
|---|---|---|---|
| Language | Markdown (prompt) | Markdown (prompt) | Python |
| LLM Access | Anthropic API (Claude CLI key) | Anthropic API (Claude CLI key) | Google Vertex AI (ANTHROPIC_VERTEX_PROJECT_ID) |
| Invocation | Autonomous / /skills dialog |
/container-cve-validator <args> |
python cve_ai_validation.py <args> |
| Agent structure | Single prompt, sequential steps | Single prompt, sequential steps | 6 dedicated Python agents |
| Input source | Conversation context | $ARGUMENTS (CLI args) |
CLI args via argparse |
| Batch scanning | --file PATH (CSV) |
--file PATH (CSV) |
--file PATH (CSV) |
| Output formats | Markdown, JSON, CSV | Markdown, JSON, CSV | Markdown, JSON, CSV |
| Output verbosity | --output full / --output summary |
--output full / --output summary |
--output full / --output summary |
| Image validation | Full (CPE, registry, digest, mirror) | Full (CPE, registry, digest, mirror) | Full (CPE, registry, digest, mirror) |
| SBOM parsing | PURL + SPDX relationships, multi-ecosystem | PURL + SPDX relationships, multi-ecosystem | PURL + SPDX relationships, multi-ecosystem |
| Image index handling | Yes (re-fetch with amd64 digest) | Yes (re-fetch with amd64 digest) | Yes (re-fetch with amd64 digest) |
| Go vuln DB | Yes (OSV + vuln.go.dev) | Yes (OSV + vuln.go.dev) | Yes (OSV + vuln.go.dev) |
| Version comparison | Yes (RPM EVR, semver, PyPI/npm) | Yes (RPM EVR, semver, PyPI/npm) | Yes (RPM EVR, semver, PyPI/npm) |
| VEX data gap detection | Yes (3 conditions + secalert) | Yes (3 conditions + secalert) | Yes (3 conditions + secalert) |
| VEX product stream mismatch | Yes (newer CPE version detection) | Yes (newer CPE version detection) | Yes (newer CPE version detection) |
| CoreOS (RHCOS) support | Yes (/coreos-cve-validator) |
Yes (/coreos-cve-validator) |
Not yet |
| RHSA advisory input | Yes (auto-detected, resolved to CVEs) | Yes (auto-detected, resolved to CVEs) | Not yet |
| Dependencies | regctl, cosign, Python 3, requests |
regctl, cosign, Python 3, requests |
regctl, cosign, Python 3, requests, CrewAI packages |
| Portability | High (two Markdown files) | High (two Markdown files) | Medium (Python env) |
| Testability | Low | Low | High (unit-testable tools) |
| Programmatic integration | No | No | Yes (run_security_scan()) |
| Permission prompts | Yes — per command | Yes — per command | None (Python subprocess) |
| Setup complexity | Minimal | Minimal | Moderate |
The Claude skill and command execute all external tools (regctl, cosign, curl, jq) via Claude Code's terminal tool. By default, Claude Code will prompt for approval before each individual command, which can be disruptive during a scan that runs dozens of commands.
The CrewAI approach has no prompts — all commands run via Python subprocess calls, completely outside Claude Code's permission system.
Pre-approve the required commands in your Claude Code settings to eliminate per-command prompts. Add the following to .claude/settings.json (user-level) or .claude/settings.local.json (project-level):
{
"allowedTools": [
"Bash(regctl:*)",
"Bash(cosign:*)",
"Bash(curl:*)",
"Bash(jq:*)"
]
}With these rules in place, Claude Code will execute all scan commands automatically without interruption. You can also configure this interactively using the /update-config skill inside Claude Code.
All implementations follow the same core security validation sequence:
- CVE Reconnaissance — Query
cveawg.mitre.orgto identify affected packages, ecosystems, and version ranges. - SBOM Analysis — Extract the container image SBOM via
cosign. Prefer release-time attestation SBOM; fall back to build-time SBOM; fall back to syft-generated SBOM. - Red Hat VEX Check — Query
security.access.redhat.com/data/csaf/v2/vex/for the official Red Hat exploitability status. - Report — Produce a structured Markdown security report.
- CVE Reconnaissance — Same as container pipeline.
- CoreOS Metadata — Fetch OCP release info via
oc, extract RPM package list viapodman, classify RPMs by source (RHEL/OCP/Fast Datapath). - RPM Matching — Search CoreOS RPMs for the affected package, compare installed version.
- Red Hat VEX Check — Check both RHCOS component under OCP CPE and specific RPM under RHEL/OCP CPE. Detect VEX discrepancies (RHCOS entry missing but RPM assessed).
- Report — CoreOS-specific report with RPM source classification and VEX discrepancy assessment.
When a scanner reports an RHSA advisory ID instead of a CVE ID, the fetch_rhsa_advisory.py script resolves it to individual CVE IDs, which then feed into either pipeline above.
All implementations share the same helper scripts in scripts/ for deterministic data-fetching steps.
| Implementation | Report |
|---|---|
| Claude Skill + Command | This file (root REPORT.md) |
| CrewAI Multi-Agent | CrewAI/README.md |
- Test all three implementations against real CVEs and Red Hat images
- Build a shared regression test harness comparing outputs across implementations
- Consider a CI/CD pipeline integration using the CrewAI
run_security_scan()function - Adding MCP server to allow futher integration with other ai agents and tools.
The Claude command approach is significantly faster than CrewAI for the same scan. This is an architectural trade-off, not a bug.
Multiple LLM API calls over the network. The Claude command is one continuous Claude session — the LLM reads the skill prompt and executes all steps. CrewAI makes 6+ separate API calls to Google Vertex AI (one per agent, often more since agents reason → call tool → reason again about the result). Each network round trip adds latency on top of inference time.
Agent reasoning overhead per task. Each CrewAI agent doesn't just call a tool — it reasons before calling, receives the result, then reasons again about whether to call another tool. A single agent task can trigger 3-5 LLM calls internally.
Growing context with each agent. Agent 6 (Report Writer) receives the full output of all 5 prior agents as context. By that point thousands of tokens are fed into each LLM call, increasing inference time on every step.
Agent 5 (Newer Image Scanner) is particularly expensive.
It potentially calls list_newer_image_tags (parallel regctl, fast) then downloads SBOMs via cosign for multiple newer image candidates — each cosign call can take 10-30 seconds, multiplied by several tags.
Sequential execution by design. All 6 agents run sequentially. Each waits for the previous to fully complete before starting.
| Step | Claude command | CrewAI (with scripts) | CrewAI (old, inline tools) |
|---|---|---|---|
| Input validation + image inspect | ~2s | ~5-10s | ~15-30s |
| CVE recon (MITRE + OSV + Go) | ~3s | ~5-15s | ~20-40s |
| SBOM download + analysis | ~10-20s | ~15-30s | ~30-60s |
| VEX validation | ~3s | ~5-15s | ~20-40s |
| Newer image scan | ~20-60s | ~30-90s | ~60-180s |
| Report generation | ~5s | ~20-40s | ~20-40s |
| Total | ~45s-2 min | ~2-4 min | ~5-10 min |
| Claude Skill / Command | CrewAI | |
|---|---|---|
| Speed | Fast — one LLM session | Slow — 6+ API round trips |
| Requires Claude Code | Yes | No |
| Human in the loop | Yes (interactive) | No (fully automated) |
| Programmatic integration | No | Yes (run_security_scan()) |
| Output formats | Markdown, JSON, CSV | Markdown, JSON, CSV |
| Testability | Low | High (unit-testable tools) |
| Extensibility | Edit Markdown | Add agents/tools in Python |
CrewAI's value is not speed — it is full automation without a human in the loop, programmatic integration into CI/CD pipelines, and independently unit-testable components. For interactive use where speed matters, the Claude command is the better choice.