#!/usr/bin/env python3 """Validate external artifact provenance before it enters a build context.""" from __future__ import annotations import argparse import json import re from pathlib import Path from urllib.parse import urlparse SHA256 = re.compile(r"^[0-9a-f]{64}$") KINDS = {"official", "china-mirror", "local-cache"} def validate(path: Path) -> list[str]: errors: list[str] = [] seen: set[tuple[str, str]] = set() with path.open("r", encoding="utf-8") as handle: for line_number, line in enumerate(handle, 1): if not line.strip(): continue try: row = json.loads(line) except json.JSONDecodeError as exc: errors.append(f"{path}:{line_number}: invalid JSON: {exc.msg}") continue if row.get("record_type") != "artifact": continue required = {"name", "version", "source_url", "sha256", "bytes", "license", "mirror_kind"} missing = sorted(required - row.keys()) if missing: errors.append(f"{path}:{line_number}: missing {missing}") continue key = (str(row["name"]), str(row["version"])) if key in seen: errors.append(f"{path}:{line_number}: duplicate artifact {key[0]}@{key[1]}") seen.add(key) if not SHA256.fullmatch(str(row["sha256"])): errors.append(f"{path}:{line_number}: sha256 must be 64 lowercase hex characters") if not isinstance(row["bytes"], int) or row["bytes"] <= 0: errors.append(f"{path}:{line_number}: bytes must be a positive integer") if row["mirror_kind"] not in KINDS: errors.append(f"{path}:{line_number}: invalid mirror_kind") parsed = urlparse(str(row["source_url"])) if row["mirror_kind"] != "local-cache" and parsed.scheme not in {"http", "https"}: errors.append(f"{path}:{line_number}: source_url must be HTTP(S) for downloaded artifacts") if row["mirror_kind"] == "china-mirror" and not row.get("origin_url"): errors.append(f"{path}:{line_number}: china-mirror requires origin_url") return errors def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--index", type=Path, default=Path("vendor/downloads/index.jsonl")) args = parser.parse_args() errors = validate(args.index) if errors: for error in errors: print(f"ERROR: {error}") return 1 print(f"download_index=valid path={args.index}") return 0 if __name__ == "__main__": raise SystemExit(main())