scripts/pnpm-audit-with-allowlist.py
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env python3
"""Run `pnpm audit --json`, drop allowlisted advisories, fail on HIGH/CRITICAL.
Mirrors scripts/pip-audit-with-allowlist.py for the node ecosystem. Allowlist entries
(supply-chain/pnpm-audit-allowlist.yml) expire on their `expires` field; expired entries
stop suppressing findings.
Handles both the pnpm v8/v9 audit JSON shapes:
- {"advisories": {"<id>": {severity, module_name, ...}}, "metadata": {...}}
- {"vulnerabilities": {...}} (npm-style passthrough)
Sub-spec §5.3; acceptance gate #8.
"""
from __future__ import annotations
import datetime as dt
import json
import pathlib
import subprocess
import sys
import yaml
REPO = pathlib.Path(__file__).resolve().parents[1]
ALLOWLIST = REPO / "supply-chain" / "pnpm-audit-allowlist.yml"
BLOCKING_SEVERITIES = {"high", "critical"}
def _active_allowlist() -> set[str]:
if not ALLOWLIST.exists():
return set()
allow = yaml.safe_load(ALLOWLIST.read_text(encoding="utf-8")) or {}
today = dt.date.today()
active: set[str] = set()
for e in allow.get("allowlist") or []:
advisory = e.get("advisory") or e.get("vuln_id")
if not advisory:
print(f"WARNING: allowlist entry without advisory id: {e}", file=sys.stderr)
continue
expires = e.get("expires")
if expires and dt.date.fromisoformat(str(expires)) < today:
print(f"NOTE: pnpm allowlist entry {advisory} expired on {expires}")
continue
active.add(str(advisory))
return active
def _collect_advisories(report: dict) -> list[dict]:
if not isinstance(report, dict) or "error" in report or not any(
key in report for key in ("advisories", "vulnerabilities")
):
raise ValueError("Missing audit result")
for key in ("advisories", "vulnerabilities"):
if key not in report:
continue
records = report[key]
if not isinstance(records, dict):
raise ValueError("Invalid advisory collection")
for value in records.values():
if not isinstance(value, dict) or value.get("severity") not in {
"info", "low", "moderate", "high", "critical"
}:
raise ValueError("Invalid advisory severity")
out: list[dict] = []
for adv in (report.get("advisories") or {}).values():
out.append({
"id": str(adv.get("github_advisory_id") or adv.get("id") or "?"),
"package": adv.get("module_name", "?"),
"severity": (adv.get("severity") or "unknown").lower(),
"url": adv.get("url", ""),
})
for pkg, v in (report.get("vulnerabilities") or {}).items():
if not isinstance(v, dict):
continue
sev = (v.get("severity") or "unknown").lower()
via = v.get("via") or []
ids = [x.get("source") or x.get("url") for x in via if isinstance(x, dict)]
out.append({
"id": str(ids[0]) if ids else pkg,
"package": v.get("name", pkg),
"severity": sev,
"url": "",
})
return out
def main() -> int:
output = REPO / "pnpm-audit-report.json"
try:
output.unlink(missing_ok=True)
except OSError:
print("Cannot retire stale audit evidence (failing closed)", file=sys.stderr)
return 2
try:
proc = subprocess.run(
["pnpm", "audit", "--json", "--audit-level", "high"],
capture_output=True, text=True, check=False,
cwd=REPO, timeout=120,
)
except (OSError, subprocess.TimeoutExpired):
print("pnpm audit unavailable (failing closed)", file=sys.stderr)
return 2
if proc.returncode not in (0, 1) or not proc.stdout.strip():
print("pnpm audit did not produce valid evidence (failing closed)", file=sys.stderr)
return 2
try:
report = json.loads(proc.stdout)
advisories = _collect_advisories(report)
expected_failure = any(adv["severity"] in BLOCKING_SEVERITIES for adv in advisories)
if (proc.returncode == 1) != expected_failure:
raise ValueError("Audit exit status contradicts the result")
active_allow = _active_allowlist()
except (ValueError, TypeError, AttributeError, OSError, yaml.YAMLError):
print("pnpm audit evidence or allowlist invalid (failing closed)", file=sys.stderr)
return 2
try:
output.write_text(json.dumps(report, indent=2), encoding="utf-8")
except OSError:
print("Cannot persist audit evidence (failing closed)", file=sys.stderr)
return 2
blocking: list[dict] = []
for adv in advisories:
if adv["severity"] not in BLOCKING_SEVERITIES:
continue
if adv["id"] in active_allow:
print(f"ALLOWLISTED: {adv['id']} in {adv['package']}")
continue
blocking.append(adv)
if blocking:
print("BLOCKING:", json.dumps(blocking, indent=2))
return 1
print("pnpm audit clean (after allowlist filter)")
return 0
if __name__ == "__main__":
raise SystemExit(main())