This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Exercise the fail-closed artifact execution policy gate."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BLOCKED_SHA256 = "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63"
|
||||
|
||||
|
||||
def run(command: list[str], expected_decision: str) -> dict[str, object]:
|
||||
result = subprocess.run(command, check=False, capture_output=True, text=True)
|
||||
line = result.stdout.strip().splitlines()[-1]
|
||||
document = json.loads(line)
|
||||
if document.get("decision") != expected_decision:
|
||||
raise RuntimeError(f"unexpected decision: {document}")
|
||||
if expected_decision == "DENY" and result.returncode == 0:
|
||||
raise RuntimeError("denied input returned success")
|
||||
if expected_decision != "DENY" and result.returncode != 0:
|
||||
raise RuntimeError(f"eligible input failed: {result.stderr}")
|
||||
if document.get("execution_authorized") is not False:
|
||||
raise RuntimeError("policy gate must never grant execution authority")
|
||||
return document
|
||||
|
||||
|
||||
def without_option(command: list[str], option: str) -> list[str]:
|
||||
index = command.index(option)
|
||||
return command[:index] + command[index + 2 :]
|
||||
|
||||
|
||||
def write_manifest(path: Path, artifact: Path, eligible: bool) -> str:
|
||||
source_commit = "1" * 40
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"artifact": {
|
||||
"filename": artifact.name,
|
||||
"id": "execution-policy-test",
|
||||
"sha256": hashlib.sha256(artifact.read_bytes()).hexdigest(),
|
||||
"size": artifact.stat().st_size,
|
||||
"target": "test",
|
||||
"version": "1",
|
||||
},
|
||||
"execution": {
|
||||
"authorized": False,
|
||||
"executed": False,
|
||||
"execution_eligible": eligible,
|
||||
"transferred": False,
|
||||
},
|
||||
"schema_version": 1,
|
||||
"source": {
|
||||
"commit": source_commit,
|
||||
"dirty": False,
|
||||
"repository": "private-gitea-test",
|
||||
},
|
||||
},
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return source_commit
|
||||
|
||||
|
||||
def write_runtime_profile(
|
||||
path: Path,
|
||||
artifact: Path,
|
||||
source_commit: str,
|
||||
*,
|
||||
decision: str = "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT",
|
||||
firmware: str = "9.60",
|
||||
effect: str = "PAYLOAD_PROCESS_LOCAL",
|
||||
) -> None:
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"artifact": {
|
||||
"built": True,
|
||||
"filename": artifact.name,
|
||||
"id": "execution-policy-test",
|
||||
"sha256": hashlib.sha256(artifact.read_bytes()).hexdigest(),
|
||||
"size": artifact.stat().st_size,
|
||||
"source_commit": source_commit,
|
||||
},
|
||||
"budgets": {
|
||||
"automatic_retry": False,
|
||||
"filesystem_write_budget": "controlled_artifact_directory_only",
|
||||
"maximum_runtime_ms": 2000,
|
||||
"payload_network_access": "none",
|
||||
"persistent_write_budget": "controlled_artifact_removable",
|
||||
},
|
||||
"decision": decision,
|
||||
"deployment": {
|
||||
"installed": False,
|
||||
"ready_for_installation": True,
|
||||
"rollback_prepared": True,
|
||||
},
|
||||
"effects": [{"classification": effect, "id": "test_effect"}],
|
||||
"execution_authorized": False,
|
||||
"expected_volatile_effects": (
|
||||
["test_effect"]
|
||||
if effect == "EXPECTED_VOLATILE_RUNTIME_EFFECT"
|
||||
else []
|
||||
),
|
||||
"execution": {
|
||||
"authorized": False,
|
||||
"executed": False,
|
||||
"execution_eligible": True,
|
||||
"transferred": False,
|
||||
},
|
||||
"firmware": {
|
||||
"device_attested": False,
|
||||
"evidence": "jens_explicitly_confirmed_exact_9.60",
|
||||
"exact": firmware,
|
||||
},
|
||||
"hard_blockers": [],
|
||||
"payload_manager": {
|
||||
"base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1",
|
||||
"hardened_commit": "e23d94ff91233aa770e2342800c1467875bdef44",
|
||||
"installed": False,
|
||||
"release": "v0.3.1-chimera-controlled-phase07",
|
||||
"reproducible": True,
|
||||
"sha256": (
|
||||
"8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1"
|
||||
),
|
||||
"size": 99560,
|
||||
},
|
||||
"loader": {
|
||||
"base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2",
|
||||
"hardened_commit": "197623058f509eddde18868dafcb92fdcac66464",
|
||||
"installed": False,
|
||||
"release": "v0.23-chimera-phase07",
|
||||
"reproducible": True,
|
||||
"sha256": (
|
||||
"63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561"
|
||||
),
|
||||
"size": 397000,
|
||||
},
|
||||
"profile": "controlled-ps5-runtime",
|
||||
"schema_version": 1,
|
||||
"sdk": {
|
||||
"commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37",
|
||||
"release": "v0.41",
|
||||
},
|
||||
},
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
root = args.root.resolve()
|
||||
denylist_path = root / "manifests/artifact-denylist.json"
|
||||
denylist = json.loads(denylist_path.read_text(encoding="utf-8"))
|
||||
if [entry["sha256"] for entry in denylist["entries"]] != [BLOCKED_SHA256]:
|
||||
raise RuntimeError("permanent denylist hash changed or is absent")
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
temporary = Path(directory)
|
||||
artifact = temporary / "test.elf"
|
||||
artifact.write_bytes(b"chimera-execution-policy-test\n")
|
||||
manifest = temporary / "manifest.json"
|
||||
runtime_profile = temporary / "runtime-profile.json"
|
||||
command = [
|
||||
sys.executable,
|
||||
str(root / "tools/check_artifact_execution_policy.py"),
|
||||
"--manifest",
|
||||
str(manifest),
|
||||
"--denylist",
|
||||
str(denylist_path),
|
||||
"--artifact",
|
||||
str(artifact),
|
||||
"--runtime-profile",
|
||||
str(runtime_profile),
|
||||
"--firmware",
|
||||
"9.60",
|
||||
]
|
||||
|
||||
source_commit = write_manifest(manifest, artifact, eligible=False)
|
||||
write_runtime_profile(
|
||||
runtime_profile,
|
||||
artifact,
|
||||
source_commit,
|
||||
decision="BLOCKED_VERSION_OR_UNBOUNDED_EFFECT",
|
||||
)
|
||||
denied = run(command, "DENY")
|
||||
if "MANIFEST_EXECUTION_INELIGIBLE" not in denied["reason_codes"]:
|
||||
raise RuntimeError("execution-ineligible manifest was not refused")
|
||||
|
||||
source_commit = write_manifest(manifest, artifact, eligible=True)
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
run(command, "PASS_STATIC_DEPLOYMENT_ELIGIBILITY_GATE")
|
||||
|
||||
no_artifact = without_option(command, "--artifact")
|
||||
denied = run(no_artifact, "DENY")
|
||||
if "ARTIFACT_BYTES_NOT_SUPPLIED" not in denied["reason_codes"]:
|
||||
raise RuntimeError("missing artifact bytes did not fail closed")
|
||||
|
||||
no_profile = without_option(command, "--runtime-profile")
|
||||
denied = run(no_profile, "DENY")
|
||||
if "CONTROLLED_RUNTIME_PROFILE_REQUIRED" not in denied["reason_codes"]:
|
||||
raise RuntimeError("missing controlled runtime profile did not fail closed")
|
||||
|
||||
write_runtime_profile(
|
||||
runtime_profile,
|
||||
artifact,
|
||||
source_commit,
|
||||
effect="UNBOUNDED_OR_UNKNOWN",
|
||||
)
|
||||
denied = run(command, "DENY")
|
||||
if "RUNTIME_PROFILE_HARD_EFFECT" not in denied["reason_codes"]:
|
||||
raise RuntimeError("unbounded runtime effect did not fail closed")
|
||||
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
document = json.loads(runtime_profile.read_text(encoding="utf-8"))
|
||||
document["expected_volatile_effects"] = ["not_the_classified_effect"]
|
||||
runtime_profile.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(command, "DENY")
|
||||
if (
|
||||
"RUNTIME_PROFILE_VOLATILE_EFFECTS_MISMATCH"
|
||||
not in denied["reason_codes"]
|
||||
):
|
||||
raise RuntimeError("volatile-effect mismatch did not fail closed")
|
||||
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
document = json.loads(runtime_profile.read_text(encoding="utf-8"))
|
||||
del document["expected_volatile_effects"]
|
||||
runtime_profile.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(command, "DENY")
|
||||
if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]:
|
||||
raise RuntimeError("missing volatile-effect declaration did not fail closed")
|
||||
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
document = json.loads(runtime_profile.read_text(encoding="utf-8"))
|
||||
document["execution_authorized"] = True
|
||||
runtime_profile.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(command, "DENY")
|
||||
if (
|
||||
"RUNTIME_PROFILE_EXECUTION_STATE_INVALID"
|
||||
not in denied["reason_codes"]
|
||||
):
|
||||
raise RuntimeError("runtime authorization widening did not fail closed")
|
||||
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
document = json.loads(runtime_profile.read_text(encoding="utf-8"))
|
||||
document["payload_manager"]["hardened_commit"] = "0" * 40
|
||||
runtime_profile.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(command, "DENY")
|
||||
if "PAYLOAD_MANAGER_IDENTITY_MISMATCH" not in denied["reason_codes"]:
|
||||
raise RuntimeError("Payload Manager identity mismatch did not fail closed")
|
||||
|
||||
write_runtime_profile(
|
||||
runtime_profile,
|
||||
artifact,
|
||||
source_commit,
|
||||
effect="NOT_A_CLASSIFICATION",
|
||||
)
|
||||
denied = run(command, "DENY")
|
||||
if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]:
|
||||
raise RuntimeError("unknown effect classification did not fail closed")
|
||||
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
document = json.loads(runtime_profile.read_text(encoding="utf-8"))
|
||||
document["profile"] = "controlled-ps5-lifecycle-v1"
|
||||
runtime_profile.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(command, "DENY")
|
||||
if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]:
|
||||
raise RuntimeError("wrong runtime profile name did not fail closed")
|
||||
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
document = json.loads(manifest.read_text(encoding="utf-8"))
|
||||
document["execution"]["transferred"] = True
|
||||
manifest.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(command, "DENY")
|
||||
if "MANIFEST_EXECUTION_STATE_INVALID" not in denied["reason_codes"]:
|
||||
raise RuntimeError("manifest transfer claim did not fail closed")
|
||||
|
||||
source_commit = write_manifest(manifest, artifact, eligible=True)
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
mismatched_firmware = command.copy()
|
||||
mismatched_firmware[-1] = "9.40"
|
||||
denied = run(mismatched_firmware, "DENY")
|
||||
if "FIRMWARE_MISMATCH" not in denied["reason_codes"]:
|
||||
raise RuntimeError("firmware mismatch did not fail closed")
|
||||
|
||||
document = json.loads(manifest.read_text(encoding="utf-8"))
|
||||
document["artifact"]["sha256"] = BLOCKED_SHA256
|
||||
manifest.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(no_artifact, "DENY")
|
||||
if "ARTIFACT_PERMANENTLY_DENYLISTED" not in denied["reason_codes"]:
|
||||
raise RuntimeError("denylisted hash was not refused")
|
||||
|
||||
del document["execution"]["execution_eligible"]
|
||||
manifest.write_text(json.dumps(document), encoding="utf-8")
|
||||
denied = run(no_artifact, "DENY")
|
||||
if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]:
|
||||
raise RuntimeError("missing eligibility did not fail closed")
|
||||
|
||||
artifact.write_bytes(b"changed\n")
|
||||
source_commit = write_manifest(manifest, artifact, eligible=True)
|
||||
write_runtime_profile(runtime_profile, artifact, source_commit)
|
||||
artifact.write_bytes(b"changed-again\n")
|
||||
denied = run(command, "DENY")
|
||||
if "ARTIFACT_DIGEST_MISMATCH" not in denied["reason_codes"]:
|
||||
raise RuntimeError("changed bytes were not refused")
|
||||
|
||||
print("artifact execution policy gate passed all refusal tests")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user