Files
aios/tests/test_owner_merge_authorization_server_v2.py
T
AIOS Governed Continuation 0136a53426
autonomy-smoke-tests phase2-pr76 PASS evidence_sha256=c16c68f86ee0c4e8cfd0af3f76078be45441ee151395fbde5800145c08e189b5
autonomy-unit-tests phase2-pr76 PASS evidence_sha256=15bc76da47bdf8218f18fc8a7da65fafd841677d5f754b71a0c9f56e07d84c1b
campaign-approval-gate phase2-pr76 PASS evidence_sha256=ea7ed0516efe6736a8eaee426685e40a2393fbf3ae84104a743f394204c2ec33
git-diff-check phase2-pr76 PASS evidence_sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
py-compile phase2-pr76 PASS evidence_sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
reconciliation-contract phase2-pr76 PASS evidence_sha256=211444cb0c146f5d4a57e13b780cfe7ffb406e8a8bc2e11c02a3a4728f66b0b2
release-state-machine phase2-pr76 PASS evidence_sha256=6cea88752b3e58ae5cee56ff3b1b13eab34117918d6bad9661194c031e2dc157
secret-scan phase2-pr76 PASS evidence_sha256=7cb68dd604dc6cd4904246e16333072d512dba14c096301e08c5a2b78a45bd55
signer-validator-contract phase2-pr76 PASS evidence_sha256=775b7aa27d0c17f48b3feff20c836f14b6ed8ebcb90ec7e0b76d2a13c579268a
aios.owner-final-merge-authorization-v2 real v2 contract tests passed
feat(governed): make Owner merge authority final
2026-09-07 23:14:29 +08:00

177 lines
6.1 KiB
Python

from __future__ import annotations
import json
import os
from pathlib import Path
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from aios_owner_merge_authorization import (
OwnerMergeApprovalError,
OwnerMergeApprovalPolicy,
OwnerMergeApprovalVerifier,
)
from aios_owner_merge_authorization_server import OwnerMergeApprovalService
from aios_single_host_authority import (
RegistryOwnerApprovalVerifier,
ssh_fingerprint,
ssh_public_line,
)
NOW = 2_000_000_000
def _write_key(path: Path, key: Ed25519PrivateKey) -> None:
path.write_bytes(
key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption(),
)
)
os.chmod(path, 0o600)
@pytest.fixture
def service_env(tmp_path: Path):
owner_key = Ed25519PrivateKey.generate()
owner_path = tmp_path / "owner.key"
_write_key(owner_path, owner_key)
registry_path = tmp_path / "registry.json"
registry_path.write_text(
json.dumps(
{
"schema": "aios.single-host-signer-registry.v1",
"authority_generation": 1,
"signers": [
{
"signer_id": "local-owner-authority",
"role": "OWNER_AUTHORITY",
"provider": "local_unix_service",
"fingerprint": ssh_fingerprint(owner_key.public_key()),
"public_key": ssh_public_line(owner_key.public_key(), "owner"),
"socket": "/run/aios/owner-authority.sock",
"secret_ref": "aios-secret://owner",
"status": "ACTIVE",
"generation": 1,
"parent_signer_id": "",
"parent_fingerprint": "",
}
],
},
sort_keys=True,
)
+ "\n"
)
os.chmod(registry_path, 0o644)
policy = OwnerMergeApprovalPolicy(
repository="aios/aios",
pr_number=75,
pr_head_oid="a" * 40,
expected_main_oid="b" * 40,
operation="merge.pull-request",
required_check_contract_digest="c" * 64,
required_checks=("contract", "security"),
owner_identity="owner:aios-single-host",
audit_lineage_digest="d" * 64,
maximum_ttl_seconds=300,
)
policy_path = tmp_path / "policy.json"
policy_path.write_text(json.dumps(policy.as_dict(), sort_keys=True) + "\n")
os.chmod(policy_path, 0o600)
return {
"policy": policy,
"policy_path": policy_path,
"registry_path": registry_path,
"owner_path": owner_path,
"ledger_path": tmp_path / "issuance.sqlite3",
}
def _service(env):
return OwnerMergeApprovalService(
policy_path=env["policy_path"],
registry_path=env["registry_path"],
owner_key_path=env["owner_path"],
issuance_ledger_path=env["ledger_path"],
allowed_uids=(1000,),
require_root_owner=False,
clock=lambda: NOW,
token_urlsafe=lambda _size: "server-generated-nonce",
)
def test_service_issues_exact_owner_approval_and_exact_replay_is_audit_idempotent(service_env):
service = _service(service_env)
request = {"operation": "issue_owner_merge_approval", "payload": {}}
first = service.handle(request, peer_uid=1000)
second = _service(service_env).handle(request, peer_uid=1000)
assert first["status"] == "PASS"
assert first["idempotent"] is False
assert second["idempotent"] is True
assert second["artifact"] == first["artifact"]
verifier = OwnerMergeApprovalVerifier(
primary=RegistryOwnerApprovalVerifier(
service_env["registry_path"], require_root_owner=False
),
clock=lambda: NOW,
)
payload, proof_path = verifier.verify(first["artifact"], service_env["policy"])
assert proof_path == "primary"
assert payload["repository"] == "aios/aios"
assert payload["pr_number"] == 75
def test_expired_approval_is_retained_for_audit_but_reissued_with_a_new_nonce(service_env):
clock = [NOW]
token_values = iter(("approval-one", "nonce-one", "approval-two", "nonce-two"))
service = OwnerMergeApprovalService(
policy_path=service_env["policy_path"],
registry_path=service_env["registry_path"],
owner_key_path=service_env["owner_path"],
issuance_ledger_path=service_env["ledger_path"],
allowed_uids=(1000,),
require_root_owner=False,
clock=lambda: clock[0],
token_urlsafe=lambda _size: next(token_values),
)
request = {"operation": "issue_owner_merge_approval", "payload": {}}
first = service.handle(request, peer_uid=1000)
clock[0] += service_env["policy"].maximum_ttl_seconds
second = service.handle(request, peer_uid=1000)
assert first["idempotent"] is False
assert second["idempotent"] is False
assert second["artifact"]["payload"]["nonce"] != first["artifact"]["payload"]["nonce"]
assert second["artifact"]["payload"]["issued_at"] == clock[0]
@pytest.mark.parametrize(
"raw_request",
[
{"operation": "issue_owner_merge_approval", "payload": {"nonce": "caller-nonce"}},
{"operation": "issue_owner_merge_approval", "payload": {"pr_head_oid": "f" * 40}},
{"operation": "sign", "payload": {}},
{"operation": "sign_bytes", "payload": {}},
],
)
def test_service_rejects_caller_selected_scope_and_generic_signing(service_env, raw_request):
service = _service(service_env)
with pytest.raises(OwnerMergeApprovalError, match="OWNER_MERGE_ISSUER_"):
service.handle(raw_request, peer_uid=1000)
def test_service_rejects_untrusted_peer_before_issuing_anything(service_env):
with pytest.raises(OwnerMergeApprovalError, match="OWNER_MERGE_ISSUER_PEER_UID_FORBIDDEN"):
_service(service_env).handle(
{"operation": "issue_owner_merge_approval", "payload": {}},
peer_uid=2000,
)