This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Twenty-two host-only guardrails for Phase 0.9E-R."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from types import ModuleType
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def load_module(name: str, path: Path) -> ModuleType:
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise RuntimeError(f"could not load {path}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def require(condition: bool, message: str) -> None:
|
||||
if not condition:
|
||||
raise RuntimeError(message)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
root = args.root.resolve()
|
||||
validator = load_module(
|
||||
"phase09er_validator", root / "tools/validate_phase09er_provenance.py"
|
||||
)
|
||||
manifest = validator.load_json(
|
||||
root / "manifests/runtime/phase-0.9e-r-release-correlation.json"
|
||||
)
|
||||
port_manifest = validator.load_json(
|
||||
root / "manifests/runtime/phase-0.9e-r-port9020-audit.json"
|
||||
)
|
||||
cases: list[tuple[str, Callable[[], None]]] = []
|
||||
|
||||
def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]:
|
||||
def register(function: Callable[[], None]) -> Callable[[], None]:
|
||||
cases.append((name, function))
|
||||
return function
|
||||
|
||||
return register
|
||||
|
||||
@case("01 name match alone is not a byte match")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.official_byte_match(
|
||||
official_source=True,
|
||||
local_name="same.zip",
|
||||
local_size=1,
|
||||
local_sha256="a" * 64,
|
||||
asset_name="same.zip",
|
||||
asset_size=2,
|
||||
asset_sha256="b" * 64,
|
||||
),
|
||||
"name-only candidate became a byte match",
|
||||
)
|
||||
|
||||
@case("02 size match alone is not a byte match")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.official_byte_match(
|
||||
official_source=True,
|
||||
local_name="local.zip",
|
||||
local_size=10,
|
||||
local_sha256="a" * 64,
|
||||
asset_name="asset.zip",
|
||||
asset_size=10,
|
||||
asset_sha256="b" * 64,
|
||||
),
|
||||
"size-only candidate became a byte match",
|
||||
)
|
||||
|
||||
@case("03 hash match also requires exact byte count")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.official_byte_match(
|
||||
official_source=True,
|
||||
local_name="same.zip",
|
||||
local_size=10,
|
||||
local_sha256="a" * 64,
|
||||
asset_name="same.zip",
|
||||
asset_size=11,
|
||||
asset_sha256="a" * 64,
|
||||
),
|
||||
"hash with a different byte count became a match",
|
||||
)
|
||||
|
||||
@case("04 a mirror cannot establish an official byte match")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.official_byte_match(
|
||||
official_source=False,
|
||||
local_name="same.zip",
|
||||
local_size=10,
|
||||
local_sha256="a" * 64,
|
||||
asset_name="same.zip",
|
||||
asset_size=10,
|
||||
asset_sha256="a" * 64,
|
||||
),
|
||||
"non-official mirror established an official match",
|
||||
)
|
||||
|
||||
@case("05 release association is not inner-content binding")
|
||||
def _() -> None:
|
||||
require(
|
||||
validator.source_binding(
|
||||
release_associated=True,
|
||||
inner_bytes_matched=False,
|
||||
inner_opaque=True,
|
||||
)
|
||||
== "SOURCE_ONLY_ASSOCIATION",
|
||||
"release association was promoted to reproducible content",
|
||||
)
|
||||
|
||||
@case("06 opaque SIECAF receives no invented provenance")
|
||||
def _() -> None:
|
||||
require(
|
||||
manifest["inner_archive"]["classification"] == "OPAQUE_UNBOUND"
|
||||
and manifest["inner_archive"]["further_reverse_engineering_performed"]
|
||||
is False,
|
||||
"opaque inner archive provenance was overclaimed",
|
||||
)
|
||||
|
||||
@case("07 public release commits are exact")
|
||||
def _() -> None:
|
||||
for tag in manifest["tags"]:
|
||||
require(
|
||||
len(tag["commit"]) == 40
|
||||
and all(character in "0123456789abcdef" for character in tag["commit"]),
|
||||
f"release commit is not exact: {tag['tag']}",
|
||||
)
|
||||
|
||||
@case("08 each source archive is hashed")
|
||||
def _() -> None:
|
||||
for tag in manifest["tags"]:
|
||||
require(
|
||||
len(tag["source_archive_sha256"]) == 64,
|
||||
f"source archive hash missing: {tag['tag']}",
|
||||
)
|
||||
|
||||
@case("09 upstream worktree must be clean")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["upstream_worktree"]["clean"] = False
|
||||
require(
|
||||
bool(validator.validate_release_manifest(changed)),
|
||||
"dirty upstream worktree passed",
|
||||
)
|
||||
|
||||
@case("10 port reference is not an implementation")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.port_implementation_sufficient("PORT_9020_REFERENCE_ONLY"),
|
||||
"reference-only text was accepted as implementation",
|
||||
)
|
||||
|
||||
@case("11 embedded bytes receive no invented source commit")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(port_manifest)
|
||||
changed["port_9021_relation"]["source_commit"] = "0" * 40
|
||||
require(
|
||||
bool(validator.validate_port_manifest(changed)),
|
||||
"embedded loader bytes accepted an invented source commit",
|
||||
)
|
||||
|
||||
@case("12 a sender without receive is not duplex")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.sender_duplex(sends=True, receives=False),
|
||||
"one-way sender became duplex",
|
||||
)
|
||||
|
||||
@case("13 missing short-send handling is a deficiency")
|
||||
def _() -> None:
|
||||
require(
|
||||
validator.sender_short_send_deficiency(
|
||||
uses_sendall=False, explicit_send_loop=False
|
||||
),
|
||||
"missing short-send handling was accepted",
|
||||
)
|
||||
require(
|
||||
not validator.sender_short_send_deficiency(
|
||||
uses_sendall=True, explicit_send_loop=False
|
||||
),
|
||||
"sendall was incorrectly marked as lacking short-send handling",
|
||||
)
|
||||
|
||||
@case("14 operator attestation is not runtime evidence")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.attestation_is_runtime_proof(
|
||||
attested=True, hardware_observed=False
|
||||
),
|
||||
"operator attestation became runtime proof",
|
||||
)
|
||||
|
||||
@case("15 empty attestation authorizes nothing")
|
||||
def _() -> None:
|
||||
text = (
|
||||
root
|
||||
/ "docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md"
|
||||
).read_text(encoding="utf-8")
|
||||
require("attested: false" in text, "template became attested")
|
||||
for field in validator.AUTHORIZATION_FIELDS:
|
||||
require(f"{field}: false" in text, f"template omits false {field}")
|
||||
|
||||
@case("16 Phase 0.9F blocks without official match")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.phase09f_design_allowed(
|
||||
correlation="OFFICIAL_RELEASE_NO_MATCH",
|
||||
release_commit_known=True,
|
||||
upstream_clean=True,
|
||||
port_classification="PORT_9020_IMPLEMENTATION_FOUND",
|
||||
sender_identified=True,
|
||||
attestation_available=True,
|
||||
all_authorizations_false=True,
|
||||
),
|
||||
"Phase 0.9F passed without an official asset match",
|
||||
)
|
||||
|
||||
@case("17 Phase 0.9F blocks without found or partial loader")
|
||||
def _() -> None:
|
||||
require(
|
||||
not validator.phase09f_design_allowed(
|
||||
correlation="OFFICIAL_RELEASE_BYTE_MATCH",
|
||||
release_commit_known=True,
|
||||
upstream_clean=True,
|
||||
port_classification="PORT_9020_REFERENCE_ONLY",
|
||||
sender_identified=True,
|
||||
attestation_available=True,
|
||||
all_authorizations_false=True,
|
||||
),
|
||||
"Phase 0.9F passed with a reference-only loader",
|
||||
)
|
||||
|
||||
@case("18 no target source appears")
|
||||
def _() -> None:
|
||||
errors = validator.phase09er_path_errors(
|
||||
{"docs/runtime/phase-0.9e-r-note.md", "src/backends/ps5/rescue.c"}
|
||||
)
|
||||
require(any("target" in error for error in errors), "target source guard failed")
|
||||
require(
|
||||
not validator.phase09er_path_errors(
|
||||
{"docs/runtime/phase-0.9e-r-note.md"}
|
||||
),
|
||||
"documentation was rejected as target source",
|
||||
)
|
||||
|
||||
@case("19 no target artifact appears")
|
||||
def _() -> None:
|
||||
require(
|
||||
bool(validator.phase09er_path_errors({"packaging/rescue.elf"})),
|
||||
"target artifact guard failed",
|
||||
)
|
||||
|
||||
@case("20 all authorization fields remain false")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["authorization"]["execution_authorized"] = True
|
||||
require(
|
||||
bool(validator.validate_release_manifest(changed)),
|
||||
"true authorization passed",
|
||||
)
|
||||
|
||||
@case("21 automatic retry remains false")
|
||||
def _() -> None:
|
||||
require(
|
||||
manifest["authorization"]["automatic_retry"] is False
|
||||
and port_manifest["authorization"]["automatic_retry"] is False
|
||||
and port_manifest["official_remote_js_loader"][
|
||||
"automatic_retry_authorized"
|
||||
]
|
||||
is False,
|
||||
"automatic retry was authorized",
|
||||
)
|
||||
|
||||
@case("22 no large release asset enters Git")
|
||||
def _() -> None:
|
||||
actions = manifest["actions"]
|
||||
require(
|
||||
actions["large_release_assets_downloaded"] == 0
|
||||
and actions["large_release_asset_bytes_downloaded"] == 0,
|
||||
"a large official release asset was recorded as downloaded",
|
||||
)
|
||||
for path in root.rglob("*"):
|
||||
if (
|
||||
path.is_file()
|
||||
and ".git" not in path.parts
|
||||
and "work" not in path.parts
|
||||
):
|
||||
require(
|
||||
path.stat().st_size <= 50 * 1024 * 1024,
|
||||
f"large release-like file entered repository: {path}",
|
||||
)
|
||||
|
||||
failures: list[str] = []
|
||||
for name, function in cases:
|
||||
try:
|
||||
function()
|
||||
except Exception as error: # guardrail harness reports all failures
|
||||
failures.append(f"{name}: {error}")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
return 1
|
||||
print(f"Phase-0.9E-R provenance guardrails: {len(cases)}/{len(cases)} PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user