Enterprise V2R2C2 P0: wire the Round-3 Provider Authentication code bundle into the governed evidence producer/runtime while preserving the Round-2 chain. - Add trusted_provider_receipts.py: fail-closed TrustedProviderReceiptResolver (root-owned-store-v1 / hmac-sha256-v1) enforcing absolute-ref, .. traversal, symlink, owner-UID, mode, hardlink, max-size, identity/operation allowlist, canonical SHA256, source/tree OID, issued_at/skew/age, HMAC compare_digest. - Add governed_provider_auth_adapter.py: receipt_ref-only evidence; rejects CALLER_SUPPLIED_PROVIDER_RECEIPT_DENIED; PROVIDER_AUTHENTICATION_REQUIRED when no resolver; Gate-W (tests/gitea/secret_scan) vs Gate-R split. - Add governed_authenticated_evidence_operation.py: production produce_authenticated_evidence_closure + validate_gate_w_provider_auth. - Register governed.authenticated.release.evidence.produce with handler_ref -> governed_authenticated_evidence_operation.produce_authenticated_evidence_closure. - Build TrustedProviderReceiptResolver from governed evidence-providers policy (never request-supplied store path/identity/key id/HMAC). - Two-layer receipt binding: legacy v1 receipt kept, plus resolver-authenticated v2 provider_receipt provenance as second-layer input; no gate relaxed. - Add config/gitops/trusted-evidence-providers.json + verify_round3_integration.py + 3 bundled test files. Linux acceptance (cn-network-mkt): bundle 24 passed; producer 10; runtime 5; materializer 5; captrace/trusted-builder 30; task-admission 106; verifier PASS. Windows: round2 producer/runtime 15 passed; bundle 23/1 platform-boundary (os.getuid absent, symlink privilege, py3.13 stat_result) - Linux-validated.
142 lines
4.8 KiB
Python
142 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from governed_provider_auth_adapter import (
|
|
ALL_RELEASE_EVIDENCE_KINDS,
|
|
authenticate_build_artifacts,
|
|
gate_w_authentication_status,
|
|
)
|
|
from trusted_provider_receipts import (
|
|
PROVIDER_RECEIPT_SCHEMA,
|
|
PROVIDER_RECEIPT_VERSION,
|
|
ProviderAuthenticationError,
|
|
TrustedProviderPolicy,
|
|
TrustedProviderReceiptResolver,
|
|
canonical_json_bytes,
|
|
canonical_sha256,
|
|
)
|
|
|
|
SOURCE = "a" * 40
|
|
TREE = "b" * 40
|
|
NOW = datetime(2026, 9, 9, 5, 0, tzinfo=timezone.utc)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FakeBuild:
|
|
source_commit: str
|
|
tree_oid: str
|
|
tests: dict
|
|
canary: dict
|
|
gitea: dict
|
|
rollback: dict
|
|
secret_scan: dict
|
|
|
|
|
|
def _policy(kind: str, root: Path) -> TrustedProviderPolicy:
|
|
return TrustedProviderPolicy(
|
|
evidence_kind=kind,
|
|
provider_identity=f"aios.{kind}-provider",
|
|
store_id=kind,
|
|
store_root=root / kind,
|
|
allowed_operations=frozenset({f"{kind}.run"}),
|
|
expected_owner_uid=os.getuid(),
|
|
allowed_directory_modes=frozenset({0o700}),
|
|
allowed_receipt_modes=frozenset({0o600}),
|
|
max_age_seconds=300,
|
|
)
|
|
|
|
|
|
def _write_receipt(root: Path, kind: str, result: dict) -> str:
|
|
store = root / kind
|
|
store.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
os.chmod(store, 0o700)
|
|
ref = f"run-1/{kind}.json"
|
|
path = store / ref
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
os.chmod(path.parent, 0o700)
|
|
payload = {
|
|
"schema": PROVIDER_RECEIPT_SCHEMA,
|
|
"version": PROVIDER_RECEIPT_VERSION,
|
|
"provider_identity": f"aios.{kind}-provider",
|
|
"provider_operation": f"{kind}.run",
|
|
"operation_run_id": f"{kind}-run-1",
|
|
"result_reference": f"runs/{kind}-run-1/result.json",
|
|
"content_sha256": canonical_sha256(result),
|
|
"source_oid": SOURCE,
|
|
"tree_oid": TREE,
|
|
"environment": "linux-isolated",
|
|
"issued_at": "2026-09-09T04:59:00+00:00",
|
|
"result": result,
|
|
"authentication": {"kind": "root-owned-store-v1", "store_id": kind},
|
|
}
|
|
path.write_bytes(canonical_json_bytes(payload))
|
|
os.chmod(path, 0o600)
|
|
return ref
|
|
|
|
|
|
def _resolver(root: Path):
|
|
policies = {kind: _policy(kind, root) for kind in ALL_RELEASE_EVIDENCE_KINDS}
|
|
return TrustedProviderReceiptResolver(policies, now_fn=lambda: NOW)
|
|
|
|
|
|
def _request(root: Path) -> FakeBuild:
|
|
refs = {}
|
|
for kind in ALL_RELEASE_EVIDENCE_KINDS:
|
|
result = {"passed": True, "kind": kind}
|
|
if kind == "gitea":
|
|
result = {"consistent": True, "kind": kind}
|
|
elif kind == "rollback":
|
|
result = {"ready": True, "kind": kind}
|
|
refs[kind] = {"receipt_ref": _write_receipt(root, kind, result)}
|
|
return FakeBuild(source_commit=SOURCE, tree_oid=TREE, **refs)
|
|
|
|
|
|
def test_full_release_authentication_replaces_receipt_refs_with_authenticated_results(tmp_path: Path):
|
|
request = _request(tmp_path)
|
|
authenticated = authenticate_build_artifacts(request, resolver=_resolver(tmp_path))
|
|
assert authenticated.tests["passed"] is True
|
|
assert authenticated.gitea["consistent"] is True
|
|
assert authenticated.rollback["ready"] is True
|
|
assert authenticated.tests["provider_receipt"]["provider_identity"] == "aios.tests-provider"
|
|
assert "receipt_ref" not in authenticated.tests
|
|
|
|
|
|
def test_missing_resolver_fails_closed(tmp_path: Path):
|
|
request = _request(tmp_path)
|
|
with pytest.raises(ProviderAuthenticationError, match="PROVIDER_AUTHENTICATION_REQUIRED"):
|
|
authenticate_build_artifacts(request, resolver=None)
|
|
|
|
|
|
def test_gate_w_authenticates_only_currently_real_provider_classes(tmp_path: Path):
|
|
request = _request(tmp_path)
|
|
# Remove canary/rollback stores entirely; Gate W must not pretend final
|
|
# release closure exists, but it can validate tests/Gitea/secret-scan.
|
|
for kind in ("canary", "rollback"):
|
|
store = tmp_path / kind
|
|
for child in sorted(store.rglob("*"), reverse=True):
|
|
if child.is_file():
|
|
child.unlink()
|
|
elif child.is_dir():
|
|
child.rmdir()
|
|
store.rmdir()
|
|
|
|
status = gate_w_authentication_status(request, resolver=_resolver(tmp_path))
|
|
assert status == {
|
|
"producer_runtime": "PRODUCER_RUNTIME_REGISTRATION_PASS",
|
|
"provider_auth": "PROVIDER_AUTH_ENFORCEMENT_PASS",
|
|
"final_release_closure": "FINAL_RELEASE_CLOSURE_PENDING_GATE_R",
|
|
}
|
|
|
|
|
|
def test_full_release_authentication_requires_canary_and_rollback_receipts(tmp_path: Path):
|
|
request = _request(tmp_path)
|
|
(tmp_path / "canary" / "run-1" / "canary.json").unlink()
|
|
with pytest.raises(ProviderAuthenticationError, match="PROVIDER_RECEIPT_UNAVAILABLE"):
|
|
authenticate_build_artifacts(request, resolver=_resolver(tmp_path))
|