Claude Skill

cve-source-check

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.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download notque-vexjoy-agent-skills_infrastructure_cve-source-check-8ad6845.zip · 8 KB
Part of notque/vexjoy-agent — 69 skills

Install

skills CLI npx skills add https://github.com/notque/vexjoy-agent/tree/main/skills/infrastructure/cve-source-check
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install notque-vexjoy-agent@llmmart
Git git clone https://github.com/notque/vexjoy-agent.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole notque/vexjoy-agent collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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

# 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

  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 component present. Empty inventory -> exit 3.

Phase 2: MAP & VERIFY

  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 status; every source has monitored and reachable fields.

Phase 3: REPORT

  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.

Gate: both files written, summary printed.

Registry Schema

tech-source-registry.json shape:

{
  "$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.
Files (vexjoy-agent)
  • examples
    • current-sources.example.txt 218 B
      # Example: feeds my-service currently monitors.
      # One URL per line; blank lines and `#` comments are skipped.
      https://pkg.go.dev/vuln/list
      https://security.alpinelinux.org/
      https://www.postgresql.org/support/security/
      
    • inventory.example.json 368 B
      {
        "service": "my-service",
        "components": [
          {"name": "go", "version": "1.22.3", "type": "runtime"},
          {"name": "alpine", "version": "3.19", "type": "base-image"},
          {"name": "postgres", "version": "16.2", "type": "container"},
          {"name": "redis", "version": "7.2", "type": "container"},
          {"name": "nginx", "version": "1.25", "type": "container"}
        ]
      }
      
  • scripts
    • check-cve-sources.py 13.3 KB
      #!/usr/bin/env python3
      """check-cve-sources.py — audit CVE/vulnerability source coverage for a tech stack.
      
      Three-phase methodology:
        1. LOAD          inventory + registry + (optional) currently-monitored sources
        2. MAP & VERIFY  resolve each component to authoritative feeds; mark monitored;
                         optionally HEAD-check URL reachability
        3. REPORT        emit JSON + Markdown audit reports
      
      stdlib only. Generic. Vendor-neutral.
      """
      
      from __future__ import annotations
      
      import argparse
      import json
      import os
      import re
      import sys
      import urllib.error
      import urllib.request
      from datetime import datetime, timezone
      
      DEFAULT_REGISTRY = os.path.join(
          os.path.dirname(os.path.abspath(__file__)),
          "..",
          "tech-source-registry.json",
      )
      HEAD_TIMEOUT = 5
      REACHABLE_CODES = {200, 301, 302, 403, 405}
      
      
      # ----------------------------- LOAD -----------------------------------------
      
      
      def load_registry(path: str) -> dict:
          with open(path, "r", encoding="utf-8") as f:
              return json.load(f)
      
      
      def build_lookup(registry: dict) -> dict[str, dict]:
          """Map name + aliases (lowercased) -> tech entry."""
          lookup: dict[str, dict] = {}
          for entry in registry.get("technologies", []):
              keys = [entry["name"]] + list(entry.get("aliases", []))
              for k in keys:
                  lookup[k.lower()] = entry
          return lookup
      
      
      def parse_inventory(path: str) -> list[dict]:
          """JSON inventory: [{name, version?, type?}, ...] or {components: [...]}."""
          with open(path, "r", encoding="utf-8") as f:
              data = json.load(f)
          if isinstance(data, dict):
              components = data.get("components", [])
          elif isinstance(data, list):
              components = data
          else:
              raise ValueError("Inventory JSON must be a list or {components: [...]}")
          out = []
          for c in components:
              if isinstance(c, str):
                  name, ver = _split_pin(c)
                  out.append({"name": name, "version": ver, "type": ""})
              elif isinstance(c, dict):
                  out.append(
                      {
                          "name": c.get("name", "").strip(),
                          "version": str(c.get("version", "")).strip(),
                          "type": c.get("type", "").strip(),
                      }
                  )
          return [c for c in out if c["name"]]
      
      
      def parse_inline(spec: str) -> list[dict]:
          """Parse 'name@ver,name,name@ver' into component list."""
          out = []
          for token in spec.split(","):
              token = token.strip()
              if not token:
                  continue
              name, ver = _split_pin(token)
              out.append({"name": name, "version": ver, "type": ""})
          return out
      
      
      def _split_pin(token: str) -> tuple[str, str]:
          if "@" in token:
              n, v = token.split("@", 1)
              return n.strip(), v.strip()
          return token.strip(), ""
      
      
      def load_current_sources(path: str) -> set[str]:
          """One URL per line; blank and # comments skipped."""
          urls: set[str] = set()
          with open(path, "r", encoding="utf-8") as f:
              for line in f:
                  line = line.strip()
                  if not line or line.startswith("#"):
                      continue
                  urls.add(_normalize_url(line))
          return urls
      
      
      def _normalize_url(url: str) -> str:
          # Drop trailing slash, lowercase scheme+host for comparison.
          m = re.match(r"^(https?://)([^/]+)(/.*)?$", url, re.IGNORECASE)
          if not m:
              return url.rstrip("/")
          scheme, host, path = m.group(1).lower(), m.group(2).lower(), (m.group(3) or "")
          return f"{scheme}{host}{path}".rstrip("/")
      
      
      # ----------------------------- MAP & VERIFY ---------------------------------
      
      
      def map_component(component: dict, lookup: dict[str, dict]) -> dict:
          key = component["name"].lower()
          entry = lookup.get(key)
          if entry is None:
              return {
                  **component,
                  "status": "unmapped",
                  "type": component.get("type") or "unknown",
                  "sources": [],
              }
          sources = [{**s, "monitored": False, "reachable": None} for s in entry.get("sources", [])]
          return {
              **component,
              "status": "mapped",
              "type": component.get("type") or entry.get("type", "unknown"),
              "sources": sources,
          }
      
      
      def mark_monitored(mapped: list[dict], current: set[str]) -> None:
          for comp in mapped:
              for s in comp["sources"]:
                  if _normalize_url(s["url"]) in current:
                      s["monitored"] = True
      
      
      def head_check(url: str) -> bool | None:
          """Return True (reachable), False (definitely unreachable), None (network error)."""
          try:
              req = urllib.request.Request(url, method="HEAD")
              with urllib.request.urlopen(req, timeout=HEAD_TIMEOUT) as resp:
                  return resp.status in REACHABLE_CODES
          except urllib.error.HTTPError as e:
              return e.code in REACHABLE_CODES
          except (urllib.error.URLError, TimeoutError, OSError):
              return None
          except Exception:
              return None
      
      
      def verify_urls(mapped: list[dict]) -> int:
          """HEAD-check every URL; return count of definitively unreachable URLs."""
          unreachable = 0
          seen: dict[str, bool | None] = {}
          for comp in mapped:
              for s in comp["sources"]:
                  url = s["url"]
                  if url not in seen:
                      seen[url] = head_check(url)
                  s["reachable"] = seen[url]
                  if seen[url] is False:
                      unreachable += 1
          return unreachable
      
      
      # ----------------------------- REPORT ---------------------------------------
      
      
      def summarize(mapped: list[dict]) -> dict:
          n = len(mapped)
          n_mapped = sum(1 for c in mapped if c["status"] == "mapped")
          n_unmapped = n - n_mapped
          n_monitored = sum(1 for c in mapped if c["status"] == "mapped" and any(s["monitored"] for s in c["sources"]))
          n_gaps = (
              sum(1 for c in mapped if c["status"] == "mapped" and not any(s["monitored"] for s in c["sources"])) + n_unmapped
          )
          n_unreachable = sum(1 for c in mapped for s in c["sources"] if s.get("reachable") is False)
          coverage = round(100.0 * n_monitored / n, 1) if n else 0.0
          return {
              "components": n,
              "mapped": n_mapped,
              "unmapped": n_unmapped,
              "monitored": n_monitored,
              "coverage_pct": coverage,
              "gaps": n_gaps,
              "unreachable": n_unreachable,
          }
      
      
      def render_json(service: str, mapped: list[dict], summary: dict) -> dict:
          return {
              "service": service,
              "generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
              "summary": summary,
              "components": mapped,
          }
      
      
      def _marker(comp: dict) -> str:
          if comp["status"] == "unmapped":
              return "❌"
          if any(s["monitored"] for s in comp["sources"]):
              return "✅"
          return "⚠️"
      
      
      def _reach_str(reachable) -> str:
          if reachable is True:
              return "ok"
          if reachable is False:
              return "DOWN"
          return "—"
      
      
      def render_markdown(service: str, mapped: list[dict], summary: dict, checked: bool) -> str:
          ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
          out: list[str] = []
          out.append(f"# CVE Source Coverage Report: {service}")
          out.append("")
          out.append(f"_Generated: {ts}_")
          out.append("")
          out.append("## Summary")
          out.append("")
          out.append("| Metric | Value |")
          out.append("|--------|-------|")
          out.append(f"| Components | {summary['components']} |")
          out.append(f"| Mapped to registry | {summary['mapped']} |")
          out.append(f"| Unmapped | {summary['unmapped']} |")
          out.append(f"| Monitored (≥1 source) | {summary['monitored']} |")
          out.append(f"| Coverage | {summary['coverage_pct']}% |")
          out.append(f"| Gaps | {summary['gaps']} |")
          if checked:
              out.append(f"| Unreachable URLs | {summary['unreachable']} |")
          out.append("")
          out.append("Legend: ✅ mapped + monitored • ⚠️ mapped, not monitored • ❌ unmapped")
          out.append("")
          out.append("## Components")
          out.append("")
          header = "| Status | Component | Version | Type | Sources |"
          sep = "|--------|-----------|---------|------|---------|"
          out.append(header)
          out.append(sep)
          for c in mapped:
              sources_cell = _format_sources(c["sources"], checked)
              out.append(
                  f"| {_marker(c)} | `{c['name']}` | {c.get('version') or '—'} | {c.get('type') or '—'} | {sources_cell} |"
              )
          out.append("")
      
          # Gaps
          gap_rows = [c for c in mapped if c["status"] == "mapped" and not any(s["monitored"] for s in c["sources"])]
          if gap_rows:
              out.append("## Gaps — Sources to add")
              out.append("")
              out.append("Components mapped to authoritative sources you do not currently monitor:")
              out.append("")
              for c in gap_rows:
                  out.append(f"### {c['name']}")
                  for s in c["sources"]:
                      if s["priority"] == "primary":
                          out.append(f"- **primary** ({s['kind']}): {s['url']}")
                  for s in c["sources"]:
                      if s["priority"] != "primary":
                          out.append(f"- secondary ({s['kind']}): {s['url']}")
                  out.append("")
      
          # Unmapped
          unmapped = [c for c in mapped if c["status"] == "unmapped"]
          if unmapped:
              out.append("## Unmapped — Registry Extension TODO")
              out.append("")
              out.append("These components are not in the registry. Add entries (see `references/registry-schema.md`):")
              out.append("")
              for c in unmapped:
                  out.append(f"- [ ] `{c['name']}` (version `{c.get('version') or '?'}`, type `{c.get('type') or '?'}`)")
              out.append("")
      
          return "\n".join(out) + "\n"
      
      
      def _format_sources(sources: list[dict], checked: bool) -> str:
          if not sources:
              return "—"
          cells = []
          for s in sources:
              flag = "✓" if s["monitored"] else "·"
              reach = f" [{_reach_str(s.get('reachable'))}]" if checked else ""
              cells.append(f"{flag} {s['priority']}/{s['kind']}{reach}")
          return "<br>".join(cells)
      
      
      # ----------------------------- CLI ------------------------------------------
      
      
      def parse_args(argv: list[str]) -> argparse.Namespace:
          p = argparse.ArgumentParser(
              description="Audit CVE/vulnerability source coverage for a technology stack.",
          )
          src = p.add_mutually_exclusive_group(required=True)
          src.add_argument("--inventory", help="Path to JSON inventory file.")
          src.add_argument("--inline", help='Inline list: "name@ver,name,name@ver"')
          p.add_argument("--current-sources", help="Optional path; one URL per line.")
          p.add_argument("--service", default="service", help="Service name for report header.")
          p.add_argument("--check-urls", action="store_true", help="HEAD-check source URLs (5s timeout).")
          p.add_argument("--registry", default=DEFAULT_REGISTRY, help="Path to tech-source-registry.json.")
          p.add_argument("--out-dir", default=".", help="Output directory (default: cwd).")
          return p.parse_args(argv)
      
      
      def main(argv: list[str]) -> int:
          args = parse_args(argv)
      
          # PHASE 1: LOAD
          try:
              registry = load_registry(args.registry)
          except (OSError, json.JSONDecodeError) as e:
              print(f"ERROR: failed to load registry: {e}", file=sys.stderr)
              return 3
          lookup = build_lookup(registry)
      
          if args.inventory:
              try:
                  components = parse_inventory(args.inventory)
              except (OSError, json.JSONDecodeError, ValueError) as e:
                  print(f"ERROR: failed to load inventory: {e}", file=sys.stderr)
                  return 3
          else:
              components = parse_inline(args.inline)
      
          if not components:
              print("ERROR: inventory is empty", file=sys.stderr)
              return 3
      
          current = set()
          if args.current_sources:
              try:
                  current = load_current_sources(args.current_sources)
              except OSError as e:
                  print(f"WARN: failed to load current sources ({e}); proceeding without.", file=sys.stderr)
      
          # PHASE 2: MAP & VERIFY
          mapped = [map_component(c, lookup) for c in components]
          if current:
              mark_monitored(mapped, current)
      
          unreachable_count = 0
          if args.check_urls:
              unreachable_count = verify_urls(mapped)
      
          # PHASE 3: REPORT
          summary = summarize(mapped)
          payload = render_json(args.service, mapped, summary)
          md = render_markdown(args.service, mapped, summary, checked=args.check_urls)
      
          os.makedirs(args.out_dir, exist_ok=True)
          date_tag = datetime.now(timezone.utc).strftime("%Y%m%d")
          safe_service = re.sub(r"[^A-Za-z0-9_.-]+", "-", args.service)
          json_path = os.path.join(args.out_dir, f"cve-source-report-{safe_service}-{date_tag}.json")
          md_path = os.path.join(args.out_dir, f"cve-source-report-{safe_service}-{date_tag}.md")
      
          with open(json_path, "w", encoding="utf-8") as f:
              json.dump(payload, f, indent=2, ensure_ascii=False)
              f.write("\n")
          with open(md_path, "w", encoding="utf-8") as f:
              f.write(md)
      
          print(f"Service:       {args.service}")
          print(f"Components:    {summary['components']}")
          print(f"Mapped:        {summary['mapped']}  (unmapped: {summary['unmapped']})")
          print(f"Monitored:     {summary['monitored']}  (coverage: {summary['coverage_pct']}%)")
          print(f"Gaps:          {summary['gaps']}")
          if args.check_urls:
              print(f"Unreachable:   {summary['unreachable']}")
          print(f"JSON report:   {json_path}")
          print(f"MD report:     {md_path}")
      
          if args.check_urls and unreachable_count > 0:
              return 2
          if summary["gaps"] > 0:
              return 1
          return 0
      
      
      if __name__ == "__main__":
          sys.exit(main(sys.argv[1:]))
      
  • SKILL.md 5.4 KB
    ---
    name: cve-source-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:
        - assessment
    ---
    
    # 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
    
    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 component present. Empty inventory -> exit 3.
    
    ### Phase 2: MAP & VERIFY
    
    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 status; every source has `monitored` and `reachable` fields.
    
    ### Phase 3: REPORT
    
    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.
    
    **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. |
    
  • tech-source-registry.json 8.6 KB
    {
      "$schema_version": "1.0",
      "description": "Maps technologies to authoritative CVE/security feeds. See references/registry-schema.md to extend.",
      "kinds": ["advisory-list", "github-security", "mailing-list", "distro-tracker", "vendor-page", "mitre"],
      "priorities": ["primary", "secondary"],
      "technologies": [
        {
          "name": "go",
          "aliases": ["golang", "go-lang"],
          "type": "runtime",
          "sources": [
            {"url": "https://pkg.go.dev/vuln/list", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://groups.google.com/g/golang-announce", "kind": "mailing-list", "priority": "primary"},
            {"url": "https://github.com/golang/go/security/advisories", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "python",
          "aliases": ["python3", "cpython"],
          "type": "runtime",
          "sources": [
            {"url": "https://discuss.python.org/c/announcements/security-announcements/47", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://github.com/python/cpython/security/advisories", "kind": "github-security", "priority": "primary"},
            {"url": "https://osv.dev/list?q=&ecosystem=PyPI", "kind": "advisory-list", "priority": "secondary"}
          ]
        },
        {
          "name": "node",
          "aliases": ["nodejs", "node.js"],
          "type": "runtime",
          "sources": [
            {"url": "https://nodejs.org/en/blog/vulnerability/", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://github.com/nodejs/node/security/advisories", "kind": "github-security", "priority": "primary"},
            {"url": "https://github.com/advisories?query=ecosystem%3Anpm", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "java",
          "aliases": ["openjdk", "jdk", "jre"],
          "type": "runtime",
          "sources": [
            {"url": "https://www.oracle.com/security-alerts/", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://openjdk.org/groups/vulnerability.html", "kind": "vendor-page", "priority": "primary"},
            {"url": "https://github.com/advisories?query=ecosystem%3Amaven", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "alpine",
          "aliases": ["alpine-linux"],
          "type": "base-image",
          "sources": [
            {"url": "https://security.alpinelinux.org/", "kind": "distro-tracker", "priority": "primary"},
            {"url": "https://lists.alpinelinux.org/~alpine/security-announce/", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "debian",
          "aliases": ["debian-linux"],
          "type": "base-image",
          "sources": [
            {"url": "https://security-tracker.debian.org/tracker/", "kind": "distro-tracker", "priority": "primary"},
            {"url": "https://lists.debian.org/debian-security-announce/", "kind": "mailing-list", "priority": "primary"}
          ]
        },
        {
          "name": "ubuntu",
          "aliases": ["ubuntu-linux"],
          "type": "base-image",
          "sources": [
            {"url": "https://ubuntu.com/security/notices", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://ubuntu.com/security/cves", "kind": "distro-tracker", "priority": "primary"},
            {"url": "https://lists.ubuntu.com/archives/ubuntu-security-announce/", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "postgres",
          "aliases": ["postgresql", "pg"],
          "type": "container",
          "sources": [
            {"url": "https://www.postgresql.org/support/security/", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://www.postgresql.org/list/pgsql-announce/", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "redis",
          "aliases": ["redis-server"],
          "type": "container",
          "sources": [
            {"url": "https://github.com/redis/redis/security/advisories", "kind": "github-security", "priority": "primary"},
            {"url": "https://redis.io/blog/", "kind": "vendor-page", "priority": "secondary"}
          ]
        },
        {
          "name": "mysql",
          "aliases": ["mariadb"],
          "type": "container",
          "sources": [
            {"url": "https://www.oracle.com/security-alerts/", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://mariadb.org/about/security-policy/", "kind": "vendor-page", "priority": "secondary"}
          ]
        },
        {
          "name": "opensearch",
          "aliases": ["opensearch-project"],
          "type": "container",
          "sources": [
            {"url": "https://github.com/opensearch-project/OpenSearch/security/advisories", "kind": "github-security", "priority": "primary"},
            {"url": "https://opensearch.org/blog/", "kind": "vendor-page", "priority": "secondary"}
          ]
        },
        {
          "name": "elasticsearch",
          "aliases": ["elastic", "es"],
          "type": "container",
          "sources": [
            {"url": "https://www.elastic.co/community/security", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://discuss.elastic.co/c/announcements/security-announcements/31", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "rabbitmq",
          "aliases": ["rabbit", "amqp"],
          "type": "container",
          "sources": [
            {"url": "https://github.com/rabbitmq/rabbitmq-server/security/advisories", "kind": "github-security", "priority": "primary"},
            {"url": "https://www.rabbitmq.com/release-information.html", "kind": "vendor-page", "priority": "secondary"}
          ]
        },
        {
          "name": "kafka",
          "aliases": ["apache-kafka"],
          "type": "container",
          "sources": [
            {"url": "https://kafka.apache.org/cve-list", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://lists.apache.org/list.html?announce@apache.org", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "nginx",
          "aliases": ["nginx-server"],
          "type": "container",
          "sources": [
            {"url": "https://nginx.org/en/security_advisories.html", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://mailman.nginx.org/mailman3/lists/nginx-announce.nginx.org/", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "apache",
          "aliases": ["httpd", "apache-httpd"],
          "type": "container",
          "sources": [
            {"url": "https://httpd.apache.org/security/vulnerabilities_24.html", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://lists.apache.org/list.html?announce@httpd.apache.org", "kind": "mailing-list", "priority": "secondary"}
          ]
        },
        {
          "name": "logstash",
          "aliases": [],
          "type": "container",
          "sources": [
            {"url": "https://www.elastic.co/community/security", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://github.com/elastic/logstash/security/advisories", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "prometheus",
          "aliases": ["prom"],
          "type": "container",
          "sources": [
            {"url": "https://github.com/prometheus/prometheus/security/advisories", "kind": "github-security", "priority": "primary"},
            {"url": "https://prometheus.io/docs/operating/security/", "kind": "vendor-page", "priority": "secondary"}
          ]
        },
        {
          "name": "grafana",
          "aliases": [],
          "type": "container",
          "sources": [
            {"url": "https://grafana.com/security/security-advisories/", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://github.com/grafana/grafana/security/advisories", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "memcached",
          "aliases": [],
          "type": "container",
          "sources": [
            {"url": "https://github.com/memcached/memcached/wiki/ReleaseNotes", "kind": "vendor-page", "priority": "primary"},
            {"url": "https://github.com/memcached/memcached/security/advisories", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "haproxy",
          "aliases": [],
          "type": "container",
          "sources": [
            {"url": "https://www.haproxy.org/#vuln", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://github.com/haproxy/haproxy/security/advisories", "kind": "github-security", "priority": "secondary"}
          ]
        },
        {
          "name": "openssl",
          "aliases": ["libssl"],
          "type": "library",
          "sources": [
            {"url": "https://www.openssl.org/news/vulnerabilities.html", "kind": "advisory-list", "priority": "primary"},
            {"url": "https://mta.openssl.org/pipermail/openssl-announce/", "kind": "mailing-list", "priority": "secondary"}
          ]
        }
      ]
    }
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related