Fail closed on unattended authority boundaries

This commit is contained in:
Codex Security
2026-09-26 11:52:08 +08:00
parent d2ad00139d
commit fb8a0f3e47
2 changed files with 92 additions and 46 deletions
@@ -12,7 +12,7 @@ import hashlib
import json
import re
import time
from typing import Any, Callable, Mapping, Sequence
from typing import Any, Callable, Mapping
OID_RE = re.compile(r"^[0-9a-fA-F]{40}$")
@@ -20,9 +20,9 @@ SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$")
IDENT_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$")
RETRYABLE_CODES = {408, 425, 429, 500, 502, 503, 504}
RETRYABLE_NAMES = {
"TIMEOUT", "TIMED_OUT", "UNAVAILABLE", "BUSY", "TEMPORARY_FAILURE",
"TASK_ADMISSION_IN_PROGRESS", "TASK_ADMISSION_TASK_STATE_UNKNOWN",
"UNAVAILABLE", "BUSY", "TEMPORARY_FAILURE", "TASK_ADMISSION_IN_PROGRESS",
}
RISKS = {"low", "medium", "high"}
class UnattendedReleaseError(RuntimeError):
@@ -78,21 +78,22 @@ class SourcePublicationIntent:
_require_oid("source_commit", self.source_commit)
_require_oid("source_tree_oid", self.source_tree_oid)
_require_oid("authority_base_oid", self.authority_base_oid)
if not isinstance(self.target_branch, str) or not self.target_branch or self.target_branch.startswith("-"):
if not isinstance(self.target_branch, str) or not self.target_branch or not self.target_branch.startswith("agent/") or self.target_branch.startswith("-"):
raise UnattendedReleaseError("INVALID_TARGET_BRANCH")
if ".." in self.target_branch or "//" in self.target_branch or "@{" in self.target_branch:
raise UnattendedReleaseError("INVALID_TARGET_BRANCH")
if not isinstance(self.generation, int) or isinstance(self.generation, bool) or self.generation < 1:
raise UnattendedReleaseError("INVALID_GENERATION")
_require_sha("execution_digest", self.execution_digest)
if not self.file_allowlist or any(not isinstance(p, str) or not p or p.startswith("/") or ".." in p for p in self.file_allowlist):
if not self.file_allowlist or any(not isinstance(p, str) or not p or p.startswith(("/", "\\")) or ".." in p or "\\" in p or p == ".git" or p.startswith(".git/") for p in self.file_allowlist):
raise UnattendedReleaseError("INVALID_FILE_ALLOWLIST")
if len(set(self.file_allowlist)) != len(self.file_allowlist):
raise UnattendedReleaseError("DUPLICATE_FILE_ALLOWLIST")
if not self.requested_capabilities or any(not isinstance(c, str) or not c or "*" in c for c in self.requested_capabilities):
raise UnattendedReleaseError("INVALID_CAPABILITIES")
if not self.risk or not isinstance(self.risk, str):
if not isinstance(self.risk, str) or self.risk.lower() not in RISKS:
raise UnattendedReleaseError("INVALID_RISK")
object.__setattr__(self, "risk", self.risk.lower())
object.__setattr__(self, "file_allowlist", tuple(sorted(self.file_allowlist)))
object.__setattr__(self, "requested_capabilities", tuple(sorted(set(self.requested_capabilities))))
if not IDENT_RE.fullmatch(self.event_id):
@@ -118,20 +119,30 @@ class SourcePublicationIntent:
def idempotency_key(self) -> str:
return "release-intake-" + _digest({"task_id": self.task_id, "execution_digest": self.execution_digest.lower()})[:32]
def admission_request(self) -> dict[str, Any]:
"""Return the sole normalized request accepted by Task admission."""
return {"idempotency_key": self.idempotency_key, "task_id": self.task_id,
"repository": self.repository, "authority_base_oid": self.authority_base_oid,
"target_branch": self.target_branch, "generation": self.generation,
"execution_digest": self.execution_digest, "requested_capabilities": list(self.requested_capabilities),
"file_allowlist": list(self.file_allowlist), "risk": self.risk}
def _error_code(exc: BaseException) -> str:
code = getattr(exc, "code", None) or getattr(exc, "status", None) or str(exc)
return str(code).upper()
code = getattr(exc, "code", None) or getattr(exc, "status", None) or exc.__class__.__name__
return str(code).upper().replace(" ", "_")
def _retryable(exc: BaseException) -> bool:
status = getattr(exc, "status_code", None)
if status is None:
status = getattr(exc, "http_status", None)
status = getattr(exc, "status_code", None) or getattr(exc, "http_status", None) or getattr(exc, "status", None)
if isinstance(status, int):
return status in RETRYABLE_CODES
code = _error_code(exc)
return any(name in code for name in RETRYABLE_NAMES)
return code in RETRYABLE_NAMES
def _unknown_outcome(exc: BaseException) -> bool:
return isinstance(exc, (TimeoutError, ConnectionError, OSError)) or _error_code(exc) in {"TASK_ADMISSION_TASK_STATE_UNKNOWN", "TIMEOUT", "TIMED_OUT", "CONNECTION_LOST"}
class GovernedUnattendedReleaseRunner:
@@ -141,12 +152,16 @@ class GovernedUnattendedReleaseRunner:
publication_adapter: Any = None, owner_approval_broker: Callable[[Mapping[str, Any]], Mapping[str, Any]] | None = None,
deployment_enabled: bool = False, deployment_service: Any = None,
max_attempts: int = 3, deadline_seconds: float = 30.0,
environment_mode: str = "SIMULATION",
clock: Callable[[], float] = time.monotonic, sleep: Callable[[float], None] = time.sleep):
if admission is None:
raise ValueError("admission is required")
if max_attempts < 1 or max_attempts > 10 or deadline_seconds <= 0:
raise ValueError("invalid retry bounds")
self.admission = admission
self.environment_mode = str(environment_mode).upper()
if self.environment_mode not in {"SIMULATION", "AUTHORITY"}:
raise ValueError("environment_mode must be SIMULATION or AUTHORITY")
self.release_service = release_service
self.publication_adapter = publication_adapter
self.owner_approval_broker = owner_approval_broker
@@ -159,60 +174,82 @@ class GovernedUnattendedReleaseRunner:
def intake(self, intent: SourcePublicationIntent) -> dict[str, Any]:
if not isinstance(intent, SourcePublicationIntent):
raise UnattendedReleaseError("INTENT_TYPE_REQUIRED")
return {"phase": "PREPARATION", "mode": "SIMULATION", "task_id": intent.task_id,
return {"phase": "PREPARATION", "mode": self.environment_mode, "task_id": intent.task_id,
"idempotency_key": intent.idempotency_key, "intent": intent.as_dict(),
"release_rights": False, "deployment_rights": False}
def admit(self, intent: SourcePublicationIntent, authorization: Mapping[str, Any]) -> dict[str, Any]:
if not isinstance(authorization, Mapping):
raise UnattendedReleaseError("AUTHORIZATION_REQUIRED")
raw = {"idempotency_key": intent.idempotency_key, "task_id": intent.task_id,
"repository": intent.repository, "authority_base_oid": intent.authority_base_oid,
"target_branch": intent.target_branch, "generation": intent.generation,
"execution_digest": intent.execution_digest, "requested_capabilities": list(intent.requested_capabilities),
"file_allowlist": list(intent.file_allowlist), "risk": intent.risk}
if self.environment_mode != "AUTHORITY":
return {"phase": "BLOCKED", "mode": self.environment_mode, "task_id": intent.task_id, "error": "AUTHORITY_MODE_REQUIRED"}
raw = intent.admission_request()
deadline = self.clock() + self.deadline_seconds
envelope_id = None
for attempt in range(1, self.max_attempts + 1):
if self.clock() >= deadline:
return {"phase": "RECONCILIATION_REQUIRED" if envelope_id else "BLOCKED", "mode": "AUTHORITY", "task_id": intent.task_id, "error": "ADMISSION_DEADLINE_EXCEEDED"}
try:
created = self.admission.create_envelope(raw)
envelope = created.get("envelope", created) if isinstance(created, Mapping) else {}
envelope_id = envelope.get("envelope_id") or envelope_id
auth = dict(authorization)
auth["envelope_id"] = envelope_id
if self.clock() >= deadline:
return self._reconcile(envelope_id, intent, "ADMISSION_DEADLINE_EXCEEDED")
receipt = self.admission.authorize_and_admit(auth)
if self.clock() >= deadline:
return self._reconcile(envelope_id, intent, "ADMISSION_DEADLINE_EXCEEDED")
return {"phase": "ADMITTED", "mode": "AUTHORITY", "attempt": attempt,
"envelope": created, "receipt": self.admission.authorize_and_admit(auth)}
"envelope": created, "receipt": receipt}
except Exception as exc:
status_code = getattr(exc, "status_code", None) or getattr(exc, "http_status", None)
if status_code == 403 or "403" in _error_code(exc):
status_code = getattr(exc, "status_code", None) or getattr(exc, "http_status", None) or getattr(exc, "status", None)
if status_code == 403 or _error_code(exc) in {"403", "FORBIDDEN"}:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "task_id": intent.task_id, "error": _error_code(exc)}
if envelope_id and (not _retryable(exc) or self.clock() >= deadline or attempt >= self.max_attempts):
try:
status = self.admission.status(envelope_id)
except Exception as status_exc:
return {"phase": "RECONCILIATION_REQUIRED", "mode": "AUTHORITY", "task_id": intent.task_id,
"error": _error_code(exc), "status_error": _error_code(status_exc)}
return {"phase": "RECONCILED", "mode": "AUTHORITY", "status": status, "task_id": intent.task_id}
if envelope_id and (_unknown_outcome(exc) or not _retryable(exc) or self.clock() >= deadline or attempt >= self.max_attempts):
return self._reconcile(envelope_id, intent, _error_code(exc))
if not _retryable(exc) or self.clock() >= deadline or attempt >= self.max_attempts:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "task_id": intent.task_id, "error": _error_code(exc)}
self.sleep(min(0.05 * (2 ** (attempt - 1)), max(0.0, deadline - self.clock())))
raise UnattendedReleaseError("ADMISSION_RETRY_EXHAUSTED")
def _reconcile(self, envelope_id: str | None, intent: SourcePublicationIntent, error: str) -> dict[str, Any]:
if not envelope_id:
return {"phase": "RECONCILIATION_REQUIRED", "mode": "AUTHORITY", "task_id": intent.task_id, "error": error}
try:
status = self.admission.status(envelope_id)
except Exception as exc:
return {"phase": "RECONCILIATION_REQUIRED", "mode": "AUTHORITY", "task_id": intent.task_id, "error": error, "status_error": _error_code(exc)}
state = str(status.get("status") or status.get("task_state") or "").upper() if isinstance(status, Mapping) else ""
if state in {"NOT_APPLIED", "ABSENT", "NOT_FOUND"}:
return {"phase": "RETRYABLE_RECONCILIATION", "mode": "AUTHORITY", "task_id": intent.task_id, "status": status, "error": error}
if state in {"QUEUED", "CLAIMED", "RUNNING", "ADMITTED", "SUCCEEDED", "COMPLETED"}:
return {"phase": "RECONCILED", "mode": "AUTHORITY", "task_id": intent.task_id, "status": status}
return {"phase": "RECONCILIATION_REQUIRED", "mode": "AUTHORITY", "task_id": intent.task_id, "status": status, "error": error}
def publish(self, intent: SourcePublicationIntent, admission_receipt: Mapping[str, Any]) -> dict[str, Any]:
if self.environment_mode != "AUTHORITY":
return {"phase": "BLOCKED", "mode": self.environment_mode, "error": "AUTHORITY_MODE_REQUIRED"}
if self.publication_adapter is None:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "PUBLICATION_BACKEND_MISSING"}
required = ("task_lease", "task_principal", "writer_lease", "commit_receipt", "source_oid", "tree_oid")
required = ("task_lease", "task_principal", "writer_lease", "commit_receipt", "source_oid", "tree_oid", "authenticated")
try:
if not isinstance(admission_receipt, Mapping) or any(str(admission_receipt.get(k, "")) != expected for k, expected in (("task_id", intent.task_id), ("authority_base_oid", intent.authority_base_oid), ("target_branch", intent.target_branch), ("execution_digest", intent.execution_digest))):
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "ADMISSION_RECEIPT_BINDING_MISMATCH"}
result = self.publication_adapter.publish(intent, admission_receipt)
if not isinstance(result, Mapping) or any(not result.get(k) for k in required):
if not isinstance(result, Mapping) or result.get("authenticated") is not True or any(not result.get(k) for k in required[:-1]):
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "PUBLICATION_LEASE_CHAIN_MISSING"}
if str(result["source_oid"]).lower() != intent.source_commit.lower() or str(result["tree_oid"]).lower() != intent.source_tree_oid.lower():
if not OID_RE.fullmatch(str(result["source_oid"])) or str(result["tree_oid"]).lower() != intent.source_tree_oid.lower() or any(str(result.get(k, "")) != str(v) for k, v in (("task_id", intent.task_id), ("authority_base_oid", intent.authority_base_oid), ("target_branch", intent.target_branch), ("execution_digest", intent.execution_digest))):
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "PUBLICATION_RC_BINDING_MISMATCH"}
return {"phase": "PUBLISHED", "mode": "AUTHORITY", "readback": dict(result)}
except Exception as exc:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": _error_code(exc)}
def postpublication_rc(self, *, event_id: str, source_commit: str, version: str = "") -> dict[str, Any]:
def postpublication_rc(self, *, event_id: str, source_commit: str, version: str = "", source_readback: Mapping[str, Any] | None = None) -> dict[str, Any]:
if self.environment_mode != "AUTHORITY":
return {"phase": "BLOCKED", "mode": self.environment_mode, "error": "AUTHORITY_MODE_REQUIRED"}
if not isinstance(source_readback, Mapping) or source_readback.get("authenticated") is not True or str(source_readback.get("source_commit", "")).lower() != str(source_commit).lower():
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "SOURCE_READBACK_REQUIRED"}
if self.release_service is None:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "RELEASE_BACKEND_MISSING"}
try:
@@ -226,11 +263,13 @@ class GovernedUnattendedReleaseRunner:
candidate = self.release_service.get_release_candidate(candidate_id)
plan = self.release_service.get_release_plan(candidate_id)
acceptance = self.release_service.get_acceptance(candidate_id)
if (not isinstance(candidate, Mapping) or not isinstance(plan, Mapping) or not isinstance(acceptance, Mapping)
deployment = plan.get("deployment") if isinstance(plan, Mapping) else None
if (not isinstance(candidate, Mapping) or not isinstance(plan, Mapping) or not isinstance(deployment, Mapping) or not isinstance(acceptance, Mapping)
or str(candidate.get("candidate_id")) != candidate_id
or str(candidate.get("source_commit")) .lower() != str(source_commit).lower()
or str(plan.get("candidate_id", candidate_id)) != candidate_id
or str(plan.get("source_commit")) .lower() != str(source_commit).lower()
or str(deployment.get("source_commit")) .lower() != str(source_commit).lower()
or str(deployment.get("tree_oid", "")).lower() != str(source_readback.get("tree_oid", "")).lower()
or str(acceptance.get("candidate_id")) != candidate_id
or str(acceptance.get("status")) != "ACCEPTED"):
return {"phase": "BLOCKED", "mode": "AUTHORITY", "candidate_id": candidate_id, "error": "RC_READBACK_BINDING_MISMATCH"}
@@ -248,9 +287,12 @@ class GovernedUnattendedReleaseRunner:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": _error_code(exc)}
def deploy(self, candidate_id: str) -> dict[str, Any]:
if not self.deployment_enabled or self.deployment_service is None:
if self.environment_mode != "AUTHORITY" or not self.deployment_enabled or self.deployment_service is None:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "DEPLOYMENT_NOT_ENABLED"}
try:
return {"phase": "DEPLOYED", "mode": "AUTHORITY", "result": self.deployment_service.start_deployment(candidate_id)}
result = self.deployment_service.start_deployment(candidate_id)
if not isinstance(result, Mapping) or result.get("execution_enabled") is not True or result.get("side_effects_executed") is not True or str(result.get("status", "")).upper() not in {"COMPLETED", "COMPLETE"}:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": "DEPLOYMENT_NOT_VERIFIED", "result": dict(result) if isinstance(result, Mapping) else {}}
return {"phase": "DEPLOYED", "mode": "AUTHORITY", "result": dict(result)}
except Exception as exc:
return {"phase": "BLOCKED", "mode": "AUTHORITY", "error": _error_code(exc)}
+14 -10
View File
@@ -27,10 +27,10 @@ def intent(**changes):
source_commit=COMMIT,
source_tree_oid=TREE,
authority_base_oid=BASE,
target_branch="c5/unattended-runner-20260926",
target_branch="agent/c5-unattended-runner-20260926",
file_allowlist=("active-runtime/app/foo.py", "tests/test_foo.py"),
requested_capabilities=("release.candidate", "source.publish"),
risk="LOW",
risk="low",
execution_digest=DIGEST,
event_id="release-event-1",
version="v2r2c5",
@@ -56,7 +56,7 @@ class Admission:
self.authorize_calls += 1
if self.authorize_error:
raise self.authorize_error
return {"task_id": raw.get("task_id", "release-task-x"), "status": "QUEUED"}
return {"task_id": raw.get("task_id", "release-task-x"), "status": "QUEUED", "authority_base_oid": BASE, "target_branch": "agent/c5-unattended-runner-20260926", "execution_digest": DIGEST}
def status(self, envelope_id):
self.status_calls += 1
@@ -73,6 +73,7 @@ class Failure(RuntimeError):
def runner(admission, **kwargs):
return GovernedUnattendedReleaseRunner(
admission=admission,
environment_mode=kwargs.pop("environment_mode", "AUTHORITY"),
max_attempts=3,
deadline_seconds=10,
sleep=lambda _seconds: None,
@@ -116,16 +117,19 @@ def test_unknown_mutation_outcome_reconciles_before_resubmit():
def test_publication_requires_full_lease_chain_and_exact_rc_binding():
class Adapter:
def publish(self, _intent, _receipt):
return {"task_lease": "t", "task_principal": "p", "writer_lease": "w", "commit_receipt": "r", "source_oid": COMMIT, "tree_oid": TREE}
return {"task_lease": "t", "task_principal": "p", "writer_lease": "w", "commit_receipt": "r", "source_oid": "e" * 40, "tree_oid": TREE, "authenticated": True, "task_id": _receipt_task, "authority_base_oid": BASE, "target_branch": _receipt_branch, "execution_digest": DIGEST}
result = runner(Admission(), publication_adapter=Adapter()).publish(intent(), {"grant_id": "g"})
_receipt_task = intent().task_id
_receipt_branch = intent().target_branch
receipt = {"task_id": _receipt_task, "authority_base_oid": BASE, "target_branch": _receipt_branch, "execution_digest": DIGEST}
result = runner(Admission(), publication_adapter=Adapter()).publish(intent(), receipt)
assert result["phase"] == "PUBLISHED"
class Missing:
def publish(self, _intent, _receipt):
return {"source_oid": COMMIT, "tree_oid": TREE}
assert runner(Admission(), publication_adapter=Missing()).publish(intent(), {})["error"] == "PUBLICATION_LEASE_CHAIN_MISSING"
assert runner(Admission(), publication_adapter=Missing()).publish(intent(), receipt)["error"] == "PUBLICATION_LEASE_CHAIN_MISSING"
def test_rc_missing_approval_backend_is_blocked_and_preparation_cannot_deploy():
@@ -140,14 +144,14 @@ def test_rc_missing_approval_backend_is_blocked_and_preparation_cannot_deploy():
return {"candidate_id": candidate_id, "source_commit": COMMIT}
def get_release_plan(self, candidate_id):
return {"candidate_id": candidate_id, "source_commit": COMMIT}
return {"candidate_id": candidate_id, "deployment": {"source_commit": COMMIT, "tree_oid": TREE}}
def get_acceptance(self, candidate_id):
return {"candidate_id": candidate_id, "status": "ACCEPTED"}
service = Release()
r = runner(Admission(), release_service=service)
assert r.postpublication_rc(event_id="release-event-1", source_commit=COMMIT)["error"] == "OWNER_APPROVAL_BACKEND_MISSING"
assert r.postpublication_rc(event_id="release-event-1", source_commit=COMMIT, source_readback={"authenticated": True, "source_commit": COMMIT, "tree_oid": TREE})["error"] == "OWNER_APPROVAL_BACKEND_MISSING"
assert r.deploy("rc-1")["error"] == "DEPLOYMENT_NOT_ENABLED"
@@ -166,7 +170,7 @@ def test_rc_uses_injected_owner_broker_and_existing_verifier_path():
return {"candidate_id": candidate_id, "source_commit": COMMIT}
def get_release_plan(self, candidate_id):
return {"candidate_id": candidate_id, "source_commit": COMMIT}
return {"candidate_id": candidate_id, "deployment": {"source_commit": COMMIT, "tree_oid": TREE}}
def get_acceptance(self, candidate_id):
return {"candidate_id": candidate_id, "status": "ACCEPTED"}
@@ -176,6 +180,6 @@ def test_rc_uses_injected_owner_broker_and_existing_verifier_path():
return {"status": "VERIFIED_AND_STORED"}
service = Release()
result = runner(Admission(), release_service=service, owner_approval_broker=lambda envelope: {"candidate_id": envelope["candidate_id"], "signature": "signed-by-owner"}).postpublication_rc(event_id="release-event-1", source_commit=COMMIT)
result = runner(Admission(), release_service=service, owner_approval_broker=lambda envelope: {"candidate_id": envelope["candidate_id"], "signature": "signed-by-owner"}).postpublication_rc(event_id="release-event-1", source_commit=COMMIT, source_readback={"authenticated": True, "source_commit": COMMIT, "tree_oid": TREE})
assert result["phase"] == "APPROVED"
assert service.approval["signature"] == "signed-by-owner"