Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Container Cve Validator

ASecurity

Validate a CVE against a Red Hat container image using official SBOM attestations, Red Hat VEX data, and CVE metadata from MITRE/OSV.dev.

36 stars
0 votes
0 copies
0 views
Added 9/22/2026
securitypythongoshellbashnodegitapidatabasesecuritydocumentation

Works with

apimcp

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add NVlabs/Skill2Env --skill container-cve-validator --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Container Cve Validator?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Container Cve Validator
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nvlabs-container-cve-validator/badge)](https://www.skillsdirectory.com/skills/nvlabs-container-cve-validator)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: container-cve-validator
description: Validate a CVE against a Red Hat container image using official SBOM attestations, Red Hat VEX data, and CVE metadata from MITRE/OSV.dev.
license: Apache-2.0
user_invocable: true
model: inherit
color: red
---

# Red Hat Container Security Validator

## When to Use This Skill
Use this skill when the user asks you to check, validate, or analyze one or more CVEs against Red Hat container images. Input may be a single CVE ID + image reference, or a CSV file path containing multiple pairs.

## Optional Output Flags

The user may specify these optional flags. Parse them before extracting the required inputs.

- `--format markdown` — (default) Markdown report; verbosity controlled by `--output`
- `--format json` — machine-readable JSON with 6 structured fields
- `--format csv` — single-row CSV with a header row (one row per scan in batch mode)
- `--output full` — (default) complete Markdown report with all sections; only applies to `--format markdown`
- `--output summary` — condensed Markdown: executive summary + recommended actions only; only applies to `--format markdown`
- `--file PATH` — path to a CSV input file for batch scanning (mutually exclusive with positional CVE ID and image reference)

Record the resolved values as `OUTPUT_FORMAT` (default: `markdown`), `OUTPUT_MODE` (default: `full`), and `INPUT_FILE` (default: empty). Strip all flag tokens before extracting remaining positional arguments.

## Input Validation

Use the `validate_input.py` script for all input validation — both single scan and batch mode.

**Single scan:**
```bash
python $SCRIPTS_DIR/validate_input.py --cve [CVE-ID] --image [IMAGE_REFERENCE]
```

**Batch mode (when `--file` was provided):**
```bash
python $SCRIPTS_DIR/validate_input.py --file [PATH]
```

The script returns JSON with `valid` (boolean), `mode` ("single" or "batch"), `entries` (array of validated CVE/image pairs with registry classification), and `errors` (array of error messages with line numbers for batch mode).

- If `valid` is `false`: print the errors and stop. Do not proceed to any execution step.
- If `valid` is `true`: use the `entries` array to drive the pipeline. Each entry contains `image_ref`, `ref_format` ("tag" or "digest"), `registry` info, and `input_type`.
- If `input_type` is `"rhsa"`: the user provided an RHSA advisory ID. Resolve it to individual CVEs using (**do NOT use WebFetch or curl to access access.redhat.com**):
  ```bash
  python $SCRIPTS_DIR/fetch_rhsa_advisory.py [RHSA-ID]
  ```
  Run the full validation pipeline for each CVE in the returned `cve_ids[]` list against the same image.
- If `input_type` is `"cve"`: proceed directly with the single CVE ID.
- For batch mode, run the full validation pipeline for each entry. Print a progress indicator: `[1/N] Scanning CVE-YYYY-NNNNN — registry.../image:tag`

**Batch output aggregation:**
- **`--format markdown`:** print each report separated by a `====` divider line
- **`--format json`:** output a single JSON array `[{...}, {...}]` — one object per scan
- **`--format csv`:** one header row followed by one data row per scan (no repeated headers)

### Image Inspection

Use the `inspect_image.py` script to fetch image metadata:
```bash
python $SCRIPTS_DIR/inspect_image.py [IMAGE_REFERENCE]
```

The script returns JSON with `labels` (cpe, name, com.redhat.component, vendor, maintainer, org.opencontainers.image.created), `digest`, `architecture`, and `errors`.

If the script fails with an authentication error, log in first:
```
regctl registry login registry.redhat.io
```
Then re-run. If login fails, stop and inform the user.

From the output, record:
- `labels.cpe` → `product_cpe` — primary anchor for VEX product_tree matching
- `labels.name` → public component name for VEX matching
- `labels["com.redhat.component"]` → internal build name (secondary fallback only)
- `labels.vendor` and `labels.maintainer` — for registry ownership validation
- `labels["org.opencontainers.image.created"]` → image build timestamp

**Registry ownership check:** The `validate_input.py` output already classifies the registry (`is_redhat`, `type`). For non-Red Hat registries:
- If `vendor` or `maintainer` from inspect output is `"Red Hat, Inc."`: the image is a Red Hat image mirrored to an alternate registry. Record the canonical `registry.redhat.io` form for VEX matching.
- Otherwise: stop and report that this skill only validates Red Hat-maintained images.

**Input format classification:** The `validate_input.py` output provides `ref_format` ("tag" or "digest"). For digest-based references, the human-readable tag will be extracted from the SBOM in Step 2.

Do NOT use `version` or `release` labels to reconstruct the image tag.

## Prerequisites

**Step 0 — Resolve scripts directory.** Before anything else, locate the helper scripts. They are at `../scripts/` relative to this project's root directory. Run:
```bash
SCRIPTS_DIR="$(git rev-parse --show-toplevel)/ocp-admin/scripts/security-validation"
test -f "$SCRIPTS_DIR/validate_input.py" || { echo "Error: Scripts directory not found at $SCRIPTS_DIR"; exit 1; }
```
Use `$SCRIPTS_DIR` in all subsequent script calls. The scripts handle tool checks internally (regctl, cosign, syft) and return clear errors if tools are missing.

## Workflow

**MANDATORY EXECUTION CONTRACT — read before starting:**

This skill requires completing ALL of the following steps in order. No step may be skipped, condensed, or replaced with an alternative approach — regardless of which model is executing.

| # | Step | Required | May skip only when |
|---|---|---|---|
| 0 | Prerequisites check | Always | Never |
| 0 | Input validation + image inspection | Always | Never |
| 1 | CVE Reconnaissance (MITRE → OSV.dev → Go vuln DB) | Always | Never |
| 2 | SBOM Extraction + package verification | Always | Never |
| 3 | Red Hat VEX Validation | Always | Package NOT found in SBOM (Step 2) |
| 4 | Newer Image Availability Scan | Conditional | Trigger conditions not met (see Step 4 header) |
| 5 | Final Report | Always | Never |

**Strict rules:**
1. Execute each step using the helper scripts below. After each step, print: `✓ Step N complete — [key finding]`
2. **FORBIDDEN COMMANDS — never use these, even inside loops or pipelines:**
   - `grep` — do not grep through JSON data files
   - `jq` — do not use jq filters on JSON data
   - `curl` — do not fetch URLs directly, use the helper scripts
   - `cosign` — do not call cosign directly, use `download_sbom.py`
   - `for ... do ... done` loops that process tool-result files
   - Any ad-hoc bash script, Python one-liner, or shell pipeline that parses or filters JSON data
3. **The ONLY allowed bash commands are:** running the helper scripts (`python $SCRIPTS_DIR/...`), `SCRIPTS_DIR=...` resolution, `test -f`, and `cat` to read a file.
4. **How to handle large VEX/SBOM data:** Read the raw JSON output directly in your context window. Analyze it in your reasoning, not with shell tools. Do NOT attempt to filter it with grep/jq.
5. Do not combine, reorder, or parallelise steps. Each step depends on outputs from previous steps.
6. If a step produces unexpected results, report them and continue — do not stop unless explicitly instructed.
7. Every field in the final report must come from actual script outputs. Use `N/A — [reason]` for undetermined fields. Never fabricate values.
8. The `fetch_redhat_vex.py` script takes ONLY a CVE-ID argument — no flags. It returns the full raw VEX document.

---

### Step 1: CVE Reconnaissance

Run the `fetch_cve_metadata.py` script to query all CVE data sources in a single call:
```bash
python $SCRIPTS_DIR/fetch_cve_metadata.py [CVE-ID]
```

The script automatically queries MITRE CVE API, OSV.dev, and (if a GO-* alias is found) the Go vulnerability database. It returns merged JSON with:
- `description` — CVE description
- `affected[]` — all affected packages with `ecosystem`, `package`, `versions` (introduced/fixed), and `source` (mitre/osv/go_vuln_db)
- `aliases[]` — cross-references (GO-*, GHSA-*, etc.)
- `errors[]` — any API failures (non-fatal; the script continues with available data)

From the output, identify:
- The target package name and ecosystem for SBOM matching
- For Go CVEs, the Go vuln DB module path is authoritative for SBOM matching
- Vulnerable version ranges (use the union across all sources)
- If `affected` is empty and errors indicate the CVE was not found: stop and report "CVE not found"

Print: `✓ Step 1 complete — [package name], ecosystem: [ecosystem], sources: [list from affected[].source]`

---

### Step 2: SBOM Extraction

Run the `download_sbom.py` script to handle all SBOM extraction logic in a single call:
```bash
python $SCRIPTS_DIR/download_sbom.py [IMAGE_REFERENCE]
```

The script handles attestation/build-time fallback and image index detection automatically. It returns JSON with:
- `sbom_source` — "attestation" or "build_time"
- `spdx` — the **full raw SPDX JSON document** with all packages, relationships, PURLs, and version info
- `errors[]` — any issues encountered

**Read and interpret the full raw SPDX document from the `spdx` field.** Analyze the SPDX data directly — packages (with `externalRefs` PURLs and `versionInfo`), relationships (for parent RPM linkage), checksums. Do NOT use grep or jq to search the SBOM.

If `spdx` is null (no SBOM found), fall back to syft:
```bash
python $SCRIPTS_DIR/generate_sbom_syft.py [IMAGE_REFERENCE]
```
This generates an analyzed SBOM using syft. The output has the same JSON schema but `sbom_source` will be `"syft_analyzed"` — note this in the report as a generated SBOM, not an official one.

If neither method produces an SBOM: stop and report "Unable to extract SBOM. Cannot determine package presence."

**Package presence check and version confirmation:**

Red Hat container image SBOMs contain both RPM packages and non-RPM content (Go modules, Python packages, Node.js packages, etc.), each identified by ecosystem-specific PURLs. A non-RPM package may exist as a standalone SBOM entry AND be linked via an SPDX relationship to the RPM package that delivers it. Use the following matching logic:

1. **Identify the target package name and ecosystem from Step 1.** For Go, use the module path from the Go vuln DB as the authoritative name.

2. **Search SBOM packages by PURL and name:**
   - For each package in the SBOM, check its `externalRefs[]` for entries with `referenceType: purl`. Parse the PURL to extract ecosystem, name, and version.
   - Match the ecosystem prefix against the CVE ecosystem (`pkg:rpm`, `pkg:golang`, `pkg:pypi`, `pkg:npm`, etc.).
   - Match the package name case-insensitively. For Go, match against the full module path (e.g., `golang.org/x/net`).
   - If found as a non-RPM package: also check its SPDX relationships — look for a `DYNAMIC_LINK`, `STATIC_LINK`, or `CONTAINED_BY` relationship to a parent RPM package entry. Record both the non-RPM package entry and its parent RPM if present.
   - If found only as an RPM package: check `name` and `sourceName` fields (case-insensitive).

3. **If the package is NOT found by any method:** skip Step 3 and go to Step 4 (Early Exit — package not in image).

4. **If the package IS found:** record the installed version from the SPDX `versionInfo` field and proceed to version range confirmation.

5. **Confirm the installed version falls within the vulnerable range:**
   - Compare the installed version against the vulnerable version ranges from Step 1 (MITRE and, for Go, Go vuln DB).
   - Version comparison rules by ecosystem:
     - **RPM:** use RPM EVR ordering (`Epoch:Version-Release`). A higher EVR is a newer, potentially fixed version. If the installed version is within the `introduced`–`fixed` range from MITRE, it is vulnerable.
     - **Go:** use semver ordering. Compare using the `introduced` and `fixed` boundaries from the Go vuln DB `ranges[].events[]`.
     - **PyPI / npm:** use the version ordering rules of the respective ecosystem as documented in the CVE `versions[]` array.
   - If the installed version is **within** the vulnerable range: record as **vulnerable — version confirmed**.
   - If the installed version is **at or above** the fixed version: record as **not vulnerable — patched version installed**, skip Step 3, and go to Step 4.
   - If version comparison is inconclusive (e.g., non-standard version string): record as **version comparison inconclusive** and proceed to Step 3 noting this uncertainty.

Print: `✓ Step 2 complete — package [found|not found], version: [version], verdict: [vulnerable|patched|inconclusive], SBOM method: [attestation|build-time]`

---

### Step 3: Red Hat VEX Validation

Run the `fetch_redhat_vex.py` script to fetch the full raw VEX document:
```bash
python $SCRIPTS_DIR/fetch_redhat_vex.py [CVE-ID]
```

The script returns JSON with:
- `http_status` — 200, 404, or error code
- `vex` — the **full raw CSAF VEX document** with `product_tree`, `vulnerabilities`, `remediations`, `flags`, `threats`
- `errors[]` — any issues

**Read and interpret the full VEX document from the `vex` field.** The LLM should analyze the `product_tree` (branches, relationships, CPEs, PURLs), `vulnerabilities` (product_status, remediations, flags, threats) directly to perform matching against the image CPE and component name from Step 0. This gives the LLM full context to:
- Match the specific container component under its CPE
- Search across all CPE versions for name-only matches
- Check parent RPM status across all product streams (especially RHEL)
- Extract all RHSA advisory URLs, justification flags, severity

**Interpret the results:**

- If `http_status` is 404: record "No Red Hat VEX data available for this CVE" and proceed to Step 4 (check VEX data gap Condition B).
- If `vex.product_tree` contains only a generic blanket statement (all products under `cpe:/a:redhat`): treat as no analysis performed (check VEX data gap Condition C).

**Read [references/01-vex-validation-procedure.md](references/01-vex-validation-procedure.md)** for the complete VEX matching procedure (sub-steps 3–9: generic blanket detection, product_tree matching, status determination, remediation/flags/severity extraction, and parent RPM patch check).

Print: `✓ Step 3 complete — VEX status: [status], severity: [severity], gap condition: [A|B|C|none]`

---

### Step 4: Newer Image Availability Scan

**Trigger — perform this step only when ALL of the following are true:**
- The vulnerable package is an RPM, OR a non-RPM package delivered via a parent RPM (identified in Step 2 via SPDX relationship)
- A fixed RPM version is known — from either:
  - Red Hat VEX `product_status[].fixed` for the container (Step 3, sub-step 5), OR
  - The parent RPM patch status check (Step 3, sub-step 9) finding the RPM `fixed` in RHEL
- The RHSA release date is known (from the `vendor_fix` remediation URL or advisory metadata)

If trigger conditions are NOT met, print `✓ Step 4 skipped — trigger conditions not met ([reason])` and proceed to Step 5.

**Procedure:**

1. Extract the image repository from the scanned image reference — strip tag or digest, keeping only registry + namespace + image name (e.g., `registry.redhat.io/multicluster-engine/cluster-proxy-addon-rhel9`).

2. Run the `scan_newer_images.py` script:
   ```bash
   python $SCRIPTS_DIR/scan_newer_images.py [IMAGE_REPOSITORY] --since [SCANNED_IMAGE_CREATED_LABEL]
   ```

   The script returns JSON with `newer_images[]` — each entry has `tag`, `created`, `digest`, and `cpe`. Images are sorted by date (newest first), deduplicated by digest, capped at 10 results.

   If `newer_images` is empty: no newer images exist yet. Record this and proceed to Step 5.

3. For each newer image candidate, download its SBOM and search for the vulnerable RPM:
   ```bash
   python $SCRIPTS_DIR/download_sbom.py [IMAGE_REPOSITORY]:[TAG]
   ```
   - Find the RPM package by name in the SPDX data
   - Compare its `versionInfo` against the known fixed version using RPM EVR ordering
   - If installed version **≥ fixed version**: this image contains the fix — record it

4. For each image found to contain the fix, compare its CPE against the scanned image's CPE:
   - **Same CPE** (same product stream): "Patched image available in the same product stream: `[IMAGE_REPOSITORY]:[TAG]` (`[DIGEST]`, built `[CREATED]`)"
   - **Different CPE** (different product stream): "Patched image available in a newer product stream (`[NEWER_CPE]`): `[IMAGE_REPOSITORY]:[TAG]` (`[DIGEST]`, built `[CREATED]`). Upgrading would change product stream from `[IMAGE_CPE]` to `[NEWER_CPE]`."

5. If no newer image contains the fixed RPM: record "No patched container image released yet. The RPM fix exists but the container has not yet been rebuilt with the updated RPM."

Print: `✓ Step 4 complete — [patched image found: REPO:TAG | no patched image found | skipped]`

---

### Step 5: Final Report

**Read [references/02-report-template.md](references/02-report-template.md)** for VEX data gap condition evaluation (Conditions A/B/C), executive summary rules, upstream patch note logic, RHSA advisory assessment, the full report template (markdown/JSON/CSV), and field semantics.

Print: `✓ Step 5 complete — report generated`

## Dependencies

### Required MCP Servers
- None — this skill uses bundled Python scripts, not MCP tools

### Required Helper Scripts
- `validate_input` — validates CVE ID format and image reference
- `inspect_image` — extracts container image metadata via regctl
- `fetch_cve_metadata` — queries MITRE, OSV.dev, and Go vuln DB
- `download_sbom` — fetches SBOM attestations and build-time SBOMs
- `generate_sbom_syft` — fallback SBOM generation via syft (optional, used when attestations unavailable)
- `fetch_redhat_vex` — retrieves Red Hat VEX security advisories
- `fetch_rhsa_advisory` — resolves RHSA advisory IDs to CVE lists
- `scan_newer_images` — lists and checks newer image tags for patched RPMs

### Related Skills
- `cve-recon` — standalone CVE reconnaissance (Step 1 only)
- `image-inspect` — standalone image inspection (Input Validation only)
- `coreos-cve-validator` — CVE validation for CoreOS/RHCOS images

### Reference Documentation
- [Red Hat CSAF VEX Data](https://security.access.redhat.com/data/csaf/v2/)
- [MITRE CVE API](https://cveawg.mitre.org/api/cve/)
- [OSV.dev API](https://api.osv.dev/v1/vulns/)

Attribution

NVlabsNVlabs
View sourceMore from NVlabs →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Security Review

Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.

2456590 votes

Springboot Security

Java Spring Boot 服务中关于身份验证/授权、验证、CSRF、密钥、标头、速率限制和依赖安全的 Spring Security 最佳实践。

2456590 votes

Paperclip Task Bridge

Create, comment on, update, and list Paperclip tasks from Hermes using scoped Paperclip API credentials.

813270 votes

Summarize Status

Write a short, colloquial summary for a Paperclip summary slot: open with the 1–3 specific, concrete actions the reader needs to take right now to unblock the work, then a brief plain-language status, streaming progress as it works.

813270 votes

Paperclip Evals

Choose, inspect, validate, and report Paperclip Runner or Product E2E evaluations while preserving evidence, provenance, cost, and failure classification.

813270 votes
View all in security →