"""Owner-prepared, receipt-only native acquisition check for CUST81 revision 1. Run inside the hub environment after a separately approved credential rollout: python -m reuse_surface.private_source_verify --proposal FILE --disclosure FILE --consent FILE No credentials are accepted as arguments. No bodies or exception text are emitted. This command does not register sources or use the federation cache. """ from __future__ import annotations import argparse import hashlib import json import os import re from datetime import datetime, timezone from pathlib import Path from typing import Any from urllib.parse import quote import yaml from jsonschema import Draft202012Validator from reuse_surface import federation from reuse_surface.registry import load_schema PROPOSAL_SHA256 = "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99" DISCLOSURE_SHA256 = "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35" CONSENT_SHA256 = "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52" AUTH_ENV = "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN" REPOSITORIES = frozenset({ "rapp-openbao", "rapp-policy-nexus", "rapp-postgres", "rapp-sbom-nexus", "rapp-telemetry", "rapp-tenant-engine", "rapp-user-engine", }) def _sha(content: bytes) -> str: return hashlib.sha256(content).hexdigest() def _load(path: Path, expected: str) -> dict[str, Any]: data = path.read_bytes() if _sha(data) != expected: raise ValueError("packet_digest_mismatch") return json.loads(data) def _raw_url(repo: str, commit: str, path: str) -> str: if not re.fullmatch(r"[0-9a-f]{40}", commit): raise ValueError("invalid_commit") if not re.fullmatch(r"registry/(indexes|capabilities)/[a-zA-Z0-9._-]+\.(yaml|md)", path): raise ValueError("invalid_metadata_path") return ( f"https://forgejo.coulomb.social/api/v1/repos/coulomb/{repo}/raw/" f"{quote(path, safe='/')}?ref={commit}" ) def _prepared_sources(proposal_path: Path, disclosure_path: Path, consent_path: Path): proposal = _load(proposal_path, PROPOSAL_SHA256) disclosure = _load(disclosure_path, DISCLOSURE_SHA256) consent = _load(consent_path, CONSENT_SHA256) if ( consent["consent_status"] != "approved" or consent["proposal_sha256"] != DISCLOSURE_SHA256 or consent["proposal_id"] != disclosure["id"] or consent["proposal_revision"] != disclosure["revision"] or set(consent["repositories"]) != REPOSITORIES ): raise ValueError("invalid_consent") sources = proposal["sources"] approved = {source["repo"]: source for source in disclosure["sources"]} if len(sources) != 7 or {source["repo"] for source in sources} != REPOSITORIES: raise ValueError("invalid_source_scope") for source in sources: owner = approved[source["repo"]] files = {item["path"]: item["sha256"] for item in owner["files"]} if ( source["consented_commit"] != owner["commit"] or source["capability_ids"] != owner["capability_ids"] or source["index_sha256"] != files["registry/indexes/capabilities.yaml"] or source["auth_env"] != AUTH_ENV or source["native_raw_url"] != _raw_url( source["repo"], owner["commit"], "registry/indexes/capabilities.yaml" ) ): raise ValueError("source_not_consented") yield source, files def verify(proposal_path: Path, disclosure_path: Path, consent_path: Path) -> dict[str, Any]: receipt: dict[str, Any] = { "id": "CUST81-PRIVATE-NATIVE-FETCH-RECEIPT-R1", "checked_at": datetime.now(timezone.utc).isoformat(), "proposal_sha256": PROPOSAL_SHA256, "disclosure_sha256": DISCLOSURE_SHA256, "consent_sha256": CONSENT_SHA256, "cache_used": False, "all_verified": False, "sources": [], } try: # Exhaust all packet checks before any native network request. sources = list(_prepared_sources(proposal_path, disclosure_path, consent_path)) expected_urls = { _raw_url(source["repo"], source["consented_commit"], path) for source, files in sources for path in files } allowlist = json.loads(os.environ.get("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", "{}")) actual_urls = allowlist.get(AUTH_ENV) if ( not isinstance(actual_urls, list) or len(actual_urls) != 14 or set(actual_urls) != expected_urls ): raise ValueError("runtime_scope_not_exact") schema = Draft202012Validator(load_schema()) except Exception: receipt["error"] = "packet_scope_or_schema_validation_failed" return receipt for source, files in sources: row = { "repo": source["repo"], "commit": source["consented_commit"], "index_sha256": source["index_sha256"], "verified": False, "entry_sha256": [], } receipt["sources"].append(row) try: index_text = federation.fetch_remote_index_text(source["native_raw_url"], source) if _sha(index_text.encode("utf-8")) != source["index_sha256"]: raise ValueError("index_hash_mismatch") index = yaml.safe_load(index_text) entries = index["capabilities"] if not isinstance(entries, list) or [e["id"] for e in entries] != source["capability_ids"]: raise ValueError("unexpected_capability_ids") for entry in entries: if not {"id", "name", "summary", "vector", "domain", "status", "owner", "path"} <= entry.keys(): raise ValueError("invalid_index_row") if entry["owner"] != source["repo"] or entry["path"] not in files: raise ValueError("unconsented_entry") entry_url = _raw_url(source["repo"], source["consented_commit"], entry["path"]) entry_text = federation.fetch_remote_index_text(entry_url, source) digest = _sha(entry_text.encode("utf-8")) if digest != files[entry["path"]]: raise ValueError("entry_hash_mismatch") front = re.match(r"^---\n(.*?)\n---", entry_text, re.DOTALL) if front is None: raise ValueError("missing_front_matter") metadata = yaml.safe_load(front.group(1)) schema.validate(metadata) if metadata["id"] != entry["id"] or metadata["owner"] != source["repo"]: raise ValueError("entry_identity_mismatch") row["entry_sha256"].append(digest) row["verified"] = True except Exception: # Never stringify errors: HTTP handlers and parsers can embed bodies, # headers, credentials, or private source text in their messages. row["error"] = "native_fetch_or_metadata_validation_failed" receipt["all_verified"] = len(receipt["sources"]) == 7 and all( row["verified"] for row in receipt["sources"] ) return receipt def main() -> int: parser = argparse.ArgumentParser(description=__doc__) for name in ("proposal", "disclosure", "consent"): parser.add_argument(f"--{name}", type=Path, required=True) args = parser.parse_args() receipt = verify(args.proposal, args.disclosure, args.consent) print(json.dumps(receipt, sort_keys=True)) return 0 if receipt["all_verified"] else 1 if __name__ == "__main__": raise SystemExit(main())