CVE Source Check
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).
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.
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: [{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 only. YAML not supported (no stdlib parser).
Outputs
Files: cve-source-report-{service}-{YYYYMMDD}.{md,json} in --out-dir.
| Exit | Meaning |
|---|---|
| 0 | Full coverage. |
| 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
- Locate
tech-source-registry.json(next to SKILL.md by default, or--registry). - Build inventory from
--inventory(JSON list or{components: [...]}) or--inline(comma-splitname@version). - If
--current-sourcesprovided, read URLs and normalize for case-insensitive comparison.
Gate: at least one component present. Empty inventory -> exit 3.
Phase 2: MAP & VERIFY
- Look up each component
name(and aliases) in the registry.- Found ->
mapped, attach source list. Missing ->unmapped, sources[].
- Found ->
- If current sources loaded, mark each source
monitored: truewhen its normalized URL appears. - 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 status; every source has monitored and reachable fields.
Phase 3: REPORT
- Compute summary: components, mapped/unmapped, monitored, coverage %, gaps, unreachable.
- Write JSON report with per-component status and per-source
monitored/reachablefields. - Write Markdown report: summary table, components table (markers), gaps section (when gaps exist), unmapped section (when unmapped exist).
- Print one-screen summary to stdout. Set exit code per table above.
Gate: both files written, summary printed.
Registry Schema
tech-source-registry.json shape:
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"}]} ] }
Each technology: name (lowercase, unique), aliases (list), type (runtime/base-image/container/library), sources (1-3, at least one primary).
To add a technology: pick canonical name, list aliases, add 1-3 sources (lead with vendor advisory page), re-run against a sample inventory.
Error Handling
| Error | Cause | Fix |
|---|---|---|
| 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. |


