72 lines
2.8 KiB
Python
72 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Validate the fail-closed Phase-0.5 machine-readable decision records."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
|
|
BLOCKED_SHA256 = "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63"
|
|
|
|
|
|
def load(path: Path) -> dict[str, object]:
|
|
document = json.loads(path.read_text(encoding="utf-8"))
|
|
if not isinstance(document, dict):
|
|
raise RuntimeError(f"{path}: expected object")
|
|
return document
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--root", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
root = args.root.resolve()
|
|
|
|
audit = load(root / "manifests/runtime/phase-0.5-startup-audit.json")
|
|
decision = load(root / "manifests/runtime/minimal-startup-artifact-decision.json")
|
|
matrix = load(root / "manifests/runtime/kernelwrite-proof-matrix.json")
|
|
denylist = load(root / "manifests/artifact-denylist.json")
|
|
|
|
if audit.get("decision") != "BLOCKED" or audit["artifact"]["built"] is not False:
|
|
raise RuntimeError("startup audit no longer blocks artifact construction")
|
|
if audit["artifact"]["execution_eligible"] is not False:
|
|
raise RuntimeError("startup audit claims execution eligibility")
|
|
if audit["loader_evidence"]["caller_source_present"] is not False:
|
|
raise RuntimeError("audit claims an unreviewed loader caller")
|
|
required_reachable = {
|
|
"__patch_init",
|
|
"kernel_copyin",
|
|
"kernel_copyout",
|
|
"kernel_set_ucred_attrs",
|
|
"kernel_set_ucred_caps",
|
|
}
|
|
reachable = set(audit["crt1_static_evidence"]["reachable_prohibited_functions"])
|
|
if reachable != required_reachable:
|
|
raise RuntimeError(f"reachable kernel-write inventory changed: {reachable}")
|
|
|
|
if decision.get("decision") != "BLOCKED" or decision["artifact"]["built"] is not False:
|
|
raise RuntimeError("non-build decision changed")
|
|
if decision["execution"]["execution_eligible"] is not False:
|
|
raise RuntimeError("non-build record claims execution eligibility")
|
|
if any(value is not None for value in (
|
|
decision["artifact"]["filename"],
|
|
decision["artifact"]["sha256"],
|
|
decision["artifact"]["size"],
|
|
)):
|
|
raise RuntimeError("non-build decision fabricates artifact bytes")
|
|
|
|
statuses = {entry["status"] for entry in matrix["entries"]}
|
|
if statuses != {"SAFE", "UNSAFE", "UNPROVEN"}:
|
|
raise RuntimeError(f"review matrix lacks a status class: {statuses}")
|
|
if [entry["sha256"] for entry in denylist["entries"]] != [BLOCKED_SHA256]:
|
|
raise RuntimeError("permanent blocked hash changed")
|
|
print("Phase-0.5 machine-readable BLOCKED decision is consistent")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|