This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Thirty host-only guardrails for the Phase-0.9D readback design."""
|
||||
|
||||
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(
|
||||
"phase09d_validator", root / "tools/validate_phase09d_readback.py"
|
||||
)
|
||||
manifest = validator.load_json(
|
||||
root / "manifests/runtime/phase-0.9d-existing-stack-readback.json"
|
||||
)
|
||||
schema = validator.load_json(
|
||||
root
|
||||
/ "manifests/runtime/phase-0.9d-existing-stack-readback.schema.json"
|
||||
)
|
||||
defaults = manifest["endpoint_defaults"]
|
||||
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
|
||||
|
||||
def candidate(**changes: object) -> dict[str, object]:
|
||||
route: dict[str, object] = {
|
||||
"readback_candidate": True,
|
||||
"open_flags": ["O_RDONLY", "O_NOFOLLOW"],
|
||||
"writes_bytes": False,
|
||||
"creates_file": False,
|
||||
"removes_file": False,
|
||||
"renames_file": False,
|
||||
"modifies_configuration": False,
|
||||
"launches_payload": False,
|
||||
"process_or_service_action": False,
|
||||
"writes_autoload_triggered": False,
|
||||
"binary_safe_file_response": True,
|
||||
"exact_returned_byte_count": True,
|
||||
"partial_result_rejected": True,
|
||||
"short_read_behavior": "DETECTED_AND_INVALID",
|
||||
"automatic_retry": False,
|
||||
"automatic_resume": False,
|
||||
}
|
||||
route.update(changes)
|
||||
return route
|
||||
|
||||
def invalid_manifest(changed: dict[str, object], message: str) -> None:
|
||||
require(bool(validator.validate_manifest(changed)), message)
|
||||
|
||||
@case("01 device write can never be a readback candidate")
|
||||
def _() -> None:
|
||||
errors = validator.route_readback_errors(
|
||||
candidate(writes_bytes=True), defaults
|
||||
)
|
||||
require(bool(errors), "device-write candidate passed")
|
||||
|
||||
@case("02 rename unlink create and truncate are rejected")
|
||||
def _() -> None:
|
||||
mutations = (
|
||||
{"renames_file": True},
|
||||
{"removes_file": True},
|
||||
{"creates_file": True},
|
||||
{"open_flags": ["fopen(wb)"]},
|
||||
)
|
||||
for mutation in mutations:
|
||||
require(
|
||||
bool(
|
||||
validator.route_readback_errors(
|
||||
candidate(**mutation), defaults
|
||||
)
|
||||
),
|
||||
f"mutating route passed: {mutation}",
|
||||
)
|
||||
|
||||
@case("03 payload launch is rejected")
|
||||
def _() -> None:
|
||||
require(
|
||||
bool(
|
||||
validator.route_readback_errors(
|
||||
candidate(launches_payload=True), defaults
|
||||
)
|
||||
),
|
||||
"launch route passed",
|
||||
)
|
||||
|
||||
@case("04 autoload_triggered routes are excluded")
|
||||
def _() -> None:
|
||||
require(
|
||||
bool(
|
||||
validator.route_readback_errors(
|
||||
candidate(writes_autoload_triggered=True), defaults
|
||||
)
|
||||
),
|
||||
"autoload mutation passed",
|
||||
)
|
||||
require(
|
||||
manifest["flag_semantics"]["autoload_triggered"]["excluded_windows"]
|
||||
== [1, 2],
|
||||
"autoload route is not excluded from Windows 1 and 2",
|
||||
)
|
||||
|
||||
@case("05 server_active alone is not anti-brick critical")
|
||||
def _() -> None:
|
||||
semantics = manifest["flag_semantics"]["server_active_flag"]
|
||||
require(
|
||||
semantics["classification"] == "LOW_VOLATILE",
|
||||
"server_active is overclassified",
|
||||
)
|
||||
require(
|
||||
validator.server_active_observation_status(semantics) == "PARTIAL",
|
||||
"server_active route should remain partial",
|
||||
)
|
||||
|
||||
@case("06 server_active semantics must be complete")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["flag_semantics"]["server_active_flag"]["fully_documented"] = False
|
||||
invalid_manifest(changed, "incomplete server_active semantics passed")
|
||||
|
||||
@case("07 missing reset semantics blocks observation")
|
||||
def _() -> None:
|
||||
semantics = copy.deepcopy(
|
||||
manifest["flag_semantics"]["server_active_flag"]
|
||||
)
|
||||
semantics["reset_path"] = "UNPROVEN"
|
||||
require(
|
||||
validator.server_active_observation_status(semantics) == "BLOCKED",
|
||||
"unknown reset semantics did not block",
|
||||
)
|
||||
|
||||
@case("08 file read without binary framing is invalid")
|
||||
def _() -> None:
|
||||
require(
|
||||
bool(
|
||||
validator.route_readback_errors(
|
||||
candidate(binary_safe_file_response=False), defaults
|
||||
)
|
||||
),
|
||||
"text-framed file route passed",
|
||||
)
|
||||
|
||||
@case("09 file read without short-read detection is invalid")
|
||||
def _() -> None:
|
||||
require(
|
||||
bool(
|
||||
validator.route_readback_errors(
|
||||
candidate(short_read_behavior="UNPROVEN"), defaults
|
||||
)
|
||||
),
|
||||
"short-read-unsafe route passed",
|
||||
)
|
||||
|
||||
@case("10 partial host transfer is invalid")
|
||||
def _() -> None:
|
||||
record = {
|
||||
"status": "TRANSFER_INCOMPLETE",
|
||||
"transfer_complete": False,
|
||||
"automatic_resume": False,
|
||||
"automatic_retry": False,
|
||||
"recovery_proven": False,
|
||||
}
|
||||
require(
|
||||
bool(validator.backup_record_errors(record)),
|
||||
"partial transfer was not invalidated",
|
||||
)
|
||||
|
||||
@case("11 automatic resume is forbidden")
|
||||
def _() -> None:
|
||||
record = {
|
||||
"status": "INVALID",
|
||||
"automatic_resume": True,
|
||||
"automatic_retry": False,
|
||||
"recovery_proven": False,
|
||||
}
|
||||
require(
|
||||
bool(validator.backup_record_errors(record)),
|
||||
"automatic resume passed",
|
||||
)
|
||||
|
||||
@case("12 automatic retry is forbidden")
|
||||
def _() -> None:
|
||||
record = {
|
||||
"status": "INVALID",
|
||||
"automatic_resume": False,
|
||||
"automatic_retry": True,
|
||||
"recovery_proven": False,
|
||||
}
|
||||
require(
|
||||
bool(validator.backup_record_errors(record)),
|
||||
"automatic retry passed",
|
||||
)
|
||||
|
||||
@case("13 two readbacks must match size hash and bytes")
|
||||
def _() -> None:
|
||||
record = {
|
||||
"status": "COPIES_MATCH",
|
||||
"transfer_complete": True,
|
||||
"automatic_resume": False,
|
||||
"automatic_retry": False,
|
||||
"closed_and_reopened": True,
|
||||
"exact_byte_count": 8,
|
||||
"sha256": "0" * 64,
|
||||
"sizes_match": True,
|
||||
"hashes_match": True,
|
||||
"bytes_match": False,
|
||||
"recovery_proven": False,
|
||||
}
|
||||
require(
|
||||
bool(validator.backup_record_errors(record)),
|
||||
"byte mismatch passed",
|
||||
)
|
||||
|
||||
@case("14 hash without exact byte count is insufficient")
|
||||
def _() -> None:
|
||||
record = {
|
||||
"status": "HOST_COPY_HASHED",
|
||||
"transfer_complete": True,
|
||||
"automatic_resume": False,
|
||||
"automatic_retry": False,
|
||||
"closed_and_reopened": True,
|
||||
"sha256": "0" * 64,
|
||||
"recovery_proven": False,
|
||||
}
|
||||
require(
|
||||
bool(validator.backup_record_errors(record)),
|
||||
"hash without count passed",
|
||||
)
|
||||
|
||||
@case("15 host backup is not recovery proof")
|
||||
def _() -> None:
|
||||
record = {
|
||||
"status": "COPIES_MATCH",
|
||||
"transfer_complete": True,
|
||||
"automatic_resume": False,
|
||||
"automatic_retry": False,
|
||||
"closed_and_reopened": True,
|
||||
"exact_byte_count": 8,
|
||||
"sha256": "0" * 64,
|
||||
"sizes_match": True,
|
||||
"hashes_match": True,
|
||||
"bytes_match": True,
|
||||
"recovery_proven": True,
|
||||
}
|
||||
require(
|
||||
bool(validator.backup_record_errors(record)),
|
||||
"backup incorrectly proved recovery",
|
||||
)
|
||||
|
||||
@case("16 replacement-dependent recovery is self-dependent")
|
||||
def _() -> None:
|
||||
require(
|
||||
validator.recovery_dependency_classification(
|
||||
"payload_manager", "payload_manager"
|
||||
)
|
||||
== "SELF_DEPENDENT",
|
||||
"self-dependence was not detected",
|
||||
)
|
||||
|
||||
@case("17 package path cannot become live")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["path_inventory"][0]["live"] = True
|
||||
changed["runtime_observed_live_paths"] = [
|
||||
changed["path_inventory"][0]["path"]
|
||||
]
|
||||
invalid_manifest(changed, "offline path was promoted to live")
|
||||
|
||||
@case("18 conflicting paths retain PATH_CONFLICT")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["path_classification"] = "RESOLVED"
|
||||
invalid_manifest(changed, "path conflict was silently resolved")
|
||||
|
||||
@case("19 Window 1 contains no file transfer")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["operational_windows"][0]["file_transfer"] = True
|
||||
invalid_manifest(changed, "Window 1 transfer passed")
|
||||
|
||||
@case("20 Window 2 contains no device write")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["operational_windows"][1]["device_write"] = True
|
||||
invalid_manifest(changed, "Window 2 write passed")
|
||||
|
||||
@case("21 Window 2 contains no launch")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["operational_windows"][1]["payload_launch"] = True
|
||||
invalid_manifest(changed, "Window 2 launch passed")
|
||||
|
||||
@case("22 Window 2 excludes autoload_status")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["operational_windows"][1]["autoload_status_route"] = True
|
||||
invalid_manifest(changed, "Window 2 autoload_status passed")
|
||||
|
||||
@case("23 Window 3 has no automatic third attempt")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["operational_windows"][2]["automatic_third_attempt"] = True
|
||||
invalid_manifest(changed, "automatic third attempt passed")
|
||||
|
||||
@case("24 components have separate windows")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["operational_windows"][3]["component_session_separate"] = False
|
||||
invalid_manifest(changed, "combined component window passed")
|
||||
|
||||
@case("25 side-by-side does not authorize installation")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["side_by_side"]["grants_installation_authorization"] = True
|
||||
invalid_manifest(changed, "side-by-side authorized installation")
|
||||
|
||||
@case("26 no observer artifact appears")
|
||||
def _() -> None:
|
||||
require(
|
||||
manifest["actions"]["observer_created"] is False
|
||||
and manifest["actions"]["target_artifact_created"] is False,
|
||||
"observer artifact recorded",
|
||||
)
|
||||
require(validator.collect_errors(root) == [], "offline audit invalid")
|
||||
|
||||
@case("27 no target code appears")
|
||||
def _() -> None:
|
||||
forbidden = {".c", ".cc", ".cpp", ".s", ".asm", ".ld", ".elf"}
|
||||
for relative in validator._phase09d_paths(root):
|
||||
require(
|
||||
Path(relative).suffix.lower() not in forbidden,
|
||||
f"target code/artifact appeared: {relative}",
|
||||
)
|
||||
|
||||
@case("28 host tests are not hardware evidence")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["final_decision"]["hardware_evidence_claimed"] = True
|
||||
invalid_manifest(changed, "host test became hardware proof")
|
||||
|
||||
@case("29 every authorization field stays false")
|
||||
def _() -> None:
|
||||
for field in validator.AUTHORIZATION_FIELDS:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["authorization"][field] = True
|
||||
invalid_manifest(changed, f"authorization passed: {field}")
|
||||
|
||||
@case("30 Manager backup remains installation blocker")
|
||||
def _() -> None:
|
||||
changed = copy.deepcopy(manifest)
|
||||
changed["canonical_state"]["payload_manager_backup"] = "COMPLETE"
|
||||
invalid_manifest(changed, "Manager backup blocker was removed")
|
||||
|
||||
require(len(cases) == 30, f"expected 30 guardrails, found {len(cases)}")
|
||||
require(
|
||||
validator.validate_schema_instance(schema, manifest) == [],
|
||||
"manifest does not satisfy schema",
|
||||
)
|
||||
|
||||
failures: list[str] = []
|
||||
for name, function in cases:
|
||||
try:
|
||||
function()
|
||||
except Exception as error: # noqa: BLE001 - standalone test harness
|
||||
failures.append(f"{name}: {error}")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
return 1
|
||||
print("Phase-0.9D readback guardrails: 30/30 PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user