200 lines
7.2 KiB
Python
200 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Host guardrails for the blocked Phase-0.9C feasibility closure."""
|
|
|
|
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 require_invalid(errors: list[str], scenario: str) -> None:
|
|
require(bool(errors), f"unsafe Phase-0.9C mutation passed: {scenario}")
|
|
|
|
|
|
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(
|
|
"phase09c_validator", root / "tools/validate_phase09c_feasibility.py"
|
|
)
|
|
manifest = validator.load_json(
|
|
root / "manifests/runtime/phase-0.9c-feasibility.json"
|
|
)
|
|
schema = validator.load_json(
|
|
root / "manifests/runtime/phase-0.9c-feasibility.schema.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("complete offline validator")
|
|
def _() -> None:
|
|
require(validator.collect_errors(root) == [], "current audit does not validate")
|
|
|
|
@case("manifest and schema")
|
|
def _() -> None:
|
|
require(validator.validate_manifest(manifest) == [], "manifest invalid")
|
|
require(
|
|
validator.validate_schema_instance(schema, manifest) == [],
|
|
"schema rejected manifest",
|
|
)
|
|
|
|
@case("authorization remains false")
|
|
def _() -> None:
|
|
for field in validator.AUTHORIZATION_FIELDS:
|
|
changed = copy.deepcopy(manifest)
|
|
changed["authorization"][field] = True
|
|
require_invalid(validator.validate_manifest(changed), field)
|
|
|
|
@case("positive classification rejected")
|
|
def _() -> None:
|
|
changed = copy.deepcopy(manifest)
|
|
changed["status"] = "READY"
|
|
changed["classification"] = (
|
|
"FEASIBILITY_CONTRACT_PROVEN_NO_TARGET_IMPLEMENTATION"
|
|
)
|
|
changed["final_decision"]["positive_classification_allowed"] = True
|
|
changed["final_decision"]["classification"] = changed["classification"]
|
|
require_invalid(validator.validate_manifest(changed), "positive decision")
|
|
|
|
@case("startup and cleanup promotion rejected")
|
|
def _() -> None:
|
|
for field in (
|
|
"normal_sdk_kernelwrite_free",
|
|
"freestanding_dependency_closure_proven",
|
|
"safe_return_proven",
|
|
"safe_process_exit_proven",
|
|
"error_exit_proven",
|
|
"timeout_safe_exit_proven",
|
|
"complete_cleanup_proven",
|
|
):
|
|
changed = copy.deepcopy(manifest)
|
|
changed["startup_exit"][field] = True
|
|
require_invalid(validator.validate_manifest(changed), field)
|
|
|
|
@case("fabricated firmware source rejected")
|
|
def _() -> None:
|
|
changed = copy.deepcopy(manifest)
|
|
changed["firmware"]["source_two"]["identity"] = "invented"
|
|
changed["firmware"]["source_two"]["status"] = "PROVEN"
|
|
changed["firmware"]["agreement_proven"] = True
|
|
require_invalid(validator.validate_manifest(changed), "firmware source two")
|
|
|
|
@case("output implementation promotion rejected")
|
|
def _() -> None:
|
|
changed = copy.deepcopy(manifest)
|
|
changed["output_architectures"][0]["current_implementation"] = True
|
|
changed["host_protocol"]["target_implemented"] = True
|
|
require_invalid(validator.validate_manifest(changed), "output implementation")
|
|
|
|
@case("capability implementation and execution rejected")
|
|
def _() -> None:
|
|
changed = copy.deepcopy(manifest)
|
|
changed["capability_closure"][0]["implementation_allowed"] = True
|
|
changed["capability_closure"][0]["execution_allowed"] = True
|
|
changed["capability_closure"][0]["target_evidence"] = "PROVEN"
|
|
require_invalid(validator.validate_manifest(changed), "capability promotion")
|
|
|
|
@case("artifact and package rejected")
|
|
def _() -> None:
|
|
changed = copy.deepcopy(manifest)
|
|
changed["artifact"] = {
|
|
"present": True,
|
|
"path": "phase09c-observer.elf",
|
|
"sha256": "1" * 64,
|
|
"size": 1,
|
|
"execution_eligible": False,
|
|
"execution_authorized": False,
|
|
}
|
|
changed["implementation"]["target_elf_present"] = True
|
|
require_invalid(validator.validate_manifest(changed), "artifact")
|
|
for path in (
|
|
"samples/phase09c_observer/main.c",
|
|
"outputs/phase09c-observer.elf",
|
|
"outputs/phase-0.9c-observer.map",
|
|
"packaging/phase09c/install.zip",
|
|
"packaging/phase09c/lifecycle.pkg",
|
|
"packaging/phase09c/autoload.json",
|
|
):
|
|
require(validator.forbidden_repository_path(path), path)
|
|
|
|
@case("host files remain permitted")
|
|
def _() -> None:
|
|
for path in (
|
|
"docs/runtime/phase-0.9c-static-audit.md",
|
|
"tests/phase09c_feasibility_model.py",
|
|
"tests/test_phase09c_protocol.py",
|
|
"tools/validate_phase09c_feasibility.py",
|
|
"packaging/phase09c/SHA256SUMS.txt",
|
|
):
|
|
require(not validator.forbidden_repository_path(path), path)
|
|
|
|
@case("side-effect false promotion rejected")
|
|
def _() -> None:
|
|
for field in (
|
|
"no_persistent_content_write_is_side_effect_free",
|
|
"read_only_flag_is_side_effect_free",
|
|
"all_planned_observations_proven_side_effect_free",
|
|
):
|
|
changed = copy.deepcopy(manifest)
|
|
changed["side_effect_model"][field] = True
|
|
require_invalid(validator.validate_manifest(changed), field)
|
|
|
|
@case("permanent denylist and immutable evidence")
|
|
def _() -> None:
|
|
require(
|
|
validator.validate_immutable_evidence(root) == [],
|
|
"immutable evidence changed",
|
|
)
|
|
denylist = validator.load_json(root / "manifests/artifact-denylist.json")
|
|
entry = denylist["entries"][0]
|
|
require(entry["sha256"] == validator.BLOCKED_HASH, "denylist hash changed")
|
|
require(entry["permanent"] is True, "denylist is not permanent")
|
|
require(
|
|
entry["execution_eligible"] is False,
|
|
"denylisted artifact became eligible",
|
|
)
|
|
|
|
for name, function in cases:
|
|
try:
|
|
function()
|
|
except Exception as error:
|
|
raise RuntimeError(
|
|
f"Phase-0.9C feasibility case failed: {name}: {error}"
|
|
) from error
|
|
|
|
print(f"Phase-0.9C feasibility host tests: {len(cases)}/{len(cases)} PASS")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|