65 lines
2.6 KiB
Python
65 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate the active selected-case publication gate."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
GATE = Path("coverage/release-authorized-gate.report.json")
|
|
EXPECTED_SCHEMA = "release-authorized-gate.v1"
|
|
EXPECTED_SCOPE = "selected_case_web_wasm_simulation"
|
|
|
|
|
|
def canonical(value: Any) -> str:
|
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
|
|
|
|
def validate(root: Path, gate_path: Path = GATE) -> dict[str, Any]:
|
|
root = root.resolve()
|
|
path = gate_path if gate_path.is_absolute() else root / gate_path
|
|
report = json.loads(path.read_text(encoding="utf-8"))
|
|
unsigned = {key: value for key, value in report.items() if key != "report_sha256"}
|
|
expected_hash = hashlib.sha256(canonical(unsigned).encode("utf-8")).hexdigest()
|
|
errors: list[str] = []
|
|
if report.get("schema") != EXPECTED_SCHEMA:
|
|
errors.append("schema_invalid")
|
|
if report.get("status") != "authorized":
|
|
errors.append("gate_not_authorized")
|
|
if report.get("implementation_status") != "verified":
|
|
errors.append("implementation_not_verified")
|
|
if report.get("release_scope") != EXPECTED_SCOPE:
|
|
errors.append("scope_invalid")
|
|
if report.get("candidate_eligible") is not True:
|
|
errors.append("candidate_not_eligible")
|
|
if report.get("release_action") != "ALLOW_RELEASE":
|
|
errors.append("release_action_not_allowed")
|
|
if report.get("remaining_blockers") != []:
|
|
errors.append("remaining_blockers_present")
|
|
if report.get("report_sha256") != expected_hash:
|
|
errors.append("report_hash_invalid")
|
|
if not all(item.get("hash_match") is True for item in report.get("evidence", [])):
|
|
errors.append("evidence_hash_invalid")
|
|
if report.get("risk_boundary", {}).get("technical_evidence_rewritten") is not False:
|
|
errors.append("technical_evidence_was_rewritten")
|
|
return {"status": "authorized" if not errors else "blocked", "release_action": "ALLOW_RELEASE" if not errors else "STOP_RELEASE", "scope": report.get("release_scope"), "errors": errors, "report_sha256": report.get("report_sha256")}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--repo-root", type=Path, default=ROOT)
|
|
parser.add_argument("--gate", type=Path, default=GATE)
|
|
args = parser.parse_args()
|
|
result = validate(args.repo_root, args.gate)
|
|
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
|
|
return 0 if result["status"] == "authorized" else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|