#!/usr/bin/env python3 """Fail closed when a dependency-audit exception is malformed or expired.""" from __future__ import annotations import argparse import datetime as dt import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] EXCEPTIONS_PATH = ROOT / "security" / "pip-audit-exceptions.json" def load_and_validate() -> tuple[dict[str, object], list[str]]: payload = json.loads(EXCEPTIONS_PATH.read_text(encoding="utf-8")) errors: list[str] = [] if set(payload) != {"schema_version", "advisories"}: errors.append("exception policy must contain only schema_version and advisories") if payload.get("schema_version") != 1: errors.append("schema_version must be 1") advisories = payload.get("advisories") if not isinstance(advisories, list): errors.append("advisories must be a list") else: ids = [str(item.get("id", "")) for item in advisories if isinstance(item, dict)] if len(ids) != len(set(ids)) or any(not item.startswith("PYSEC-") for item in ids): errors.append("advisory IDs must be unique PYSEC identifiers") for item in advisories: if not isinstance(item, dict): errors.append("every advisory must be an object") continue required = {"id", "package", "review_by", "reason"} allowed = required | {"aliases"} if not required.issubset(item) or not set(item).issubset(allowed): errors.append("every advisory must match the documented exception schema") if not str(item.get("package", "")).strip(): errors.append("every advisory requires a package") if len(str(item.get("reason", ""))) < 30: errors.append("every advisory requires a specific reason") try: review_by = dt.date.fromisoformat(str(item["review_by"])) except (KeyError, ValueError): errors.append("every advisory review_by must be an ISO date") else: if review_by < dt.date.today(): errors.append( f"dependency exception {item.get('id', '')} expired on " f"{review_by.isoformat()}" ) aliases = [ str(alias) for item in advisories if isinstance(item, dict) for alias in item.get("aliases", []) ] if len(aliases) != len(set(aliases)) or any( not alias.startswith("CVE-") for alias in aliases ): errors.append("container aliases must be unique CVE identifiers") return payload, errors def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--print-ids", action="store_true") parser.add_argument("--print-container-ids", action="store_true") args = parser.parse_args() payload, errors = load_and_validate() if errors: for error in errors: print(f"ERROR: {error}", file=sys.stderr) return 1 if args.print_ids: for item in payload["advisories"]: print(item["id"]) elif args.print_container_ids: for item in payload["advisories"]: for alias in item.get("aliases", []): print(alias) else: print( f"Dependency exception policy valid; {len(payload['advisories'])} active exception(s)." ) return 0 if __name__ == "__main__": raise SystemExit(main())