feat(mcp): add immutable external artifact mirror lane

This commit is contained in:
ogt
2026-07-15 09:42:46 +08:00
parent 618e499471
commit 4dbd5875af
8 changed files with 3292 additions and 8 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
from __future__ import annotations
import base64
import importlib.util
import io
import json
import sys
import tarfile
import tempfile
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[3]
CONTROLLER_PATH = ROOT / "scripts/security/external_mcp_artifact_controller.py"
POLICY_PATH = ROOT / "config/mcp/playwright-mcp-artifact-policy.json"
WORKFLOW_PATH = ROOT / ".gitea/workflows/mcp-external-artifact-mirror.yaml"
RUN_ID = "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"
TRACE_ID = f"mcp-artifact-{RUN_ID}"
def _load_controller():
spec = importlib.util.spec_from_file_location(
"external_mcp_artifact_controller", CONTROLLER_PATH
)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
controller = _load_controller()
def _verified_packages(policy):
return tuple(
controller.VerifiedPackage(
policy=package,
tarball=b"tarball",
attestation=b"{}",
metadata_sha256="a" * 64,
tarball_sha512_hex=package.sha512_hex,
tarball_sha1=package.shasum_sha1,
attestation_sha256="b" * 64,
unpacked_bytes=1024,
archive_file_count=3,
license_file_count=1,
osv_response_sha256="c" * 64,
vulnerability_ids=(),
)
for package in policy.packages
)
def _tarball(
package,
*,
unsafe_symlink: bool = False,
duplicate_manifest: bool = False,
) -> bytes:
package_json = json.dumps(
{
"name": package.name,
"version": package.version,
"license": package.license,
"dependencies": package.dependencies,
"optionalDependencies": package.optional_dependencies,
}
).encode()
output = io.BytesIO()
with tarfile.open(fileobj=output, mode="w:gz") as archive:
for name, data in (
("package/package.json", package_json),
("package/LICENSE", b"Apache License 2.0"),
):
member = tarfile.TarInfo(name)
member.size = len(data)
archive.addfile(member, io.BytesIO(data))
if duplicate_manifest:
member = tarfile.TarInfo("package/package.json")
member.size = len(package_json)
archive.addfile(member, io.BytesIO(package_json))
if unsafe_symlink:
member = tarfile.TarInfo("package/escape")
member.type = tarfile.SYMTYPE
member.linkname = "../../outside"
archive.addfile(member)
return output.getvalue()
class ExternalMcpArtifactControllerTest(unittest.TestCase):
def test_policy_is_exact_discovery_only_and_fail_closed(self) -> None:
policy = controller.load_policy(POLICY_PATH)
raw = POLICY_PATH.read_text(encoding="utf-8").lower()
self.assertEqual(policy.candidate_id, "external.playwright-verifier")
self.assertEqual(policy.risk_level, "high")
self.assertEqual(len(policy.packages), 3)
self.assertEqual(policy.mirror_tag, "0.0.78-bundle-v1")
self.assertNotIn("@latest", raw)
self.assertNotIn(":latest", raw)
self.assertNotIn("npx -y", raw)
self.assertNotIn("github.com", raw)
self.assertNotIn("ghcr.io", raw)
self.assertTrue(all(package.version for package in policy.packages))
self.assertTrue(all(package.sha512_hex for package in policy.packages))
def test_gitea_workflow_has_no_remote_action_or_floating_supply_chain(self) -> None:
raw = WORKFLOW_PATH.read_text(encoding="utf-8")
lowered = raw.lower()
self.assertNotIn("uses:", lowered)
self.assertNotIn("actions/checkout", lowered)
self.assertNotIn("github.com", lowered)
self.assertNotIn("api.github.com", lowered)
self.assertNotIn("raw.githubusercontent.com", lowered)
self.assertNotIn("codeload.github.com", lowered)
self.assertNotIn("ghcr.io", lowered)
self.assertNotIn("npx -y", lowered)
self.assertNotIn("@latest", lowered)
self.assertNotIn(":latest", lowered)
self.assertNotIn("git push --force", lowered)
self.assertIn("runs-on: awoooi-non110-host", raw)
self.assertIn("docker login", raw)
self.assertIn("--password-stdin", raw)
self.assertIn("docker logout", raw)
self.assertIn("verify_external_mcp_artifact_receipt.py", raw)
self.assertIn("git merge --no-edit gitea/main", raw)
self.assertIn("git push gitea HEAD:main", raw)
self.assertIn('cron: "13 2 * * 3"', raw)
def test_dependency_closure_rejects_drift(self) -> None:
payload = json.loads(POLICY_PATH.read_text(encoding="utf-8"))
payload["packages"][0]["dependencies"]["playwright"] = "1.2.3"
with tempfile.TemporaryDirectory() as temp:
path = Path(temp) / "policy.json"
path.write_text(json.dumps(payload), encoding="utf-8")
with self.assertRaisesRegex(
controller.ControllerError, "dependency_closure_invalid"
):
controller.load_policy(path)
def test_run_and_trace_identity_must_match(self) -> None:
with self.assertRaisesRegex(
controller.ControllerError, "run_trace_identity_mismatch"
):
controller.main(
[
"--policy",
str(POLICY_PATH),
"--run-id",
RUN_ID,
"--trace-id",
"mcp-artifact-00000000-0000-4000-8000-000000000000",
"check",
]
)
def test_noncanonical_run_id_is_rejected(self) -> None:
with self.assertRaisesRegex(controller.ControllerError, "run_id_not_canonical"):
controller.main(
[
"--policy",
str(POLICY_PATH),
"--run-id",
RUN_ID.upper(),
"--trace-id",
TRACE_ID,
"check",
]
)
def test_check_mode_emits_bound_no_write_receipt(self) -> None:
policy = controller.load_policy(POLICY_PATH)
verified = _verified_packages(policy)
with (
patch.object(controller, "verify_upstream_bundle", return_value=verified),
patch.object(controller, "build_cyclonedx_sbom", return_value={}),
patch.object(
controller,
"_write_bundle",
return_value=("d" * 64, "e" * 64),
),
io.StringIO() as output,
redirect_stdout(output),
):
result = controller.main(
[
"--policy",
str(POLICY_PATH),
"--run-id",
RUN_ID,
"--trace-id",
TRACE_ID,
"check",
]
)
receipt = json.loads(output.getvalue())
self.assertEqual(result, 0)
self.assertEqual(receipt["run_id"], RUN_ID)
self.assertEqual(receipt["trace_id"], TRACE_ID)
self.assertEqual(
receipt["status"], "completed_check_mode_pending_independent_verifier"
)
self.assertFalse(receipt["promotion_allowed"])
self.assertFalse(receipt["canary_allowed"])
self.assertFalse(
receipt["stage_receipts"]["sensor_source_receipt"]
["runtime_request_or_response_body_stored"]
)
self.assertTrue(
receipt["stage_receipts"]["sensor_source_receipt"]
["upstream_supply_chain_attestation_bundled"]
)
self.assertEqual(
receipt["stage_receipts"]["rollback_or_no_write_terminal"]["status"],
"no_write_pending_external_verifier",
)
self.assertEqual(
receipt["stage_receipts"]["independent_post_verifier"]["status"],
"pending_external_verifier",
)
def test_mirror_apply_requires_receipt_path(self) -> None:
with self.assertRaisesRegex(
controller.ControllerError, "apply_receipt_path_required"
):
controller.main(
[
"--policy",
str(POLICY_PATH),
"--run-id",
RUN_ID,
"--trace-id",
TRACE_ID,
"mirror",
"--apply",
]
)
def test_tarball_inspection_accepts_exact_manifest(self) -> None:
package = controller.load_policy(POLICY_PATH).packages[0]
unpacked, files, licenses = controller.inspect_tarball(
package,
_tarball(package),
maximum_unpacked_bytes=1024 * 1024,
)
self.assertGreater(unpacked, 0)
self.assertEqual(files, 2)
self.assertEqual(licenses, 1)
def test_tarball_inspection_rejects_symlink(self) -> None:
package = controller.load_policy(POLICY_PATH).packages[0]
with self.assertRaisesRegex(
controller.ControllerError, "package_archive_unsafe_entry"
):
controller.inspect_tarball(
package,
_tarball(package, unsafe_symlink=True),
maximum_unpacked_bytes=1024 * 1024,
)
def test_tarball_inspection_rejects_duplicate_manifest(self) -> None:
package = controller.load_policy(POLICY_PATH).packages[0]
with self.assertRaisesRegex(
controller.ControllerError,
"package_archive_duplicate_or_invalid_file",
):
controller.inspect_tarball(
package,
_tarball(package, duplicate_manifest=True),
maximum_unpacked_bytes=1024 * 1024,
)
def test_slsa_subject_must_match_exact_tarball_digest(self) -> None:
package = controller.load_policy(POLICY_PATH).packages[0]
statement = {
"_type": controller.IN_TOTO_STATEMENT_TYPE,
"predicateType": controller.SLSA_PREDICATE_TYPE,
"subject": [
{
"name": package.slsa_subject_name,
"digest": {"sha512": package.slsa_subject_sha512_hex},
}
],
}
payload = {
"attestations": [
{
"predicateType": controller.SLSA_PREDICATE_TYPE,
"bundle": {
"dsseEnvelope": {
"payload": base64.b64encode(
json.dumps(statement).encode()
).decode()
}
},
}
]
}
controller.validate_slsa_attestation(package, payload)
bad_statement = dict(statement)
bad_statement["subject"] = [
{
"name": package.slsa_subject_name,
"digest": {"sha512": "0" * 128},
}
]
payload["attestations"][0]["bundle"]["dsseEnvelope"]["payload"] = (
base64.b64encode(json.dumps(bad_statement).encode()).decode()
)
with self.assertRaisesRegex(controller.ControllerError, "slsa_subject_mismatch"):
controller.validate_slsa_attestation(package, payload)
def test_sbom_has_exact_npm_purls_and_dependency_edges(self) -> None:
policy = controller.load_policy(POLICY_PATH)
sbom = controller.build_cyclonedx_sbom(
policy,
_verified_packages(policy),
)
refs = {row["bom-ref"] for row in sbom["components"]}
self.assertIn("pkg:npm/%40playwright/mcp@0.0.78", refs)
self.assertNotIn("pkg:npm/%40playwright%2Fmcp@0.0.78", refs)
root = next(
row
for row in sbom["dependencies"]
if row["ref"] == "pkg:npm/%40playwright/mcp@0.0.78"
)
self.assertEqual(len(root["dependsOn"]), 2)
def test_osv_records_fail_closed(self) -> None:
policy = controller.load_policy(POLICY_PATH)
package = policy.packages[0]
class OsvClient:
def post_json(self, *_args, **_kwargs):
return b'{"vulns":[{"id":"OSV-TEST-1"}]}'
response_hash, vulnerabilities = controller._query_osv(
package,
OsvClient(),
policy.maximum_metadata_bytes,
)
self.assertEqual(len(response_hash), 64)
self.assertEqual(vulnerabilities, ("OSV-TEST-1",))
def test_mirror_receipt_never_promotes_runtime(self) -> None:
policy = controller.load_policy(POLICY_PATH)
verified = _verified_packages(policy)
receipt = controller.build_receipt(
policy=policy,
verified=verified,
sbom_sha256="a" * 64,
bundle_manifest_sha256="b" * 64,
run_id=RUN_ID,
trace_id=TRACE_ID,
mode="apply",
internal_mirror_ref=(
"registry.wooo.work/awoooi/mcp-artifacts/playwright-mcp@sha256:"
+ "c" * 64
),
runtime_mirror_ref=(
"192.168.0.110:5000/awoooi/mcp-artifacts/playwright-mcp@sha256:"
+ "c" * 64
),
)
self.assertEqual(
receipt["status"],
"completed_internal_mirror_pending_independent_verifier",
)
self.assertFalse(receipt["promotion_allowed"])
self.assertFalse(receipt["replay_allowed"])
self.assertFalse(receipt["shadow_allowed"])
self.assertFalse(receipt["canary_allowed"])
self.assertFalse(
receipt["stage_receipts"]["bounded_execution"]
["production_route_changed"]
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,471 @@
from __future__ import annotations
import base64
import hashlib
import importlib.util
import io
import json
import sys
import tarfile
import tempfile
import unittest
import subprocess
from copy import deepcopy
from pathlib import Path
from urllib.parse import quote
ROOT = Path(__file__).resolve().parents[3]
VERIFIER_PATH = ROOT / "scripts/security/verify_external_mcp_artifact_receipt.py"
RUN_ID = "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"
TRACE_ID = f"mcp-artifact-{RUN_ID}"
def _load_verifier():
spec = importlib.util.spec_from_file_location(
"verify_external_mcp_artifact_receipt", VERIFIER_PATH
)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
verifier = _load_verifier()
def _canonical(payload) -> bytes:
return json.dumps(
payload,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode()
def _fixture():
package_data = {
"@playwright/mcp": b"root-package",
"playwright": b"playwright-package",
"playwright-core": b"playwright-core-package",
}
versions = {
"@playwright/mcp": "0.0.78",
"playwright": "1.62.0-alpha-1783623505000",
"playwright-core": "1.62.0-alpha-1783623505000",
}
packages = []
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
private_key = root / "private.pem"
public_key = root / "public.der"
subprocess.run(
[
"openssl",
"ecparam",
"-name",
"prime256v1",
"-genkey",
"-noout",
"-out",
str(private_key),
],
check=True,
capture_output=True,
)
subprocess.run(
[
"openssl",
"pkey",
"-in",
str(private_key),
"-pubout",
"-outform",
"DER",
"-out",
str(public_key),
],
check=True,
capture_output=True,
)
public_key_base64 = base64.b64encode(public_key.read_bytes()).decode()
for index, (name, data) in enumerate(package_data.items()):
integrity = "sha512-" + base64.b64encode(
hashlib.sha512(data).digest()
).decode()
message = f"{name}@{versions[name]}:{integrity}".encode()
message_path = root / f"message-{index}.bin"
signature_path = root / f"signature-{index}.der"
message_path.write_bytes(message)
subprocess.run(
[
"openssl",
"dgst",
"-sha256",
"-sign",
str(private_key),
"-out",
str(signature_path),
str(message_path),
],
check=True,
capture_output=True,
)
packages.append(
{
"name": name,
"version": versions[name],
"integrity": integrity,
"signature": base64.b64encode(
signature_path.read_bytes()
).decode(),
"slsa_subject_name": (
f"pkg:npm/{quote(name, safe='/')}@{versions[name]}"
),
"slsa_subject_sha512_hex": hashlib.sha512(data).hexdigest(),
"shasum_sha1": hashlib.sha1(
data, usedforsecurity=False
).hexdigest(),
}
)
policy = {
"schema_version": verifier.POLICY_SCHEMA_VERSION,
"work_item_id": "MCP-ARTIFACT-PLAYWRIGHT-TEST",
"candidate_id": "external.playwright-verifier",
"risk_level": "high",
"registry_contract": {
"signature_key": {
"public_key_der_base64": public_key_base64,
}
},
"packages": packages,
"internal_mirror": {
"push_repository": (
"registry.wooo.work/awoooi/mcp-artifacts/playwright-mcp"
),
"runtime_repository": (
"192.168.0.110:5000/awoooi/mcp-artifacts/playwright-mcp"
),
},
}
policy_checksum = "sha256:" + hashlib.sha256(_canonical(policy)).hexdigest()
sbom = {
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [
{
"bom-ref": f"pkg:npm/{quote(row['name'], safe='/')}@{row['version']}"
}
for row in packages
],
}
sbom_bytes = _canonical(sbom) + b"\n"
files = {"artifact/sbom.cdx.json": sbom_bytes}
manifest_rows = []
receipt_rows = []
for row in packages:
safe_name = row["name"].replace("@", "").replace("/", "-")
filename = f"{safe_name}-{row['version']}.tgz"
statement = {
"_type": verifier.IN_TOTO_STATEMENT_TYPE,
"predicateType": verifier.SLSA_PREDICATE_TYPE,
"subject": [
{
"name": row["slsa_subject_name"],
"digest": {"sha512": row["slsa_subject_sha512_hex"]},
}
],
}
attestation = _canonical(
{
"attestations": [
{
"predicateType": verifier.SLSA_PREDICATE_TYPE,
"bundle": {
"dsseEnvelope": {
"payload": base64.b64encode(
_canonical(statement)
).decode()
}
},
}
]
}
)
files[f"artifact/tarballs/{filename}"] = package_data[row["name"]]
files[f"artifact/attestations/{filename}.attestations.json"] = attestation
manifest_rows.append(
{
"name": row["name"],
"version": row["version"],
"tarball_path": f"tarballs/{filename}",
"tarball_sha512_hex": row["slsa_subject_sha512_hex"],
"tarball_sha1": row["shasum_sha1"],
"attestation_path": (
f"attestations/{filename}.attestations.json"
),
"attestation_sha256": hashlib.sha256(attestation).hexdigest(),
"npm_registry_signature_verified": True,
"slsa_subject_verified": True,
"archive_safety_verified": True,
"vulnerability_ids": [],
}
)
receipt_rows.append(
{
"name": row["name"],
"version": row["version"],
"tarball_sha512_hex": row["slsa_subject_sha512_hex"],
"tarball_sha1": row["shasum_sha1"],
"npm_registry_signature_verified": True,
"slsa_subject_verified": True,
"archive_safety_verified": True,
"license_decision": "passed",
"vulnerability_decision": "passed_no_known_osv_records",
"vulnerability_ids": [],
}
)
manifest = {
"candidate_id": policy["candidate_id"],
"work_item_id": policy["work_item_id"],
"policy_checksum": policy_checksum,
"package_count": 3,
"packages": manifest_rows,
"sbom": {"sha256": hashlib.sha256(sbom_bytes).hexdigest()},
"operation_boundaries": {
"mcp_server_started": False,
"browser_started": False,
"runtime_request_or_response_body_stored": False,
},
}
manifest_bytes = _canonical(manifest) + b"\n"
files["artifact/bundle-manifest.json"] = manifest_bytes
digest = "sha256:" + "c" * 64
receipt = {
"schema_version": verifier.CONTROLLER_RECEIPT_SCHEMA_VERSION,
"run_id": RUN_ID,
"trace_id": TRACE_ID,
"work_item_id": policy["work_item_id"],
"candidate_id": policy["candidate_id"],
"risk_level": "high",
"mode": "apply",
"status": "completed_internal_mirror_pending_independent_verifier",
"policy_checksum": policy_checksum,
"packages": receipt_rows,
"internal_mirror_ref": (
policy["internal_mirror"]["push_repository"] + "@" + digest
),
"runtime_mirror_ref": (
policy["internal_mirror"]["runtime_repository"] + "@" + digest
),
"bundle_manifest_sha256": hashlib.sha256(manifest_bytes).hexdigest(),
"cyclonedx_sbom_sha256": hashlib.sha256(sbom_bytes).hexdigest(),
"promotion_allowed": False,
"replay_allowed": False,
"shadow_allowed": False,
"canary_allowed": False,
"stage_receipts": {
"sensor_source_receipt": {
"redirects_followed": 0,
"runtime_request_or_response_body_stored": False,
"upstream_supply_chain_attestation_bundled": True,
},
"check_mode": {
"status": "passed",
"mcp_process_started": False,
"browser_started": False,
"production_write_performed": False,
},
"bounded_execution": {
"status": "internal_oci_bundle_pushed",
"internal_registry_write_performed": True,
"external_runtime_mutation_performed": False,
"production_route_changed": False,
},
"independent_post_verifier": {
"status": "pending_external_verifier",
"internal_digest_ref_verified": False,
},
},
}
return policy, receipt, files
def _write_image_archive(path: Path, files: dict[str, bytes], *, symlink=False):
layer_buffer = io.BytesIO()
with tarfile.open(fileobj=layer_buffer, mode="w") as layer:
for name, data in files.items():
member = tarfile.TarInfo(name)
member.size = len(data)
layer.addfile(member, io.BytesIO(data))
if symlink:
member = tarfile.TarInfo("artifact/unsafe-link")
member.type = tarfile.SYMTYPE
member.linkname = "../../outside"
layer.addfile(member)
layer_bytes = layer_buffer.getvalue()
manifest_bytes = json.dumps([{"Layers": ["layer.tar"]}]).encode()
with tarfile.open(path, mode="w") as outer:
for name, data in (
("manifest.json", manifest_bytes),
("layer.tar", layer_bytes),
):
member = tarfile.TarInfo(name)
member.size = len(data)
outer.addfile(member, io.BytesIO(data))
class ExternalMcpArtifactIndependentVerifierTest(unittest.TestCase):
def test_controller_receipt_identity_and_boundaries_are_validated(self) -> None:
policy, receipt, _ = _fixture()
identity = verifier.validate_controller_receipt(policy, receipt)
self.assertEqual(identity["run_id"], RUN_ID)
self.assertEqual(identity["trace_id"], TRACE_ID)
self.assertEqual(identity["digest"], "sha256:" + "c" * 64)
def test_controller_receipt_rejects_promotion_claim(self) -> None:
policy, receipt, _ = _fixture()
receipt["canary_allowed"] = True
with self.assertRaisesRegex(
verifier.VerifierError,
"controller_receipt_promotion_boundary_invalid",
):
verifier.validate_controller_receipt(policy, receipt)
def test_bundle_files_are_rehashed_independently(self) -> None:
policy, receipt, files = _fixture()
evidence = verifier.verify_bundle_files(
files,
policy=policy,
controller_receipt=receipt,
)
self.assertEqual(evidence["verified_package_count"], 3)
self.assertEqual(evidence["artifact_file_count"], 8)
def test_bundle_tarball_tamper_is_rejected(self) -> None:
policy, receipt, files = _fixture()
tampered = deepcopy(files)
root_tarball = next(
key
for key in tampered
if key.startswith("artifact/tarballs/playwright-mcp-")
)
tampered[root_tarball] += b"tamper"
with self.assertRaisesRegex(
verifier.VerifierError,
"bundle_package_evidence_mismatch",
):
verifier.verify_bundle_files(
tampered,
policy=policy,
controller_receipt=receipt,
)
def test_bundle_extra_file_is_rejected(self) -> None:
policy, receipt, files = _fixture()
files["artifact/unexpected.bin"] = b"unexpected"
with self.assertRaisesRegex(
verifier.VerifierError,
"bundle_file_set_invalid",
):
verifier.verify_bundle_files(
files,
policy=policy,
controller_receipt=receipt,
)
def test_independent_npm_signature_rejects_tamper(self) -> None:
policy, _, files = _fixture()
package = deepcopy(policy["packages"][0])
package["signature"] = base64.b64encode(b"invalid-signature").decode()
tarball_path = next(
key
for key in files
if key.startswith("artifact/tarballs/playwright-mcp-")
)
with self.assertRaisesRegex(
verifier.VerifierError,
"bundled_npm_signature_verification_failed",
):
verifier.verify_bundled_npm_signature(
policy,
package,
files[tarball_path],
)
def test_independent_slsa_subject_rejects_tamper(self) -> None:
policy, _, files = _fixture()
package = deepcopy(policy["packages"][0])
package["slsa_subject_sha512_hex"] = "0" * 128
attestation_path = next(
key
for key in files
if key.startswith("artifact/attestations/playwright-mcp-")
)
with self.assertRaisesRegex(
verifier.VerifierError,
"bundled_slsa_subject_mismatch",
):
verifier.verify_bundled_slsa_subject(
package,
files[attestation_path],
)
def test_image_archive_is_inspected_as_data(self) -> None:
_, _, files = _fixture()
with tempfile.TemporaryDirectory() as temp:
archive = Path(temp) / "image.tar"
_write_image_archive(archive, files)
extracted = verifier.extract_image_files(archive)
self.assertEqual(extracted, files)
def test_image_archive_rejects_symlink(self) -> None:
_, _, files = _fixture()
with tempfile.TemporaryDirectory() as temp:
archive = Path(temp) / "image.tar"
_write_image_archive(archive, files, symlink=True)
with self.assertRaisesRegex(
verifier.VerifierError,
"image_layer_unsafe_entry",
):
verifier.extract_image_files(archive)
def test_verifier_receipt_keeps_runtime_promotion_disabled(self) -> None:
policy, receipt, files = _fixture()
identity = verifier.validate_controller_receipt(policy, receipt)
evidence = verifier.verify_bundle_files(
files,
policy=policy,
controller_receipt=receipt,
)
result = verifier.build_verifier_receipt(
identity=identity,
controller_receipt_bytes=_canonical(receipt) + b"\n",
bundle_evidence=evidence,
)
self.assertEqual(result["status"], "completed_internal_mirror_verified")
self.assertFalse(result["promotion_allowed"])
self.assertFalse(result["replay_allowed"])
self.assertFalse(result["shadow_allowed"])
self.assertFalse(result["canary_allowed"])
self.assertFalse(
result["independent_post_verifier"]["container_or_mcp_started"]
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,688 @@
#!/usr/bin/env python3
"""Independently verify an internal external-MCP artifact mirror receipt.
This verifier does not import the artifact controller. It validates the policy
and controller receipt through a separate code path, reads the Harbor image back
by immutable digest, and inspects its layers as data. It never starts a container,
MCP server, browser, replay, or production canary.
"""
from __future__ import annotations
import argparse
import base64
import binascii
import hashlib
import io
import json
import re
import shutil
import subprocess
import sys
import tarfile
import tempfile
import uuid
from datetime import datetime, timezone
from pathlib import Path, PurePosixPath
from typing import Any
from urllib.parse import quote
POLICY_SCHEMA_VERSION = "awoooi_external_mcp_artifact_policy_v1"
CONTROLLER_RECEIPT_SCHEMA_VERSION = "awoooi_external_mcp_artifact_receipt_v1"
VERIFIER_RECEIPT_SCHEMA_VERSION = "awoooi_external_mcp_artifact_verifier_receipt_v1"
SHA256_HEX_PATTERN = re.compile(r"^[0-9a-f]{64}$")
SHA1_HEX_PATTERN = re.compile(r"^[0-9a-f]{40}$")
SHA512_HEX_PATTERN = re.compile(r"^[0-9a-f]{128}$")
OCI_DIGEST_PATTERN = re.compile(r"^sha256:[0-9a-f]{64}$")
MAX_IMAGE_ARCHIVE_BYTES = 96 * 1024 * 1024
MAX_LAYER_UNPACKED_BYTES = 64 * 1024 * 1024
MAX_LAYER_FILES = 2_048
SLSA_PREDICATE_TYPE = "https://slsa.dev/provenance/v1"
IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1"
class VerifierError(RuntimeError):
"""Fail-closed public-safe verifier error class."""
def _canonical_json_bytes(payload: Any) -> bytes:
return json.dumps(
payload,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def _sha256_hex(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def _read_json(path: Path, error_class: str) -> dict[str, Any]:
try:
data = path.read_bytes()
except OSError as exc:
raise VerifierError(error_class) from exc
if len(data) > 4 * 1024 * 1024:
raise VerifierError(error_class)
try:
payload = json.loads(data)
except json.JSONDecodeError as exc:
raise VerifierError(error_class) from exc
if not isinstance(payload, dict):
raise VerifierError(error_class)
return payload
def _require_dict(value: Any, error_class: str) -> dict[str, Any]:
if not isinstance(value, dict):
raise VerifierError(error_class)
return value
def _expected_packages(policy: dict[str, Any]) -> dict[str, dict[str, Any]]:
rows = policy.get("packages")
if not isinstance(rows, list) or len(rows) != 3:
raise VerifierError("policy_package_set_invalid")
result: dict[str, dict[str, Any]] = {}
for row in rows:
if not isinstance(row, dict):
raise VerifierError("policy_package_set_invalid")
name = row.get("name")
version = row.get("version")
sha512_hex = row.get("slsa_subject_sha512_hex")
sha1_hex = row.get("shasum_sha1")
if (
not isinstance(name, str)
or not isinstance(version, str)
or not isinstance(sha512_hex, str)
or not SHA512_HEX_PATTERN.fullmatch(sha512_hex)
or not isinstance(sha1_hex, str)
or not SHA1_HEX_PATTERN.fullmatch(sha1_hex)
or name in result
):
raise VerifierError("policy_package_set_invalid")
result[name] = row
if set(result) != {"@playwright/mcp", "playwright", "playwright-core"}:
raise VerifierError("policy_package_set_invalid")
return result
def validate_controller_receipt(
policy: dict[str, Any],
receipt: dict[str, Any],
) -> dict[str, Any]:
"""Validate the controller receipt without using controller code."""
if policy.get("schema_version") != POLICY_SCHEMA_VERSION:
raise VerifierError("policy_schema_invalid")
if receipt.get("schema_version") != CONTROLLER_RECEIPT_SCHEMA_VERSION:
raise VerifierError("controller_receipt_schema_invalid")
policy_checksum = "sha256:" + _sha256_hex(_canonical_json_bytes(policy))
if receipt.get("policy_checksum") != policy_checksum:
raise VerifierError("controller_receipt_policy_checksum_mismatch")
if (
receipt.get("work_item_id") != policy.get("work_item_id")
or receipt.get("candidate_id") != policy.get("candidate_id")
or receipt.get("risk_level") != "high"
or receipt.get("mode") != "apply"
or receipt.get("status")
!= "completed_internal_mirror_pending_independent_verifier"
):
raise VerifierError("controller_receipt_identity_or_state_invalid")
run_id = receipt.get("run_id")
trace_id = receipt.get("trace_id")
try:
normalized_run_id = str(uuid.UUID(run_id))
except (ValueError, AttributeError) as exc:
raise VerifierError("controller_receipt_run_id_invalid") from exc
if run_id != normalized_run_id or trace_id != f"mcp-artifact-{run_id}":
raise VerifierError("controller_receipt_run_trace_mismatch")
if any(
receipt.get(field) is not False
for field in (
"promotion_allowed",
"replay_allowed",
"shadow_allowed",
"canary_allowed",
)
):
raise VerifierError("controller_receipt_promotion_boundary_invalid")
expected = _expected_packages(policy)
rows = receipt.get("packages")
if not isinstance(rows, list) or len(rows) != len(expected):
raise VerifierError("controller_receipt_package_set_invalid")
seen: set[str] = set()
for row in rows:
if not isinstance(row, dict) or row.get("name") not in expected:
raise VerifierError("controller_receipt_package_set_invalid")
package = expected[row["name"]]
if row["name"] in seen:
raise VerifierError("controller_receipt_package_set_invalid")
seen.add(row["name"])
if (
row.get("version") != package.get("version")
or row.get("tarball_sha512_hex")
!= package.get("slsa_subject_sha512_hex")
or row.get("tarball_sha1") != package.get("shasum_sha1")
or row.get("npm_registry_signature_verified") is not True
or row.get("slsa_subject_verified") is not True
or row.get("archive_safety_verified") is not True
or row.get("license_decision") != "passed"
or row.get("vulnerability_decision")
!= "passed_no_known_osv_records"
or row.get("vulnerability_ids") != []
):
raise VerifierError("controller_receipt_package_evidence_invalid")
if seen != set(expected):
raise VerifierError("controller_receipt_package_set_invalid")
stages = _require_dict(receipt.get("stage_receipts"), "stage_receipts_invalid")
sensor = _require_dict(
stages.get("sensor_source_receipt"), "sensor_source_receipt_invalid"
)
check_mode = _require_dict(stages.get("check_mode"), "check_mode_invalid")
execution = _require_dict(
stages.get("bounded_execution"), "bounded_execution_invalid"
)
pending_verifier = _require_dict(
stages.get("independent_post_verifier"),
"independent_post_verifier_state_invalid",
)
if (
sensor.get("redirects_followed") != 0
or sensor.get("runtime_request_or_response_body_stored") is not False
or sensor.get("upstream_supply_chain_attestation_bundled") is not True
or check_mode.get("status") != "passed"
or check_mode.get("mcp_process_started") is not False
or check_mode.get("browser_started") is not False
or check_mode.get("production_write_performed") is not False
or execution.get("status") != "internal_oci_bundle_pushed"
or execution.get("internal_registry_write_performed") is not True
or execution.get("external_runtime_mutation_performed") is not False
or execution.get("production_route_changed") is not False
or pending_verifier.get("status") != "pending_external_verifier"
or pending_verifier.get("internal_digest_ref_verified") is not False
):
raise VerifierError("controller_receipt_stage_boundary_invalid")
mirror = _require_dict(policy.get("internal_mirror"), "internal_mirror_invalid")
internal_ref = receipt.get("internal_mirror_ref")
runtime_ref = receipt.get("runtime_mirror_ref")
push_prefix = f"{mirror.get('push_repository')}@"
runtime_prefix = f"{mirror.get('runtime_repository')}@"
if (
not isinstance(internal_ref, str)
or not internal_ref.startswith(push_prefix)
or not isinstance(runtime_ref, str)
or not runtime_ref.startswith(runtime_prefix)
):
raise VerifierError("controller_receipt_internal_ref_invalid")
internal_digest = internal_ref.removeprefix(push_prefix)
runtime_digest = runtime_ref.removeprefix(runtime_prefix)
if (
internal_digest != runtime_digest
or not OCI_DIGEST_PATTERN.fullmatch(internal_digest)
):
raise VerifierError("controller_receipt_internal_digest_invalid")
for field in ("bundle_manifest_sha256", "cyclonedx_sbom_sha256"):
if not isinstance(receipt.get(field), str) or not SHA256_HEX_PATTERN.fullmatch(
receipt[field]
):
raise VerifierError("controller_receipt_bundle_hash_invalid")
return {
"run_id": run_id,
"trace_id": trace_id,
"work_item_id": receipt["work_item_id"],
"candidate_id": receipt["candidate_id"],
"policy_checksum": policy_checksum,
"internal_ref": internal_ref,
"runtime_ref": runtime_ref,
"digest": internal_digest,
"expected_packages": expected,
}
def _run_command(command: list[str], *, cwd: Path, timeout: int) -> None:
try:
completed = subprocess.run(
command,
cwd=cwd,
check=False,
capture_output=True,
text=True,
timeout=timeout,
)
except (OSError, subprocess.TimeoutExpired) as exc:
raise VerifierError("registry_readback_command_failed") from exc
if completed.returncode != 0:
raise VerifierError("registry_readback_command_failed")
def verify_bundled_npm_signature(
policy: dict[str, Any],
package: dict[str, Any],
tarball: bytes,
) -> None:
"""Verify the npm ECDSA signature with OpenSSL through a separate path."""
integrity = package.get("integrity")
signature_base64 = package.get("signature")
registry = _require_dict(policy.get("registry_contract"), "registry_contract_invalid")
signature_key = _require_dict(
registry.get("signature_key"), "registry_signature_key_invalid"
)
public_key_base64 = signature_key.get("public_key_der_base64")
if (
not isinstance(integrity, str)
or not integrity.startswith("sha512-")
or not isinstance(signature_base64, str)
or not isinstance(public_key_base64, str)
):
raise VerifierError("bundled_npm_signature_policy_invalid")
try:
integrity_digest = base64.b64decode(
integrity.removeprefix("sha512-"), validate=True
)
signature = base64.b64decode(signature_base64, validate=True)
public_key = base64.b64decode(public_key_base64, validate=True)
except (ValueError, binascii.Error) as exc:
raise VerifierError("bundled_npm_signature_encoding_invalid") from exc
if integrity_digest != hashlib.sha512(tarball).digest():
raise VerifierError("bundled_npm_integrity_mismatch")
openssl = shutil.which("openssl")
if not openssl:
raise VerifierError("openssl_signature_verifier_unavailable")
message = f"{package.get('name')}@{package.get('version')}:{integrity}".encode()
with tempfile.TemporaryDirectory(prefix="awoooi-mcp-npm-verifier-") as temp:
root = Path(temp)
der_path = root / "public.der"
pem_path = root / "public.pem"
signature_path = root / "signature.der"
message_path = root / "message.bin"
der_path.write_bytes(public_key)
signature_path.write_bytes(signature)
message_path.write_bytes(message)
convert = subprocess.run(
[
openssl,
"pkey",
"-pubin",
"-inform",
"DER",
"-in",
str(der_path),
"-out",
str(pem_path),
],
check=False,
capture_output=True,
text=True,
timeout=10,
)
if convert.returncode != 0:
raise VerifierError("bundled_npm_public_key_invalid")
verified = subprocess.run(
[
openssl,
"dgst",
"-sha256",
"-verify",
str(pem_path),
"-signature",
str(signature_path),
str(message_path),
],
check=False,
capture_output=True,
text=True,
timeout=10,
)
if verified.returncode != 0:
raise VerifierError("bundled_npm_signature_verification_failed")
def verify_bundled_slsa_subject(
package: dict[str, Any],
attestation: bytes,
) -> None:
"""Decode the bundled DSSE statement and independently bind its subject."""
try:
payload = json.loads(attestation)
except json.JSONDecodeError as exc:
raise VerifierError("bundled_slsa_attestation_invalid") from exc
rows = payload.get("attestations") if isinstance(payload, dict) else None
if not isinstance(rows, list):
raise VerifierError("bundled_slsa_attestation_invalid")
matches = [
row
for row in rows
if isinstance(row, dict) and row.get("predicateType") == SLSA_PREDICATE_TYPE
]
if len(matches) != 1:
raise VerifierError("bundled_slsa_attestation_invalid")
bundle = matches[0].get("bundle")
envelope = bundle.get("dsseEnvelope") if isinstance(bundle, dict) else None
encoded = envelope.get("payload") if isinstance(envelope, dict) else None
if not isinstance(encoded, str):
raise VerifierError("bundled_slsa_dsse_payload_missing")
try:
statement = json.loads(base64.b64decode(encoded, validate=True))
except (ValueError, binascii.Error, json.JSONDecodeError) as exc:
raise VerifierError("bundled_slsa_dsse_payload_invalid") from exc
expected_subject = {
"name": package.get("slsa_subject_name"),
"digest": {"sha512": package.get("slsa_subject_sha512_hex")},
}
if (
not isinstance(statement, dict)
or statement.get("_type") != IN_TOTO_STATEMENT_TYPE
or statement.get("predicateType") != SLSA_PREDICATE_TYPE
or statement.get("subject") != [expected_subject]
):
raise VerifierError("bundled_slsa_subject_mismatch")
def read_image_files(internal_ref: str) -> dict[str, bytes]:
"""Pull and inspect an OCI image as tar data without creating a container."""
docker = shutil.which("docker")
if not docker:
raise VerifierError("docker_cli_unavailable")
with tempfile.TemporaryDirectory(prefix="awoooi-mcp-artifact-verifier-") as temp:
root = Path(temp)
archive_path = root / "image.tar"
_run_command(
[docker, "buildx", "imagetools", "inspect", internal_ref],
cwd=root,
timeout=60,
)
_run_command([docker, "pull", internal_ref], cwd=root, timeout=180)
_run_command(
[docker, "image", "save", "--output", str(archive_path), internal_ref],
cwd=root,
timeout=120,
)
return extract_image_files(archive_path)
def extract_image_files(archive_path: Path) -> dict[str, bytes]:
"""Extract bounded artifact files from a Docker image archive."""
try:
if archive_path.stat().st_size > MAX_IMAGE_ARCHIVE_BYTES:
raise VerifierError("image_archive_size_exceeded")
with tarfile.open(archive_path, mode="r:") as outer:
manifest_member = outer.getmember("manifest.json")
manifest_file = outer.extractfile(manifest_member)
if manifest_file is None:
raise VerifierError("image_manifest_missing")
manifests = json.loads(manifest_file.read(1_048_577))
if not isinstance(manifests, list) or len(manifests) != 1:
raise VerifierError("image_manifest_invalid")
layers = manifests[0].get("Layers")
if not isinstance(layers, list) or not layers:
raise VerifierError("image_layers_missing")
files: dict[str, bytes] = {}
total_bytes = 0
file_count = 0
for layer_name in layers:
if not isinstance(layer_name, str):
raise VerifierError("image_layer_name_invalid")
layer_path = PurePosixPath(layer_name)
if layer_path.is_absolute() or ".." in layer_path.parts:
raise VerifierError("image_layer_name_invalid")
layer_member = outer.getmember(layer_name)
layer_file = outer.extractfile(layer_member)
if layer_file is None:
raise VerifierError("image_layer_missing")
layer_bytes = layer_file.read(MAX_IMAGE_ARCHIVE_BYTES + 1)
if len(layer_bytes) > MAX_IMAGE_ARCHIVE_BYTES:
raise VerifierError("image_layer_size_exceeded")
with tarfile.open(fileobj=io.BytesIO(layer_bytes)) as layer:
for member in layer.getmembers():
path = PurePosixPath(member.name)
if (
path.is_absolute()
or ".." in path.parts
or member.issym()
or member.islnk()
or member.isdev()
):
raise VerifierError("image_layer_unsafe_entry")
if member.isdir():
continue
if not member.isfile():
raise VerifierError("image_layer_unsupported_entry")
if any(part.startswith(".wh.") for part in path.parts):
raise VerifierError("image_layer_whiteout_not_allowed")
normalized = str(path).lstrip("./")
if not normalized.startswith("artifact/"):
raise VerifierError("image_layer_file_outside_artifact")
extracted = layer.extractfile(member)
if extracted is None:
raise VerifierError("image_layer_file_unreadable")
data = extracted.read(member.size + 1)
if len(data) != member.size:
raise VerifierError("image_layer_file_size_mismatch")
total_bytes += len(data)
file_count += 1
if (
total_bytes > MAX_LAYER_UNPACKED_BYTES
or file_count > MAX_LAYER_FILES
):
raise VerifierError("image_layer_bounds_exceeded")
files[normalized] = data
except (OSError, KeyError, tarfile.TarError, json.JSONDecodeError) as exc:
raise VerifierError("image_archive_invalid") from exc
return files
def verify_bundle_files(
files: dict[str, bytes],
*,
policy: dict[str, Any],
controller_receipt: dict[str, Any],
) -> dict[str, Any]:
"""Recompute immutable bundle evidence from image layer files."""
manifest_bytes = files.get("artifact/bundle-manifest.json")
sbom_bytes = files.get("artifact/sbom.cdx.json")
if manifest_bytes is None or sbom_bytes is None:
raise VerifierError("bundle_core_files_missing")
if _sha256_hex(manifest_bytes) != controller_receipt.get(
"bundle_manifest_sha256"
) or _sha256_hex(sbom_bytes) != controller_receipt.get("cyclonedx_sbom_sha256"):
raise VerifierError("bundle_core_hash_mismatch")
try:
manifest = json.loads(manifest_bytes)
sbom = json.loads(sbom_bytes)
except json.JSONDecodeError as exc:
raise VerifierError("bundle_core_json_invalid") from exc
if not isinstance(manifest, dict) or not isinstance(sbom, dict):
raise VerifierError("bundle_core_json_invalid")
policy_checksum = "sha256:" + _sha256_hex(_canonical_json_bytes(policy))
if (
manifest.get("candidate_id") != policy.get("candidate_id")
or manifest.get("work_item_id") != policy.get("work_item_id")
or manifest.get("policy_checksum") != policy_checksum
or manifest.get("package_count") != 3
or manifest.get("operation_boundaries", {}).get("mcp_server_started")
is not False
or manifest.get("operation_boundaries", {}).get("browser_started")
is not False
or manifest.get("operation_boundaries", {}).get(
"runtime_request_or_response_body_stored"
)
is not False
):
raise VerifierError("bundle_manifest_boundary_invalid")
expected = _expected_packages(policy)
package_rows = manifest.get("packages")
if not isinstance(package_rows, list) or len(package_rows) != len(expected):
raise VerifierError("bundle_package_set_invalid")
verified_package_names: list[str] = []
expected_file_paths = {
"artifact/bundle-manifest.json",
"artifact/sbom.cdx.json",
}
for row in package_rows:
if not isinstance(row, dict) or row.get("name") not in expected:
raise VerifierError("bundle_package_set_invalid")
package = expected[row["name"]]
tarball_path = row.get("tarball_path")
attestation_path = row.get("attestation_path")
if not isinstance(tarball_path, str) or not isinstance(attestation_path, str):
raise VerifierError("bundle_package_path_invalid")
tarball = files.get(f"artifact/{tarball_path}")
attestation = files.get(f"artifact/{attestation_path}")
if tarball is None or attestation is None:
raise VerifierError("bundle_package_file_missing")
expected_file_paths.update(
{f"artifact/{tarball_path}", f"artifact/{attestation_path}"}
)
if (
hashlib.sha512(tarball).hexdigest()
!= package.get("slsa_subject_sha512_hex")
or hashlib.sha1(tarball, usedforsecurity=False).hexdigest()
!= package.get("shasum_sha1")
or _sha256_hex(attestation) != row.get("attestation_sha256")
or row.get("npm_registry_signature_verified") is not True
or row.get("slsa_subject_verified") is not True
or row.get("archive_safety_verified") is not True
or row.get("vulnerability_ids") != []
):
raise VerifierError("bundle_package_evidence_mismatch")
verify_bundled_npm_signature(policy, package, tarball)
verify_bundled_slsa_subject(package, attestation)
verified_package_names.append(row["name"])
if len(set(verified_package_names)) != len(expected):
raise VerifierError("bundle_package_set_invalid")
if set(files) != expected_file_paths:
raise VerifierError("bundle_file_set_invalid")
expected_refs = {
f"pkg:npm/{quote(name, safe='/')}@{row['version']}"
for name, row in expected.items()
}
components = sbom.get("components")
if (
sbom.get("bomFormat") != "CycloneDX"
or sbom.get("specVersion") != "1.5"
or not isinstance(components, list)
or {row.get("bom-ref") for row in components if isinstance(row, dict)}
!= expected_refs
):
raise VerifierError("bundle_sbom_invalid")
manifest_sbom = _require_dict(manifest.get("sbom"), "bundle_sbom_invalid")
if manifest_sbom.get("sha256") != _sha256_hex(sbom_bytes):
raise VerifierError("bundle_sbom_hash_mismatch")
return {
"artifact_file_count": len(files),
"verified_package_count": len(verified_package_names),
"bundle_manifest_sha256": _sha256_hex(manifest_bytes),
"cyclonedx_sbom_sha256": _sha256_hex(sbom_bytes),
}
def build_verifier_receipt(
*,
identity: dict[str, Any],
controller_receipt_bytes: bytes,
bundle_evidence: dict[str, Any],
) -> dict[str, Any]:
return {
"schema_version": VERIFIER_RECEIPT_SCHEMA_VERSION,
"run_id": identity["run_id"],
"trace_id": identity["trace_id"],
"work_item_id": identity["work_item_id"],
"candidate_id": identity["candidate_id"],
"status": "completed_internal_mirror_verified",
"observed_at": datetime.now(timezone.utc).isoformat(),
"policy_checksum": identity["policy_checksum"],
"controller_receipt_sha256": _sha256_hex(controller_receipt_bytes),
"internal_mirror_ref": identity["internal_ref"],
"runtime_mirror_ref": identity["runtime_ref"],
"internal_digest": identity["digest"],
"promotion_allowed": False,
"replay_allowed": False,
"shadow_allowed": False,
"canary_allowed": False,
"independent_post_verifier": {
"status": "passed",
"implementation": "standalone_no_controller_import",
"registry_digest_readback_verified": True,
"npm_registry_signatures_reverified": True,
"slsa_subjects_reverified": True,
"container_or_mcp_started": False,
**bundle_evidence,
},
"rollback_terminal": {
"status": "rollback_ready_not_promoted",
"action": "disable_candidate_and_retain_immutable_bundle",
"production_route_changed": False,
"external_artifact_delete_allowed": False,
},
"learning_writeback": {
"status": "verifier_receipt_ready_for_committed_writeback",
"external_rag_write_performed": False,
"raw_runtime_request_or_response_body_stored": False,
},
}
def _write_receipt(path: Path, receipt: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(_canonical_json_bytes(receipt) + b"\n")
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--policy", required=True, type=Path)
parser.add_argument("--controller-receipt", required=True, type=Path)
parser.add_argument("--verifier-receipt", required=True, type=Path)
return parser
def main(argv: list[str] | None = None) -> int:
args = _build_parser().parse_args(argv)
policy = _read_json(args.policy, "policy_unreadable")
controller_receipt_bytes = args.controller_receipt.read_bytes()
if len(controller_receipt_bytes) > 4 * 1024 * 1024:
raise VerifierError("controller_receipt_too_large")
controller_receipt = _read_json(
args.controller_receipt,
"controller_receipt_unreadable",
)
identity = validate_controller_receipt(policy, controller_receipt)
files = read_image_files(identity["internal_ref"])
bundle_evidence = verify_bundle_files(
files,
policy=policy,
controller_receipt=controller_receipt,
)
receipt = build_verifier_receipt(
identity=identity,
controller_receipt_bytes=controller_receipt_bytes,
bundle_evidence=bundle_evidence,
)
_write_receipt(args.verifier_receipt, receipt)
print(json.dumps(receipt, ensure_ascii=False, sort_keys=True))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (OSError, VerifierError) as exc:
error_class = str(exc) if isinstance(exc, VerifierError) else "verifier_io_error"
print(
json.dumps(
{
"schema_version": VERIFIER_RECEIPT_SCHEMA_VERSION,
"status": "blocked_with_safe_next_action",
"error_class": error_class,
},
sort_keys=True,
),
file=sys.stderr,
)
raise SystemExit(1) from None