cve-source-check · diff
git:20260815.7152afb to git:20260919.28d30f9
66 added, 109 removed. Audit A to A.
---
name: cve-source-check
- promoted_to: service-health-check
+ promoted_to: deploy
description: "Audit CVE/vulnerability source coverage for a technology stack. Maps each component (container, library, base image, runtime) to authoritative CVE feeds, flags gaps, and produces audit-ready reports. Generic: works for any service or stack."
user-invocable: false
argument-hint: "[--inventory <file>] [--inline <tech-list>] [--current-sources <file>] [--service <name>] [--check-urls]"
allowed-tools:
- Bash
- Read
- Write
- Edit
- Glob
- Grep
routing:
triggers:
- "check cve sources"
- "cve source coverage"
- "audit cve feeds"
- "vulnerability source audit"
- "verify cve sources"
- "security feed audit"
category: infrastructure
complexity: Simple
pairs_with:
- - service-health-check
+ - assessment
---
# CVE Source Check
- Audits CVE/vulnerability source coverage for a technology stack. Given an inventory
- of components and (optionally) the feeds you currently monitor, it maps each
- component to authoritative CVE sources, flags gaps, and emits audit-ready reports.
+ Audit CVE/vulnerability source coverage for a technology stack. Maps components
+ to authoritative CVE feeds via a versioned registry, flags gaps, and produces
+ audit-ready reports (JSON + Markdown).
- ## Scope
+ **In scope**: component-to-feed mapping, coverage/gap reporting, optional URL
+ reachability checks. **Out of scope**: running scanners (Trivy/Snyk), fetching
+ CVE content, private vuln databases.
- | In scope | Out of scope |
- |---|---|
- | Mapping components → authoritative feeds via a versioned registry | Running vulnerability scanners (Trivy/Snyk/etc.) |
- | Reporting coverage and gaps in JSON + Markdown | Fetching CVE content or ranking by severity |
- | Optional HEAD-check for source URL reachability | Integrating with private/commercial vuln databases |
- | Audit-ready output (deterministic, reproducible) | Live LLM research per run |
+ ## Quick Start
+ ```bash
+ # Inline, offline
+ python3 scripts/check-cve-sources.py \
+ --inline "go@1.22,alpine@3.19,postgres@16,redis@7,nginx@1.25" \
+ --service my-service
+
+ # Inventory + monitored feeds + link verification
+ python3 scripts/check-cve-sources.py \
+ --inventory examples/inventory.example.json \
+ --current-sources examples/current-sources.example.txt \
+ --service my-service --check-urls
+ ```
+
## Inputs
| Flag | Purpose |
|---|---|
- | `--inventory <file>` | JSON inventory: `[{name, version?, type?}, ...]` or `{components: [...]}`. |
- | `--inline "name@ver,name,..."` | Quick comma-separated list. Mutually exclusive with `--inventory`. |
- | `--current-sources <file>` | Optional. One URL per line. Blank lines and `#` comments skipped. |
- | `--service <name>` | Free-form name used in report header and filenames. |
+ | `--inventory <file>` | JSON: `[{name, version?, type?}, ...]` or `{components: [...]}`. |
+ | `--inline "name@ver,..."` | Comma-separated list. Mutually exclusive with `--inventory`. |
+ | `--current-sources <file>` | One URL per line. `#` comments and blank lines skipped. |
+ | `--service <name>` | Name for report header and filenames. |
| `--check-urls` | HEAD-check every source URL (5s timeout, graceful degradation). |
| `--registry <path>` | Override default `tech-source-registry.json`. |
| `--out-dir <path>` | Output directory (default: cwd). |
- JSON inventory format only. YAML is not supported — stdlib does not ship a YAML parser.
+ JSON only. YAML not supported (no stdlib parser).
## Outputs
- | File | Format |
- |---|---|
- | `cve-source-report-{service}-{YYYYMMDD}.md` | Human-readable audit report. |
- | `cve-source-report-{service}-{YYYYMMDD}.json` | Machine-readable per `references/output-formats.md`. |
+ Files: `cve-source-report-{service}-{YYYYMMDD}.{md,json}` in `--out-dir`.
- | Exit code | Meaning |
+ | Exit | Meaning |
|---|---|
| 0 | Full coverage. |
- | 1 | Gaps exist (unmapped components or unmonitored sources). |
- | 2 | At least one source URL is unreachable (only with `--check-urls`). |
+ | 1 | Gaps (unmapped components or unmonitored sources). |
+ | 2 | Unreachable source URL (only with `--check-urls`). |
| 3 | Input error (missing/malformed registry or inventory). |
## Workflow
### Phase 1: LOAD
- 1. Locate the registry: `tech-source-registry.json` next to this SKILL.md by default.
- 2. Build an inventory:
- - From `--inventory`: parse JSON; accept either a list or `{components: [...]}`.
- - From `--inline`: split on commas, parse `name@version` pairs.
- 3. If `--current-sources` is provided, read URLs (one per line); normalize for
- case-insensitive comparison.
+ 1. Locate `tech-source-registry.json` (next to SKILL.md by default, or `--registry`).
+ 2. Build inventory from `--inventory` (JSON list or `{components: [...]}`) or `--inline` (comma-split `name@version`).
+ 3. If `--current-sources` provided, read URLs and normalize for case-insensitive comparison.
- **Gate**: at least one inventory component is present. Empty inventory → exit 3.
+ **Gate**: at least one component present. Empty inventory -> exit 3.
### Phase 2: MAP & VERIFY
- 1. For each component, look up `name` (and aliases) in the registry.
- - Found → status `mapped`, attach the registry's source list.
- - Missing → status `unmapped`, sources `[]`.
- 2. If current sources were loaded, mark each source `monitored: true` when its
- normalized URL appears in the set.
- 3. If `--check-urls` is set, HEAD-check every unique source URL. Treat
- 200/301/302/403/405 as reachable; record definite failures and network errors
- distinctly. See `references/source-verification.md`.
+ 1. Look up each component `name` (and aliases) in the registry.
+ - Found -> `mapped`, attach source list. Missing -> `unmapped`, sources `[]`.
+ 2. If current sources loaded, mark each source `monitored: true` when its normalized URL appears.
+ 3. If `--check-urls`: HEAD-check each unique URL. Treat 200/301/302/403/405 as reachable. 4xx (except 403/405) and 5xx -> `reachable: false`. Timeout/DNS/TLS failure -> `reachable: null` (WARN, does not affect exit code). 5s timeout per URL, cached per run.
- **Gate**: every component has a status; every source has `monitored` and
- `reachable` fields populated (`reachable: null` when checks are skipped).
+ **Gate**: every component has status; every source has `monitored` and `reachable` fields.
### Phase 3: REPORT
- 1. Compute the summary: components, mapped/unmapped, monitored, coverage %,
- gaps, unreachable.
- 2. Write the JSON report.
- 3. Write the Markdown report:
- - Summary table.
- - Components table with ✅ / ⚠️ / ❌ markers.
- - **Gaps** section listing primary then secondary sources to add (only when
- gaps exist).
- - **Unmapped** section listing registry-extension TODOs (only when unmapped
- components exist).
- 4. Print a one-screen summary to stdout including report paths.
- 5. Set the exit code per the table above.
-
- **Gate**: both files exist on disk and the summary printed; exit code reflects
- the audit result.
+ 1. Compute summary: components, mapped/unmapped, monitored, coverage %, gaps, unreachable.
+ 2. Write JSON report with per-component status and per-source `monitored`/`reachable` fields.
+ 3. Write Markdown report: summary table, components table (markers), gaps section (when gaps exist), unmapped section (when unmapped exist).
+ 4. Print one-screen summary to stdout. Set exit code per table above.
- ## Quick start
+ **Gate**: both files written, summary printed.
- ```bash
- # Inline, offline, no monitoring data
- python3 scripts/check-cve-sources.py \
- --inline "go@1.22,alpine@3.19,postgres@16,redis@7,nginx@1.25" \
- --service my-service
+ ## Registry Schema
- # Inventory file + current monitored feeds
- python3 scripts/check-cve-sources.py \
- --inventory examples/inventory.example.json \
- --current-sources examples/current-sources.example.txt \
- --service my-service
+ `tech-source-registry.json` shape:
- # Same, with link verification
- python3 scripts/check-cve-sources.py \
- --inventory examples/inventory.example.json \
- --current-sources examples/current-sources.example.txt \
- --service my-service \
- --check-urls
+ ```json
+ {
+ "$schema_version": "1.0",
+ "kinds": ["advisory-list", "github-security", "mailing-list", "distro-tracker", "vendor-page", "mitre"],
+ "priorities": ["primary", "secondary"],
+ "technologies": [
+ {"name": "postgres", "aliases": ["postgresql","pg"], "type": "container",
+ "sources": [{"url": "https://...", "kind": "advisory-list", "priority": "primary"}]}
+ ]
+ }
```
- ## Extending the registry
+ Each technology: `name` (lowercase, unique), `aliases` (list), `type` (`runtime`/`base-image`/`container`/`library`), `sources` (1-3, at least one `primary`).
- To add a technology, edit `tech-source-registry.json`. Each entry needs `name`,
- `aliases`, `type`, and 1–3 `sources`. Schema lives at
- `references/registry-schema.md`.
+ To add a technology: pick canonical name, list aliases, add 1-3 sources (lead with vendor advisory page), re-run against a sample inventory.
- ## Reference Loading Table
+ ## Error Handling
- | Signal | Load These Files | Why |
+ | Error | Cause | Fix |
|---|---|---|
- | adding a technology to the registry | `registry-schema.md` | Defines registry shape and allowed values. |
- | checking source URLs | `source-verification.md` | Defines HEAD-check semantics and graceful degradation. |
- | generating audit reports | `output-formats.md` | Defines JSON and Markdown report contracts. |
-
- ## Error handling
-
- ### "ERROR: failed to load registry"
- Cause: registry file missing or malformed JSON.
- Solution: confirm `tech-source-registry.json` is at `--registry` (or default
- location) and parses with `python3 -m json.tool`.
-
- ### "ERROR: failed to load inventory"
- Cause: inventory file missing, malformed JSON, or unexpected shape.
- Solution: validate with `python3 -m json.tool`. Inventory must be a list or an
- object with a `components` key.
-
- ### "ERROR: inventory is empty"
- Cause: no usable components after parsing.
- Solution: confirm each entry has a `name`. Inline form requires non-empty tokens.
-
- ### Coverage stuck at 0%
- Cause: `--current-sources` URLs do not match registry URLs exactly (e.g., extra
- path segments, trailing slashes).
- Solution: copy URLs directly from the registry. The script normalizes scheme/host
- case and trailing slash; everything else must match.
-
- ### `--check-urls` flags many `[—]` entries
- Cause: network issues (proxy, DNS, offline) — recorded as `reachable: null`.
- Solution: re-run without `--check-urls` for the audit; investigate network
- separately. Network errors do not affect the gap exit code.
+ | Failed to load registry | Missing or malformed JSON | Validate with `python3 -m json.tool` |
+ | Failed to load inventory | Missing, malformed, or wrong shape | Validate JSON; must be list or `{components: [...]}` |
+ | Inventory empty | No usable components | Each entry needs `name`. Inline needs non-empty tokens. |
+ | Coverage stuck at 0% | `--current-sources` URLs don't match registry | Copy URLs from registry. Scheme/host case and trailing slash are normalized; rest must match. |
+ | Many `[--]` entries with `--check-urls` | Network issues | Re-run without `--check-urls`. Network errors don't affect gap exit code. |