Files
aios/tests/test_c5_unattended_release_integration.py
T

80 lines
3.2 KiB
Python

"""Use the real signed builder, durable release service and approval verifier."""
import json
import pytest
from governed_unattended_release import GovernedUnattendedReleaseRunner
from silent_release_hardening import CapabilityAuthorizationError
from test_silent_release_execution_readiness import (
COMMIT, trusted_service, signed_shadow_approval,
)
def bind_simulated_source(service):
service.source_admission_resolver = lambda source: {
"admission_id": "simulated-source-admission", "source_commit": source,
"tree_oid": "3" * 40, "version": "V2R2C5",
}
def test_real_release_service_and_signed_owner_broker_are_composable(tmp_path):
service, signer = trusted_service(tmp_path)
bind_simulated_source(service)
runner = GovernedUnattendedReleaseRunner(
admission=object(), environment_mode="AUTHORITY", release_service=service,
owner_approval_broker=lambda envelope: signed_shadow_approval(
service, signer, envelope["candidate_id"]
).to_dict(),
)
result = runner.postpublication_rc(
event_id="isolated-integration", source_commit=COMMIT,
source_readback={"authenticated": True, "source_commit": COMMIT, "tree_oid": "3" * 40},
)
assert result["phase"] == "APPROVED", result
assert service.get_release_saga(result["candidate_id"])["state"] == "APPROVED"
assert service.execution_enabled is False
assert runner.deploy(result["candidate_id"])["phase"] == "BLOCKED"
def test_real_verifier_rejects_broker_source_substitution(tmp_path):
service, signer = trusted_service(tmp_path)
bind_simulated_source(service)
broker_calls = []
def broker(envelope):
broker_calls.append(envelope["candidate_id"])
approval = signed_shadow_approval(service, signer, envelope["candidate_id"]).to_dict()
approval["source_commit"] = "f" * 40
return approval
runner = GovernedUnattendedReleaseRunner(
admission=object(), environment_mode="AUTHORITY", release_service=service,
owner_approval_broker=broker,
)
result = runner.postpublication_rc(
event_id="isolated-substitution", source_commit=COMMIT,
source_readback={"authenticated": True, "source_commit": COMMIT, "tree_oid": "3" * 40},
)
assert result["phase"] == "BLOCKED"
assert len(broker_calls) == 1
assert not list(service.approvals_root.glob("*.json"))
@pytest.mark.parametrize("damage", ["missing_verifier", "tampered_attestation"])
def test_durable_acceptance_reverification_fails_closed(tmp_path, damage):
service, _ = trusted_service(tmp_path)
bind_simulated_source(service)
created = service.create_release_intent({"event_id": "acceptance-recheck", "source_commit": COMMIT})
candidate_id = created["candidate_id"]
assert service.verify_release_acceptance(candidate_id)["status"] == "VERIFIED"
if damage == "missing_verifier":
service.builder_attestation_verifier = None
else:
path = service.acceptance_root / (candidate_id + ".json")
record = json.loads(path.read_text())
record["attestation"]["artifact_sha256"] = "f" * 64
path.write_text(json.dumps(record))
with pytest.raises(CapabilityAuthorizationError):
service.verify_release_acceptance(candidate_id)