from __future__ import annotations import json import os from pathlib import Path import sys import pytest APP = Path(__file__).resolve().parents[1] / "active-runtime" / "app" sys.path.insert(0, str(APP)) import trusted_builder_signer_provisioning as provisioning # noqa: E402 def _source(tmp_path: Path) -> tuple[Path, list[str]]: root = tmp_path / "source" for relative in provisioning.SOURCE_EVIDENCE_PATHS: path = root / relative path.parent.mkdir(parents=True, exist_ok=True) if relative != provisioning.PACKAGE_MANIFEST_PATH: path.write_text("source:" + relative + "\n", encoding="utf-8") os.chmod(path, 0o644) manifest = root / provisioning.PACKAGE_MANIFEST_PATH manifest.write_bytes(provisioning.pretty_json(provisioning.package_contract_document())) os.chmod(manifest, 0o644) return root, list(provisioning.SOURCE_EVIDENCE_PATHS) def _payload(tmp_path: Path): source, paths = _source(tmp_path) output = tmp_path / "payload.json" value = provisioning.build_payload( source, output, main_oid="c" * 40, tree_oid="d" * 40, pr_number=70, pr_head_oid="e" * 40, merge_parent_oids=["a" * 40, "e" * 40], source_files=paths, nonce="test-provisioning-nonce-001", ) return source, output, value def test_fresh_payload_is_bound_to_final_common_main_and_ordered_pr_parent(tmp_path: Path): _source_root, output, payload = _payload(tmp_path) assert output.read_bytes().startswith(b"{") provisioning._validate_payload(payload) assert payload["source"]["gitea_main_oid"] == payload["source"]["governed_main_oid"] assert payload["source"]["merge_parent_oids"][1] == payload["source"]["pr_head_oid"] assert set(payload["source"]["source_evidence_sha256"]) == set(provisioning.SOURCE_EVIDENCE_PATHS) assert set(payload["allowed_capabilities"]) == provisioning.ALLOWED_CAPABILITIES assert set(payload["denied_capabilities"]) == provisioning.DENIED_CAPABILITIES @pytest.mark.parametrize("field", ["allowed_capabilities", "denied_capabilities"]) def test_payload_rejects_capability_scope_tampering(tmp_path: Path, field: str): _source_root, _output, payload = _payload(tmp_path) payload[field] = ["owner_release_approval"] with pytest.raises(provisioning.ProvisioningError, match="CAPABILITY_SCOPE_INVALID"): provisioning._validate_payload(payload) def test_payload_rejects_main_divergence_and_wrong_second_parent(tmp_path: Path): _source_root, _output, payload = _payload(tmp_path) payload["source"]["governed_main_oid"] = "f" * 40 with pytest.raises(provisioning.ProvisioningError, match="SOURCE_MAIN_NOT_CONVERGED"): provisioning._validate_payload(payload) _source_root, _output, payload = _payload(tmp_path / "second") payload["source"]["merge_parent_oids"][1] = "a" * 40 with pytest.raises(provisioning.ProvisioningError, match="SOURCE_PR_HEAD_SECOND_PARENT_INVALID"): provisioning._validate_payload(payload) def test_payload_rejects_missing_or_extra_source_evidence(tmp_path: Path): _source_root, _output, payload = _payload(tmp_path) payload["source"]["source_evidence_sha256"].pop(next(iter(payload["source"]["source_evidence_sha256"]))) with pytest.raises(provisioning.ProvisioningError, match="SOURCE_EVIDENCE_SET_INVALID"): provisioning._validate_payload(payload) _source_root, _output, payload = _payload(tmp_path / "second") payload["source"]["source_evidence_sha256"]["extra.txt"] = "0" * 64 with pytest.raises(provisioning.ProvisioningError, match="SOURCE_EVIDENCE_SET_INVALID"): provisioning._validate_payload(payload) def test_package_contract_rejects_incompatible_source_mode_and_tampering(tmp_path: Path): source, _paths = _source(tmp_path) service = source / "deploy/systemd/aios-trusted-candidate-builder.service" os.chmod(service, 0o601) with pytest.raises(provisioning.ProvisioningError, match="SOURCE_EVIDENCE_MODE_INVALID"): provisioning.verify_package_contract(source) os.chmod(service, 0o644) manifest = source / provisioning.PACKAGE_MANIFEST_PATH value = json.loads(manifest.read_text(encoding="utf-8")) value["activation"]["credential_installation"] = True manifest.write_text(json.dumps(value), encoding="utf-8") with pytest.raises(provisioning.ProvisioningError, match="PACKAGE_CONTRACT_INVALID"): provisioning.verify_package_contract(source) def test_package_contract_declares_socket_activation_no_credentials_and_0755_launcher(tmp_path: Path): source, _paths = _source(tmp_path) provisioning.verify_package_contract(source) value = json.loads((source / provisioning.PACKAGE_MANIFEST_PATH).read_text(encoding="utf-8")) launcher = next(item for item in value["files"] if item["source_path"] == "deploy/libexec/aios-trusted-builder-signer-provisioner") assert launcher["install_mode"] == "0755" assert value["activation"] == { "daemon_reload": True, "enable_units": ["aios-trusted-candidate-builder.socket"], "service_start": "socket-activation-only", "credential_installation": False, "signer_invocation": False, } def test_provisioner_self_test_has_no_mutation(capsys): assert provisioning.main(["--self-test"]) == 0 assert capsys.readouterr().out.strip() == "PASS" def _replay_fixture(tmp_path: Path): _source_root, payload_path, payload = _payload(tmp_path) raw = payload_path.read_bytes() signature = tmp_path / "owner.sig" signature.write_bytes(b"owner-signature-test") os.chmod(signature, 0o600) config = provisioning.ProvisionerConfig() config.credential_store = tmp_path / "credstore" config.public_keyring = tmp_path / "state" / "public-keyring.json" config.binding_file = tmp_path / "state" / "service-principal-binding.json" config.receipt_log = tmp_path / "state" / "provisioning-receipts.jsonl" config.credential_store.mkdir(mode=0o700) os.chmod(config.credential_store, 0o700) config.public_keyring.parent.mkdir(mode=0o700) public = b"p" * 32 now = provisioning.utc_now() keyring = provisioning._keyring(payload["signing_identity"], public, now) config.public_keyring.write_bytes(provisioning.pretty_json(keyring)) os.chmod(config.public_keyring, 0o600) binding = { "schema": "aios.trusted-builder.service-principal-binding.v1", "purpose": provisioning.PURPOSE, "service_principal": payload["signing_identity"]["service_principal"], "key_id": payload["signing_identity"]["key_id"], "generation": payload["signing_identity"]["key_generation"], "algorithm": "Ed25519", "private_key_export": False, "public_key_sha256": provisioning.sha256_bytes(public), "source_commit": payload["source"]["gitea_main_oid"], "source_tree": payload["source"]["gitea_main_tree_oid"], "payload_sha256": provisioning.sha256_bytes(raw), } config.binding_file.write_bytes(provisioning.pretty_json(binding)) os.chmod(config.binding_file, 0o600) for name in ("signing_private_key", "builder_hmac"): target = config.credential_store / name target.write_bytes(("encrypted-" + name).encode()) os.chmod(target, 0o600) signature_bytes = signature.read_bytes() receipt = { "schema": "aios.trusted-builder-signer-provisioning-receipt.v1", "status": "COMPLETED", "created_at": provisioning.iso(now), "operation_key": "payload:" + provisioning.sha256_bytes(raw) + ":signature:" + provisioning.sha256_bytes(signature_bytes), "payload_sha256": provisioning.sha256_bytes(raw), "signature_sha256": provisioning.sha256_bytes(signature_bytes), "source_commit": payload["source"]["gitea_main_oid"], "source_tree": payload["source"]["gitea_main_tree_oid"], "key_id": payload["signing_identity"]["key_id"], "generation": payload["signing_identity"]["key_generation"], "algorithm": "Ed25519", "purpose": provisioning.PURPOSE, "service_principal": payload["signing_identity"]["service_principal"], "service_principal_bound": True, "public_key_sha256": provisioning.sha256_bytes(public), "private_key_exportable": False, "credential_projection": "systemd-encrypted-host-credential", "negative_capability_tests": {name: True for name in sorted(provisioning.DENIED_CAPABILITIES)}, "production_mutation": False, "protected_ref_mutation": False, "tag_mutation": False, "baseline_mutation": False, } return config, payload_path, payload, signature, receipt def test_legacy_multiline_receipt_parse(tmp_path: Path): path = tmp_path / "receipts.jsonl" receipt = {"schema": "legacy", "status": "COMPLETED", "nested": {"value": True}} path.write_bytes(provisioning.pretty_json(receipt)) assert provisioning._receipts(path) == [receipt] def test_new_canonical_jsonl_receipt_parse(tmp_path: Path): path = tmp_path / "receipts.jsonl" rows = [{"id": 1}, {"id": 2}] for row in rows: provisioning._append(path, row) assert path.read_bytes() == b"".join(provisioning.canonical_json(row) + b"\n" for row in rows) assert provisioning._receipts(path) == rows @pytest.mark.parametrize( "content", [b'{"status":"COMPLETED"}\nnot-json\n', b'{"status":"COMPLETED"'], ids=["malformed", "truncated"], ) def test_malformed_or_truncated_receipt_fails_closed(tmp_path: Path, content: bytes): path = tmp_path / "receipts.jsonl" path.write_bytes(content) with pytest.raises(provisioning.ProvisioningError, match="RECEIPT_LOG_INVALID"): provisioning._receipts(path) def test_preexisting_valid_state_is_adopted_without_mutation(tmp_path: Path, monkeypatch): config, payload_path, _payload_value, signature, receipt = _replay_fixture(tmp_path) config.receipt_log.write_bytes(provisioning.pretty_json(receipt)) os.chmod(config.receipt_log, 0o600) before = { path: path.read_bytes() for path in ( config.receipt_log, config.public_keyring, config.binding_file, config.credential_store / "signing_private_key", config.credential_store / "builder_hmac", ) } monkeypatch.setattr(provisioning, "_run_signature_verify", lambda *_args: None) monkeypatch.setattr(provisioning, "_verify_replay_source", lambda *_args: None) monkeypatch.setattr(provisioning.os, "geteuid", lambda: 0) result = provisioning.provision(config, payload_path, signature) assert result["status"] == "IDEMPOTENT_REPLAY_PASS" assert result["mutation_executed"] is False assert result["existing_signer_state_adopted"] is True assert result["private_key_exportable"] is False assert before == {path: path.read_bytes() for path in before} def test_duplicate_replay_is_idempotent(tmp_path: Path, monkeypatch): config, payload_path, _payload_value, signature, receipt = _replay_fixture(tmp_path) config.receipt_log.write_bytes(provisioning.pretty_json(receipt)) os.chmod(config.receipt_log, 0o600) monkeypatch.setattr(provisioning, "_run_signature_verify", lambda *_args: None) monkeypatch.setattr(provisioning, "_verify_replay_source", lambda *_args: None) monkeypatch.setattr(provisioning.os, "geteuid", lambda: 0) first = provisioning.provision(config, payload_path, signature) receipt_bytes = config.receipt_log.read_bytes() second = provisioning.provision(config, payload_path, signature) assert first == second assert config.receipt_log.read_bytes() == receipt_bytes def test_wrong_payload_is_rejected_against_preexisting_state(tmp_path: Path, monkeypatch): config, payload_path, payload, signature, receipt = _replay_fixture(tmp_path) config.receipt_log.write_bytes(provisioning.pretty_json(receipt)) os.chmod(config.receipt_log, 0o600) payload["nonce"] = "wrong-payload-nonce-0002" payload_path.write_bytes(provisioning.pretty_json(payload)) monkeypatch.setattr(provisioning, "_run_signature_verify", lambda *_args: None) monkeypatch.setattr(provisioning, "_verify_source", lambda *_args: None) monkeypatch.setattr(provisioning.os, "geteuid", lambda: 0) with pytest.raises(provisioning.ProvisioningError, match="PREEXISTING_STATE_CONFLICT"): provisioning.provision(config, payload_path, signature) def test_wrong_signature_is_rejected(tmp_path: Path, monkeypatch): config, payload_path, _payload_value, signature, receipt = _replay_fixture(tmp_path) config.receipt_log.write_bytes(provisioning.pretty_json(receipt)) monkeypatch.setattr( provisioning, "_run_signature_verify", lambda *_args: (_ for _ in ()).throw(provisioning.ProvisioningError("OWNER_SIGNATURE_VERIFY_FAILED")), ) monkeypatch.setattr(provisioning.os, "geteuid", lambda: 0) with pytest.raises(provisioning.ProvisioningError, match="OWNER_SIGNATURE_VERIFY_FAILED"): provisioning.provision(config, payload_path, signature) def test_wrong_purpose_is_rejected(tmp_path: Path): _source_root, _payload_path, payload = _payload(tmp_path) payload["signing_identity"]["purpose"] = "WRONG_PURPOSE" with pytest.raises(provisioning.ProvisioningError, match="SIGNING_IDENTITY_INVALID"): provisioning._validate_payload(payload) def test_replay_rejects_state_binding_tampering(tmp_path: Path, monkeypatch): config, payload_path, _payload_value, signature, receipt = _replay_fixture(tmp_path) receipt["service_principal"] = "wrong.service" config.receipt_log.write_bytes(provisioning.pretty_json(receipt)) monkeypatch.setattr(provisioning, "_run_signature_verify", lambda *_args: None) monkeypatch.setattr(provisioning, "_verify_replay_source", lambda *_args: None) monkeypatch.setattr(provisioning.os, "geteuid", lambda: 0) with pytest.raises(provisioning.ProvisioningError, match="RECEIPT_CONFLICT"): provisioning.provision(config, payload_path, signature)