Files
aios/tests/test_c5_governed_principal_scope.py
T
AIOS Governed Continuation 540e28a724
autonomy-smoke-tests Exact governed tree; evidence 7dbba7ff168e72af
autonomy-unit-tests Exact governed tree; evidence 7dbba7ff168e72af
campaign-approval-gate Exact governed tree; evidence 7dbba7ff168e72af
git-diff-check Exact governed tree; evidence 7dbba7ff168e72af
py-compile Exact governed tree; evidence 7dbba7ff168e72af
reconciliation-contract Exact governed tree; evidence 7dbba7ff168e72af
release-state-machine Exact governed tree; evidence 7dbba7ff168e72af
secret-scan Exact governed tree; evidence 7029b3eafb209066
signer-validator-contract Exact governed tree; evidence 7dbba7ff168e72af
Publish exact C5 issuer V3 bootstrap source
2026-10-01 23:09:33 +08:00

575 lines
23 KiB
Python

from __future__ import annotations
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import sqlite3
import sys
from types import SimpleNamespace
import pytest
ROOT = Path(__file__).resolve().parents[1]
APP = ROOT / "active-runtime" / "app"
if str(APP) not in sys.path:
sys.path.insert(0, str(APP))
import c5_governed_principal_scope as scope # noqa: E402
NOW = 2_000_000_000
OWNER_KEY = "SHA256:" + "A" * 43
TRUST = {
"schema": "aios.task-principal-assertion.v1",
"issuer_id": "issuer",
"key_id": "key",
"public_key": "public",
"purpose": "TASK_PRINCIPAL",
"bridge_id": "bridge",
"scope_manifest_sha256": "f" * 64,
}
class StubSignatureVerifier:
key_id = OWNER_KEY
identity = "aios-task-grant-signer"
def __init__(self):
self.calls = 0
def verify(self, payload, signature):
self.calls += 1
return signature == b"valid-signature"
class StubAdapter:
def resolve_row(self, *, task, execution, trust, now):
if task["status"] not in {"CLAIMED", "RUNNING"}:
raise RuntimeError("TASK_NOT_CLAIMED")
return {
"scope_id": "source-scope",
"task_id": scope.TASK_ID,
"external_job_id": execution["external_job_id"],
"repository": scope.REPOSITORY,
"target_branch": scope.TARGET_BRANCH,
"generation": scope.GENERATION,
"execution_digest": scope.EXECUTION_DIGEST,
"capabilities_json": scope.canonical_json(list(scope.SOURCE_CAPABILITIES)).decode(),
"audiences_json": scope.canonical_json([
"aios.gitops.bridge.v2/bridge/acquireWriterLease",
"aios.gitops.bridge.v2/bridge/commitFilesGoverned",
]).decode(),
"trust_manifest_sha256": scope.sha256_json(dict(trust)),
"grant_id": "grant-c5",
"grant_expires_at": NOW + 3000,
}
def _canonical_payload(value):
result = dict(value)
result["canonical_payload_digest"] = scope.sha256_json(result)
return result
def _write(path: Path, value: bytes):
path.write_bytes(value)
if os.name == "posix":
path.chmod(0o600)
def _owner_material(tmp_path: Path, signatures: StubSignatureVerifier):
policy = _canonical_payload({
"schema": scope.POLICY_SCHEMA,
"policy_id": "C5_SOURCE_PUBLICATION_ONLY",
"owner_identity": "aios-task-grant-signer",
"repository": scope.REPOSITORY,
"trust_domain": "aios.owner",
"policy_digest": "1" * 64,
"authority_base_oid": "2" * 40,
"source_oid": "3" * 40,
"source_tree_oid": "4" * 40,
"target_agent": "agent/codex-c5-release",
"target_branch": scope.TARGET_BRANCH,
"generation": scope.GENERATION,
"execution_digest": scope.EXECUTION_DIGEST,
"exact_files": ["active-runtime/app/example.py"],
"capabilities": list(scope.SOURCE_CAPABILITIES),
"risk": "engineering",
"production_impact": "none",
"renewal": "none",
"nonce": "nonce-c5",
"issued_at": NOW - 10,
"not_before": NOW - 10,
"expires_at": NOW + 1000,
"status": "ACTIVE",
"signature_namespace": scope.OWNER_NAMESPACE,
"signature_key_id": OWNER_KEY,
})
epoch = _canonical_payload({
"epoch_id": "epoch-c5",
"owner_identity": "aios-task-grant-signer",
"trust_domain": "aios.owner",
"repository": scope.REPOSITORY,
"allowed_branch_prefixes": [scope.TARGET_BRANCH],
"allowed_capabilities": list(scope.SOURCE_CAPABILITIES),
"allowed_file_prefixes": ["active-runtime/app/"],
"risk_ceiling": "engineering",
"issued_at": NOW - 10,
"not_before": NOW - 10,
"expires_at": NOW + 1000,
"revocation_epoch": 0,
"policy_digest": "1" * 64,
"schema": scope.EPOCH_SCHEMA,
"business_goal": "publish C5 source",
"production_scope": "none",
"destructive_policy": "forbidden",
"signature_namespace": scope.OWNER_NAMESPACE,
"signature_key_id": OWNER_KEY,
"status": "ACTIVE",
})
epoch_revocations = {
"schema": "aios.owner-delegation-revocations.v1",
"revoked_epoch_ids": [],
}
policy_revocations = {
"schema": "aios.source-publication-revocations.v1",
"revoked_nonces": [],
}
epoch_root = tmp_path / "epoch"
epoch_root.mkdir()
paths = {
"policy": tmp_path / "policy.json",
"policy_revocations": tmp_path / "policy-revocations.json",
"epoch_root": epoch_root,
"epoch_revocations": tmp_path / "epoch-revocations.json",
}
_write(paths["policy"], scope.canonical_json({
"payload": policy, "signature": "valid-signature",
"key_id": OWNER_KEY, "namespace": scope.OWNER_NAMESPACE,
}))
_write(
epoch_root / "owner-delegation-epoch.canonical.json",
scope.canonical_json(epoch),
)
_write(epoch_root / "owner-delegation-epoch.sig", b"valid-signature")
_write(epoch_root / "owner-delegation-epoch.key-id", (OWNER_KEY + "\n").encode())
_write(paths["epoch_revocations"], scope.canonical_json(epoch_revocations))
_write(paths["policy_revocations"], scope.canonical_json(policy_revocations))
verifier = scope.FreshOwnerMaterialVerifier(
signature_verifier=signatures,
policy_artifact=paths["policy"],
policy_revocation_file=paths["policy_revocations"],
epoch_root=paths["epoch_root"],
epoch_revocation_file=paths["epoch_revocations"],
)
return verifier, paths
def _service(tmp_path: Path):
signatures = StubSignatureVerifier()
owner, paths = _owner_material(tmp_path, signatures)
store = scope.AppendOnlyScopeEnrollmentStore(tmp_path / "enrollments.sqlite3")
store.initialize()
service = scope.C5ScopeEnrollmentService(
deriver=scope.GovernedScopeDeriver(StubAdapter()),
owner=owner,
store=store,
enrollment_signature_verifier=signatures,
)
return service, signatures, paths, store
def _task(claimed_by="agent/codex-c5-release"):
return {"task_id": scope.TASK_ID, "status": "CLAIMED", "claimed_by": claimed_by}
def _execution():
return {
"external_job_id": "job-c5-real",
"repository": scope.REPOSITORY,
"target_branch": scope.TARGET_BRANCH,
"generation": scope.GENERATION,
"execution_digest": scope.EXECUTION_DIGEST,
}
def test_claim_then_signed_enrollment_then_runtime_resolution(tmp_path):
service, signatures, _paths, store = _service(tmp_path)
contract = service.prepare(
task=_task(), execution=_execution(), trust=TRUST, now=NOW, ttl_seconds=600,
)
assert contract["claimed_by"] == "agent/codex-c5-release"
assert contract["capability_derivation"] == {
"governed.write.files": "governed.write.file",
"writer.lease": "governed.write.branch",
}
assert contract["audiences"] == [
"aios.gitops.bridge.v2/bridge/acquireWriterLease",
"aios.gitops.bridge.v2/bridge/commitFilesGoverned",
]
assert service.enroll(
contract=contract, signature=b"valid-signature", task=_task(),
execution=_execution(), trust=TRUST, now=NOW,
) is False
assert service.enroll(
contract=contract, signature=b"valid-signature", task=_task(),
execution=_execution(), trust=TRUST, now=NOW,
) is True
resolved = service.resolve(
task=_task(), execution=_execution(), trust=TRUST, now=NOW,
)
assert json.loads(resolved["capabilities_json"]) == list(scope.PRINCIPAL_CAPABILITIES)
assert resolved["scope_id"] == contract["scope_id"]
assert signatures.calls >= 9 # policy + epoch + contract are rechecked per use
assert store.get(scope.TASK_ID)[0] == contract
def test_enrollment_requires_claimed_agent_bound_by_owner_policy(tmp_path):
service, _signatures, _paths, _store = _service(tmp_path)
with pytest.raises(scope.C5PrincipalScopeError, match="C5_TASK_CLAIM_IDENTITY_MISMATCH"):
service.prepare(
task=_task("governed-writer-random"), execution=_execution(),
trust=TRUST, now=NOW, ttl_seconds=600,
)
def test_execution_job_alias_is_canonical_and_conflicts_are_denied(tmp_path):
service, _signatures, _paths, _store = _service(tmp_path)
aliased = dict(_execution())
aliased["job_id"] = aliased.pop("external_job_id")
contract = service.prepare(
task=_task(), execution=aliased, trust=TRUST, now=NOW, ttl_seconds=600,
)
assert contract["external_job_id"] == "job-c5-real"
conflicted = dict(aliased, external_job_id="job-other")
with pytest.raises(scope.C5PrincipalScopeError, match="C5_EXECUTION_JOB_ID_INVALID"):
service.prepare(
task=_task(), execution=conflicted, trust=TRUST,
now=NOW, ttl_seconds=600,
)
def test_revocation_is_rechecked_after_append(tmp_path):
service, _signatures, paths, _store = _service(tmp_path)
contract = service.prepare(
task=_task(), execution=_execution(), trust=TRUST, now=NOW, ttl_seconds=600,
)
service.enroll(
contract=contract, signature=b"valid-signature", task=_task(),
execution=_execution(), trust=TRUST, now=NOW,
)
revoked = {
"schema": "aios.owner-delegation-revocations.v1",
"revoked_epoch_ids": ["epoch-c5"],
}
_write(paths["epoch_revocations"], scope.canonical_json(revoked))
with pytest.raises(scope.C5PrincipalScopeError, match="C5_OWNER_EPOCH_REVOKED"):
service.resolve(task=_task(), execution=_execution(), trust=TRUST, now=NOW)
def test_trust_or_execution_drift_cannot_reuse_enrollment(tmp_path):
service, _signatures, _paths, _store = _service(tmp_path)
contract = service.prepare(
task=_task(), execution=_execution(), trust=TRUST, now=NOW, ttl_seconds=600,
)
service.enroll(
contract=contract, signature=b"valid-signature", task=_task(),
execution=_execution(), trust=TRUST, now=NOW,
)
drifted = dict(TRUST, key_id="other")
with pytest.raises(scope.C5PrincipalScopeError, match="C5_ENROLLMENT_SCOPE_INVALID"):
service.resolve(task=_task(), execution=_execution(), trust=drifted, now=NOW)
bad_execution = dict(_execution(), external_job_id="job-other")
with pytest.raises(scope.C5PrincipalScopeError, match="C5_ENROLLMENT_BINDING_MISMATCH"):
service.resolve(task=_task(), execution=bad_execution, trust=TRUST, now=NOW)
def test_append_only_store_rejects_update_and_conflict(tmp_path):
service, _signatures, _paths, store = _service(tmp_path)
contract = service.prepare(
task=_task(), execution=_execution(), trust=TRUST, now=NOW, ttl_seconds=600,
)
service.enroll(
contract=contract, signature=b"valid-signature", task=_task(),
execution=_execution(), trust=TRUST, now=NOW,
)
with sqlite3.connect(store.path) as con:
with pytest.raises(sqlite3.IntegrityError, match="C5_PRINCIPAL_SCOPE_APPEND_ONLY"):
con.execute(
"UPDATE c5_principal_scope_enrollments SET scope_id='other' WHERE task_id=?",
(scope.TASK_ID,),
)
changed = dict(contract, external_job_id="job-other")
changed_without_digest = dict(changed)
changed_without_digest.pop("canonical_payload_digest")
changed["canonical_payload_digest"] = scope.sha256_json(changed_without_digest)
with pytest.raises(scope.C5PrincipalScopeError, match="C5_ENROLLMENT_CONFLICT"):
store.append(changed, b"valid-signature", NOW)
def test_not_yet_enrolled_does_not_break_base_readiness(monkeypatch):
class Base:
trust = TRUST
def readiness(self):
return {"ready": True, "schema_ready": True}
def activation_scope_task_ids(self):
return ("task-c4",)
class Runtime:
def readiness(self, trust, now):
return {"ready": False, "c5_scope_enrolled": False, "error": "C5_ENROLLMENT_REQUIRED"}
monkeypatch.setattr(
scope.RuntimeConfig, "from_environment",
classmethod(lambda cls: SimpleNamespace()),
)
monkeypatch.setattr(scope, "_runtime_service", lambda: Runtime())
composite = scope.wrap_principal_runtime_from_environment(Base(), role="verifier")
readiness = composite.readiness()
assert readiness["ready"] is True
assert readiness["c5_scope_enrolled"] is False
assert readiness["c5_scope_error"] == "C5_ENROLLMENT_REQUIRED"
# The upstream Bridge storage readiness contract is the original static
# C4 singleton. C5 is admitted later through the exact signed enrollment.
assert composite.activation_scope_task_ids() == ("task-c4",)
def test_issuer_readiness_reports_actual_loaded_server_source(monkeypatch, tmp_path):
source = tmp_path / "aios_task_principal_issuer_server.py"
source.write_bytes(b"# exact issuer fixture\n")
class Base:
def readiness(self):
return {"ready": True}
monkeypatch.setitem(
sys.modules, "aios_task_principal_issuer_server",
SimpleNamespace(__file__=str(source)),
)
monkeypatch.setattr(
scope.RuntimeConfig, "from_environment", classmethod(lambda cls: None),
)
status = scope.wrap_principal_runtime_from_environment(
Base(), role="issuer",
).readiness()
assert status["ready"] is True
assert status["source_module_path"] == str(source.resolve())
assert status["source_module_sha256"] == hashlib.sha256(
source.read_bytes()
).hexdigest()
def test_issuer_readiness_accepts_only_exact_python_m_main_module(monkeypatch, tmp_path):
source = tmp_path / "aios_task_principal_issuer_server.py"
source.write_bytes(b"# python -m issuer fixture\n")
class Base:
def readiness(self):
return {"ready": True}
monkeypatch.delitem(sys.modules, "aios_task_principal_issuer_server", raising=False)
main = SimpleNamespace(
__file__=str(source),
__spec__=SimpleNamespace(name="aios_task_principal_issuer_server"),
)
monkeypatch.setitem(sys.modules, "__main__", main)
monkeypatch.setattr(
scope.RuntimeConfig, "from_environment", classmethod(lambda cls: None),
)
composite = scope.wrap_principal_runtime_from_environment(Base(), role="issuer")
status = composite.readiness()
assert status["ready"] is True
assert status["source_module_path"] == str(source.resolve())
main.__spec__ = SimpleNamespace(name="untrusted_other_module")
status = composite.readiness()
assert status["ready"] is False
assert status["error"] == "C5_ISSUER_SOURCE_MODULE_MISSING"
def test_single_activation_config_is_exact_and_old_env_does_not_enable(monkeypatch, tmp_path):
monkeypatch.delenv("AIOS_C5_PRINCIPAL_SCOPE_CONFIG", raising=False)
monkeypatch.setenv("AIOS_C5_PRINCIPAL_SCOPE_ENABLED", "true")
assert scope.RuntimeConfig.from_environment() is None
config = {
"schema": "aios.c5-task-principal-activation.v1",
"task_id": scope.TASK_ID,
"agent_id": scope.AGENT_ID,
"execution_digest": scope.EXECUTION_DIGEST,
"store_path": "/var/lib/aios-task-principal-c5/scope-enrollment.sqlite3",
"grant_db": "/var/lib/aios-gitops/bridge-v2-staging/task-admission.sqlite3",
"plan_dir": "/var/lib/aios-gitops/bridge-v2-staging/releases/c5/plans",
"producer_state_root": "/var/lib/aios-gitops/bridge-v2-staging",
"policy_artifact": "/var/lib/aios-gitops/bridge-v2-staging/releases/c5/policy.json",
"policy_revocations": "/var/lib/aios-gitops/bridge-v2-staging/releases/c5/policy-revocations.json",
"epoch_root": "/var/lib/aios-gitops/bridge-v2-staging/releases/c5/epoch",
"epoch_revocations": "/var/lib/aios-gitops/bridge-v2-staging/releases/c5/epoch-revocations.json",
"owner_registry": "/etc/aios/single-host-signer-registry.json",
"owner_signer_id": "local-owner-authority",
"registered_signer_command": "/usr/local/libexec/aios-grant-signer-adapter",
"require_root_plan_owner": True,
"grant_db_allowed_group_gid": None,
}
path = tmp_path / "principal-activation.json"
_write(path, scope.canonical_json(config))
monkeypatch.setenv("AIOS_C5_PRINCIPAL_SCOPE_CONFIG", str(path.resolve()))
parsed = scope.RuntimeConfig.from_environment()
assert parsed is not None
assert str(parsed.owner_registry).replace("\\", "/") == "/etc/aios/single-host-signer-registry.json"
config["unexpected"] = True
_write(path, scope.canonical_json(config))
with pytest.raises(scope.C5PrincipalScopeError, match="C5_CONFIG_FIELDS_INVALID"):
scope.RuntimeConfig.from_environment()
def test_typed_issuer_signs_enrolls_and_replays_without_raw_artifact(monkeypatch, tmp_path):
service, _signatures, _paths, _store = _service(tmp_path)
calls = []
class RegistryVerifier:
def __init__(self, *args, **kwargs):
calls.append(("verifier", args, kwargs))
class Signer:
def __init__(self, *args, **kwargs):
calls.append(("signer", args, kwargs))
def _sign_canonical_payload(self, namespace, payload):
calls.append(("sign", namespace, payload))
return "valid-signature"
monkeypatch.setitem(
sys.modules, "aios_single_host_authority",
SimpleNamespace(RegistryOwnerApprovalVerifier=RegistryVerifier),
)
monkeypatch.setitem(
sys.modules, "registered_owner_signer_adapter",
SimpleNamespace(RegisteredOwnerSignerAdapter=Signer),
)
config = scope.RuntimeConfig(
store_path=tmp_path / "enrollments.sqlite3",
grant_db=tmp_path / "grant.sqlite3", plan_dir=tmp_path / "plans",
producer_state_root=tmp_path, policy_artifact=tmp_path / "policy.json",
policy_revocation_file=tmp_path / "policy-revocations.json",
epoch_root=tmp_path / "epoch",
epoch_revocation_file=tmp_path / "epoch-revocations.json",
owner_registry=Path("/etc/aios/single-host-signer-registry.json"),
owner_signer_id="local-owner-authority",
registered_signer_command=Path("/usr/local/libexec/aios-grant-signer-adapter"),
require_root_plan_owner=True, allowed_group_gid=None,
)
issuer = scope.C5ScopeEnrollmentIssuer(service, config)
receipt = issuer.issue(
task=_task(), execution=_execution(), trust=TRUST, now=NOW,
ttl_seconds=600,
)
assert receipt["status"] == "ENROLLED"
assert receipt["task_id"] == scope.TASK_ID
assert receipt["agent_id"] == scope.AGENT_ID
assert receipt["job_id"] == "job-c5-real"
assert "signature" not in receipt and "contract" not in receipt
replay = issuer.issue(
task=_task(), execution=_execution(), trust=TRUST, now=NOW,
ttl_seconds=600,
)
assert replay["replayed"] is True
assert len([item for item in calls if item[0] == "sign"]) == 1
def test_overlay_builder_patches_one_shared_admission_module(tmp_path, monkeypatch):
builder_path = ROOT / "scripts" / "build_c5_live_principal_overlay.py"
spec = importlib.util.spec_from_file_location("c5_overlay_builder_test", builder_path)
assert spec is not None and spec.loader is not None
builder = importlib.util.module_from_spec(spec)
spec.loader.exec_module(builder)
principal = tmp_path / "principal"
adapter = tmp_path / "adapter"
principal.mkdir()
adapter.mkdir()
admission_payload = b"# effective governed admission fixture\n"
(principal / "aios_task_principal_admission.py").write_bytes(admission_payload)
issuer_payload = (
b"class Base:\n"
b" _read_public_manifest = staticmethod(lambda path: path)\n"
b"_BASE = Base()\n"
b"MAX_BODY = 4 * 1024 * 1024\n"
b"def main():\n"
b" return None\n"
+ builder.ISSUER_MAIN_ANCHOR.encode()
)
(principal / "aios_task_principal_issuer_server.py").write_bytes(issuer_payload)
adapter_payloads = {
"aios_task_principal_governed_adapter.py": b"# governed adapter fixture\n",
"governed_runtime_scope.py": b"# governed scope fixture\n",
}
for name, payload in adapter_payloads.items():
(adapter / name).write_bytes(payload)
builder.PINNED = {
"aios_task_principal_admission.py": hashlib.sha256(
admission_payload
).hexdigest(),
"aios_task_principal_issuer_server.py": hashlib.sha256(
issuer_payload
).hexdigest(),
**{
name: hashlib.sha256(payload).hexdigest()
for name, payload in adapter_payloads.items()
},
}
output = tmp_path / "overlay"
monkeypatch.setattr(sys, "argv", [
str(builder_path), "--principal-source-root", str(principal),
"--adapter-source-root", str(adapter), "--output-root", str(output),
"--task-id", "fixture-governed-task", "--agent-id", "agent/fixture-c5",
])
assert builder.main() == 0
manifest = json.loads((output / "overlay-manifest.json").read_text())
assert manifest["activation"]["same_admission_overlay_for_both_units"] is True
assert manifest["entrypoints"]["issuer"] == "python3 -B -m aios_task_principal_issuer_server"
issuer_output = output / "app" / "aios_task_principal_issuer_server.py"
issuer_result = issuer_output.read_text()
assert issuer_result.startswith(issuer_payload.decode().split(
builder.ISSUER_MAIN_ANCHOR, 1
)[0])
assert "_read_public_manifest = _BASE._read_public_manifest" in issuer_result
assert issuer_result.count(builder.ISSUER_MAIN_ANCHOR) == 1
namespace = {"__name__": "issuer_fixture_import"}
exec(compile(issuer_result, str(issuer_output), "exec"), namespace)
assert namespace["_read_public_manifest"]("manifest.json") == "manifest.json"
assert namespace["MAX_BODY"] == 4 * 1024 * 1024
assert manifest["files"][issuer_output.name]["base_sha256"] == hashlib.sha256(
issuer_payload
).hexdigest()
assert manifest["files"][issuer_output.name]["result_sha256"] == hashlib.sha256(
issuer_output.read_bytes()
).hexdigest()
assert manifest["files"][issuer_output.name]["bridge_private_helper"] == (
"_read_public_manifest"
)
patched = (output / "app" / "aios_task_principal_admission.py").read_text()
assert builder.PINNED["aios_task_principal_admission.py"] == hashlib.sha256(
admission_payload
).hexdigest()
assert "_ORIGINAL_SCOPE_AND_TRUST = _legacy._scope_and_trust" in patched
assert 'if str(task["task_id"]) != C5_TASK_ID:' in patched
assert "return _ORIGINAL_SCOPE_AND_TRUST(" in patched
assert "active_digest, active_scope = _legacy._active_scope_chain(con)" in patched
assert "if active_digest != digest:" in patched
assert 'event["scope_id"] != active_scope["scope_id"]' in patched
assert "_legacy._check_revocations(con, task, active_scope, trust, None)" in patched
assert "wrap_principal_runtime_from_environment" in patched
assert manifest["files"]["aios_task_principal_admission.py"][
"runtime_base_source"
] == builder.EFFECTIVE_ADMISSION_SOURCE
assert hashlib.sha256(
(output / "app" / "aios_task_principal_governed_adapter.py").read_bytes()
).hexdigest() == scope.sha256_bytes((adapter / "aios_task_principal_governed_adapter.py").read_bytes())
assert "sys.dont_write_bytecode = True" in (
output / "bin" / "aios-c5-principal-scope"
).read_text()