Compare commits

..
18 changed files with 175 additions and 164 deletions
@@ -11,8 +11,6 @@ from dataclasses import dataclass
import os
from pathlib import Path
import re
from aios_target_branch_policy import validate_target_branch
import subprocess
import tempfile
from typing import Mapping, Sequence
@@ -97,8 +95,7 @@ class AuthorityGitExecutor:
return path
def advance_task_branch_CAS(self, branch: str, expected_oid: str, new_oid: str) -> str:
branch_path = branch[len("refs/heads/"):] if branch.startswith("refs/heads/") else ""
if not branch.startswith("refs/heads/") or not validate_target_branch(branch_path):
if not TASK_BRANCH_RE.fullmatch(branch):
raise AuthorityGitError("TASK_BRANCH_SCOPE_DENIED")
_oid(expected_oid, "expected_oid")
_oid(new_oid, "new_oid")
@@ -115,6 +112,27 @@ class AuthorityGitExecutor:
raise AuthorityGitError("AUTHORITY_CAS_READBACK_MISMATCH")
return readback
def create_task_branch_CAS(self, branch: str, new_oid: str) -> str:
"""Create an agent branch only if the authoritative ref is absent."""
if not TASK_BRANCH_RE.fullmatch(branch):
raise AuthorityGitError("TASK_BRANCH_SCOPE_DENIED")
_oid(new_oid, "new_oid")
current = self.read_ref(branch)
if current is not None:
if current == new_oid:
return new_oid
raise AuthorityGitError("AUTHORITY_BRANCH_ALREADY_EXISTS")
try:
self._git(["update-ref", branch, new_oid, "0" * 40])
except AuthorityGitError as exc:
if self.read_ref(branch) == new_oid:
return new_oid
raise AuthorityGitError("AUTHORITY_BRANCH_CREATE_CONFLICT") from exc
readback = self.read_ref(branch)
if readback != new_oid:
raise AuthorityGitError("AUTHORITY_CAS_READBACK_MISMATCH")
return readback
def ff_CAS_main_after_verified_protected_merge(self, expected_oid: str, new_oid: str) -> str:
_oid(expected_oid, "expected_oid")
_oid(new_oid, "new_oid")
@@ -9,8 +9,6 @@ import os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import re
from aios_target_branch_policy import validate_target_branch
import sqlite3
from typing import Any, Mapping
@@ -126,7 +124,7 @@ class GovernedRuntime:
workspace = DetachedGitWorkspace(worktree_path)
if workspace.head_oid() != lease.expected_branch_oid or not workspace.is_detached(): raise RuntimeError("WORKTREE_HEAD_MISMATCH")
job_root = self._job_root(lease.task_id); controller = GitOpsJobController(JobStore(job_root, payload_root=job_root / "payloads")); credential = ServerSideCredentialProjection(self.config.ssh_key_path, self.config.token_path); remote = GiteaContinuationRemote(workspace=worktree_path, credential=credential, remote_name=self.config.remote_name, repository=lease.repository, api_base=self.config.api_base); executor = ContinuationExecutor(controller, AuthorityGitAdapter(self.authority), workspace, remote, credential)
binding = ContinuationRuntimeBinding(request=lease, workspace_path=worktree_path, lease_not_before=datetime.now(timezone.utc) - timedelta(seconds=1), lease_expires_at=datetime.now(timezone.utc) + timedelta(seconds=lease.lease_seconds), original_receipt_valid=True, original_grant_consumed=True, permissions=ContinuationPermissions())
binding = ContinuationRuntimeBinding(request=lease, workspace_path=worktree_path, lease_not_before=datetime.now(timezone.utc) - timedelta(seconds=1), lease_expires_at=datetime.now(timezone.utc) + timedelta(seconds=lease.lease_seconds), original_receipt_valid=True, original_grant_consumed=True, permissions=ContinuationPermissions(create_target_branch=lease.branch_mode == "CREATE_NEW"))
result = (binding, controller, executor); self.runtimes[key] = result; self.controllers[lease.task_id] = controller; return result
def create_task(self, request: Mapping[str, Any]) -> dict[str, Any]:
@@ -150,7 +148,16 @@ class GovernedRuntime:
if request["target_branch"] != binding.request.target_branch or request["expected_branch_oid"] != binding.request.expected_branch_oid: raise RuntimeError("LEASE_BINDING_CONFLICT")
commit = ContinuationCommit(idempotency_key=str(request["idempotency_key"]), files=self._files(request), message=str(request["message"]))
executor.workspace.materialize_files(commit.files)
return executor.execute(binding, commit)
result = executor.execute(binding, commit)
receipt = result.get("receipt") if isinstance(result, Mapping) else None
receipt = receipt if isinstance(receipt, Mapping) else {}
commit_oid = receipt.get("authority_oid") or receipt.get("desired_remote_oid")
if not isinstance(commit_oid, str) or len(commit_oid) != 40 or any(ch not in "0123456789abcdef" for ch in commit_oid):
raise RuntimeError("COMMIT_RECEIPT_OID_MISSING")
response = dict(result)
response["commit_oid"] = commit_oid
response["commit_visible"] = bool(receipt.get("commit_visible") or receipt.get("remote_verified"))
return response
def _controller_for_job(self, job_id: str) -> GitOpsJobController:
for controller in self.controllers.values():
@@ -212,7 +219,8 @@ class GovernedRuntime:
if (
not task_id
or repository != self.config.repository
or not validate_target_branch(branch)
or not branch.startswith("agent/")
or branch in {"main", "master"}
or branch_mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}
or generation < 1
or not self._valid_oid(base)
@@ -288,7 +296,7 @@ class GovernedRuntime:
for value in self.bindings.values():
task_id = str(value.get("task_id") or "").strip()
branch = str(value.get("target_branch") or "").strip()
if not task_id or not validate_target_branch(branch):
if not task_id or not branch.startswith("agent/"):
continue
authority = self.authority.read_ref("refs/heads/" + branch)
try:
@@ -52,11 +52,12 @@ class AuthorityPort(Protocol):
def branch_head(self, branch: str) -> str: ...
def is_ancestor(self, older: str, newer: str) -> bool: ...
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str: ...
def create_task_branch_CAS(self, branch: str, new: str) -> str: ...
class RemotePort(Protocol):
def repository_identity(self) -> str: ...
def push_exact(self, branch: str, expected: str, new: str) -> None: ...
def push_exact(self, branch: str, expected: str | None, new: str) -> None: ...
def read_git_ref(self, branch: str) -> str | None: ...
def read_api_branch(self, branch: str) -> str | None: ...
def commit_visible(self, oid: str) -> bool: ...
@@ -106,6 +107,9 @@ class AuthorityGitAdapter:
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str:
return self.executor.advance_task_branch_CAS(self._ref(branch), expected, new)
def create_task_branch_CAS(self, branch: str, new: str) -> str:
return self.executor.create_task_branch_CAS(self._ref(branch), new)
@dataclass(slots=True)
class DetachedGitWorkspace:
@@ -258,10 +262,16 @@ class GiteaContinuationRemote:
raise ContinuationExecutionError("GITEA_GIT_READBACK_INVALID")
return oid
def push_exact(self, branch: str, expected: str, new: str) -> None:
def push_exact(self, branch: str, expected: str | None, new: str) -> None:
self._require_projected()
self.repository_identity()
if self.read_git_ref(branch) != expected:
observed = self.read_git_ref(branch)
if observed == new:
return
if expected is None:
if observed is not None:
raise ContinuationExecutionError("GITEA_BRANCH_EXPECTATION_MISMATCH")
elif observed != expected:
raise ContinuationExecutionError("GITEA_BRANCH_EXPECTATION_MISMATCH")
_run(["git", "push", self.remote_name, f"{new}:refs/heads/{branch}"], cwd=self.workspace)
@@ -321,8 +331,18 @@ class ContinuationPermissions:
production_write: bool = False
main_write: bool = False
def validate(self) -> None:
if any(asdict(self).values()):
def validate(self, *, branch_mode: str) -> None:
if branch_mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}:
raise ContinuationExecutionError("BRANCH_MODE_INVALID")
expected_create = branch_mode == "CREATE_NEW"
forbidden = (
self.force_push,
self.tag_write,
self.baseline_write,
self.production_write,
self.main_write,
)
if self.create_target_branch != expected_create or any(forbidden):
raise ContinuationExecutionError("MUTATION_PERMISSION_DENIED")
@@ -415,12 +435,14 @@ class ContinuationExecutor:
def _validate_preflight(self, binding: ContinuationRuntimeBinding) -> None:
request = binding.request
normalized = validate_writer_lease_request_v2(self._request_mapping(request), self.authority)
if normalized != {**self._request_mapping(request), "create_target_branch": False, "worktree_mode": "detached_at_expected_head", "cas_ref_update_required": True}:
raise ContinuationExecutionError("CONTINUATION_NORMALIZATION_INVALID")
# WriterLeaseRequestV2 validation is the single normalization authority.
# CREATE_NEW legitimately normalizes to create_target_branch=True and
# detached_at_base_oid; pinning the CONTINUE_EXISTING shape here rejects
# valid first commits before the governed CAS ref update can run.
validate_writer_lease_request_v2(self._request_mapping(request), self.authority)
if not binding.original_receipt_valid or not binding.original_grant_consumed:
raise ContinuationExecutionError("ORIGINAL_GRANT_LINEAGE_INVALID")
binding.permissions.validate()
binding.permissions.validate(branch_mode=request.branch_mode)
now = self.now()
if now < binding.lease_not_before:
raise ContinuationExecutionError("LEASE_NOT_ACTIVE")
@@ -442,7 +464,7 @@ class ContinuationExecutor:
"campaign_id": request.task_id,
"task_id": request.task_id,
"repository": request.repository,
"operation": "CONTINUATION_COMMIT_FILES",
"operation": "CONTINUATION_COMMIT_FILES_CREATE_NEW" if request.branch_mode == "CREATE_NEW" else "CONTINUATION_COMMIT_FILES",
"branch": request.target_branch,
"expected_branch_oid": request.expected_branch_oid,
"base_oid": request.base_oid,
@@ -488,7 +510,10 @@ class ContinuationExecutor:
new_oid = self.workspace.commit_files(commit.files, commit.message)
if not OID_RE.fullmatch(new_oid) or not self.authority.is_ancestor(request.expected_branch_oid, new_oid):
raise ContinuationExecutionError("NON_FAST_FORWARD_COMMIT")
authority_oid = self.authority.compare_and_swap_branch(request.target_branch, request.expected_branch_oid, new_oid)
if request.branch_mode == "CREATE_NEW":
authority_oid = self.authority.create_task_branch_CAS(request.target_branch, new_oid)
else:
authority_oid = self.authority.compare_and_swap_branch(request.target_branch, request.expected_branch_oid, new_oid)
if authority_oid != new_oid:
raise ContinuationExecutionError("AUTHORITY_CAS_READBACK_MISMATCH")
self.controller.advance(
@@ -499,7 +524,7 @@ class ContinuationExecutor:
)
try:
self.controller.advance(submitted.job_id, JobState.REMOTE_SYNCING, side_effect_state="IN_PROGRESS")
self.remote.push_exact(request.target_branch, request.expected_branch_oid, new_oid)
self.remote.push_exact(request.target_branch, None if request.branch_mode == "CREATE_NEW" else request.expected_branch_oid, new_oid)
except Exception:
return self._remote_pending(submitted.job_id, authority_oid, new_oid)
return self._finish_remote_verification(submitted.job_id, authority_oid, new_oid)
@@ -575,7 +600,7 @@ class ContinuationExecutor:
if authority_oid == git_oid == api_oid == new_oid and visible:
self.controller.advance(job_id, JobState.RECONCILING, side_effect_state="OBSERVATION_FIRST")
return self._finish_remote_verification(job_id, new_oid, new_oid)
expected_oid = request["expected_branch_oid"]
expected_oid = None if request.get("branch_mode") == "CREATE_NEW" else request["expected_branch_oid"]
if (
self.authority.is_ancestor(new_oid, authority_oid)
and git_oid is not None
@@ -10,8 +10,6 @@ from __future__ import annotations
from dataclasses import dataclass, replace
from datetime import datetime, timedelta, timezone
import re
from aios_target_branch_policy import validate_target_branch
from typing import Protocol
@@ -62,7 +60,7 @@ class WriterLeaseRequestV2:
raise ContinuationError("branch_mode_invalid")
_oid(self.base_oid, "base_oid")
_oid(self.expected_branch_oid, "expected_branch_oid")
if not validate_target_branch(self.target_branch):
if self.target_branch in {"main", "master"} or self.target_branch.startswith(("refs/", "tags/")):
raise ContinuationError("protected_branch_rejected")
if not isinstance(self.generation, int) or isinstance(self.generation, bool) or self.generation < 1:
raise ContinuationError("generation_invalid")
+1 -3
View File
@@ -8,8 +8,6 @@ from pathlib import Path
import secrets
from typing import Any, Callable, Mapping
from aios_target_branch_policy import validate_target_branch
@dataclass(frozen=True, slots=True)
class CredentialLease:
@@ -46,7 +44,7 @@ class CredentialBroker:
self._leases: dict[str, CredentialLease] = {}
def issue(self, *, task_id: str, repository: str, target_branch: str, generation: int, scopes: tuple[str, ...] = ("git:push", "gitea:read"), lease_seconds: int = 900) -> dict[str, Any]:
if repository != "aios/aios" or not validate_target_branch(target_branch) or generation < 1 or not 1 <= lease_seconds <= 86400:
if repository != "aios/aios" or not target_branch.startswith("agent/") or generation < 1 or not 1 <= lease_seconds <= 86400:
raise ValueError("CREDENTIAL_LEASE_BINDING_INVALID")
now = self.now().astimezone(timezone.utc)
lease = CredentialLease("lease-" + secrets.token_hex(16), task_id, repository, target_branch, generation, now.isoformat(), (now + timedelta(seconds=lease_seconds)).isoformat(), tuple(scopes), type(self.provider).__name__)
+1 -2
View File
@@ -11,7 +11,6 @@ from urllib.parse import quote
from urllib.request import Request, urlopen
from aios_gitops_reconciliation_core import RemoteObservation
from aios_target_branch_policy import validate_target_branch
@dataclass(frozen=True, slots=True)
@@ -36,7 +35,7 @@ class GiteaPublisher:
@staticmethod
def validate_branch(branch: str) -> None:
if not validate_target_branch(branch):
if not isinstance(branch, str) or not branch.startswith("agent/") or branch in {"main", "master"}:
raise ValueError("BRANCH_SCOPE_DENIED")
def repository_identity(self) -> str:
@@ -18,8 +18,6 @@ import tempfile
from datetime import datetime, timezone
from typing import Any, Callable, Mapping
from aios_target_branch_policy import validate_target_branch
class JobError(RuntimeError):
pass
@@ -276,7 +274,7 @@ class GitOpsJobController:
raise JobError("EXPECTED_BRANCH_OID_INVALID")
if len(digest) != 64 or any(ch not in "0123456789abcdef" for ch in digest):
raise JobError("PAYLOAD_DIGEST_INVALID")
if not validate_target_branch(request["branch"]):
if request["branch"] in {"main", "master"} or request["branch"].startswith(("refs/", "tags/")):
raise JobError("PROTECTED_BRANCH_REJECTED")
binding = {field: str(request[field]) for field in fields}
# Operation-specific, non-secret binding inputs must survive the
@@ -385,7 +383,7 @@ class GitOpsJobController:
raise JobError("JOB_NOT_FOUND")
if int(row["revision"]) != expected_revision:
raise JobError("REVISION_CAS_CONFLICT")
if not validate_target_branch(row["branch"]):
if row["branch"] in {"main", "master"}:
raise JobError("PROTECTED_BRANCH_REJECTED")
now = utc_now()
connection.execute("INSERT INTO branch_leases(lease_id,job_id,branch,lineage_anchor_oid,current_head_oid,generation,lease_expires_at,state,revision) VALUES(?,?,?,?,?,?,?,?,?)", (lease_id, job_id, row["branch"], lineage_anchor_oid, current_head_oid, generation, lease_expires_at, "ACTIVE", 0))
@@ -4,8 +4,6 @@ from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
import re
from aios_target_branch_policy import validate_target_branch
from typing import Optional
OID_RE = re.compile(r"^[0-9a-f]{40}$")
@@ -54,7 +52,7 @@ def decide_reconciliation(observation: RemoteObservation) -> ReconciliationDecis
"""Decide from a fresh observation; never complete on a missing remote."""
if observation.repository_identity != "aios/aios":
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "REPOSITORY_IDENTITY_MISMATCH")
if not validate_target_branch(observation.branch):
if not observation.branch.startswith("agent/"):
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "BRANCH_SCOPE_DENIED")
if not _valid_oid(observation.desired_oid) or not _valid_oid(observation.expected_oid):
return ReconciliationDecision(ReconciliationAction.FAIL_CONFLICT, "CONFLICT", "OID_INVALID")
+1 -3
View File
@@ -3,8 +3,6 @@ from __future__ import annotations
from dataclasses import dataclass
import re
from aios_target_branch_policy import validate_target_branch
from typing import Any, Mapping
OID_RE = re.compile(r"^[0-9a-f]{40}$")
@@ -41,7 +39,7 @@ def evaluate_policy(value: GovernedPolicyInput | Mapping[str, Any]) -> PolicyDec
return PolicyDecision("DENY", "REPOSITORY_IDENTITY_MISMATCH")
if not OID_RE.fullmatch(value.base_oid) or not OID_RE.fullmatch(value.expected_branch_oid):
return PolicyDecision("DENY", "OID_INVALID")
if not validate_target_branch(value.target_branch):
if not value.target_branch.startswith("agent/") or value.target_branch in {"main", "master"}:
return PolicyDecision("DENY", "BRANCH_SCOPE_DENIED")
if "force_push" in value.requested_capabilities or "force" in value.operation.lower():
return PolicyDecision("DENY", "FORCE_PUSH_DENIED")
@@ -11,7 +11,7 @@ from typing import Any, Mapping
from aios_gitops_job_controller import GitOpsJobController, JobStore
OID_RE = re.compile(r"^[0-9a-f]{40}$")
from aios_target_branch_policy import BRANCH_RE, validate_target_branch
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
class BootstrapError(RuntimeError):
@@ -61,7 +61,7 @@ class GovernedTaskBootstrap:
def create(self, request: Mapping[str, Any]) -> dict[str, Any]:
values = {key: str(request[key]) for key in ("task_id", "repository", "base_oid", "target_branch", "risk", "description", "idempotency_key")}
if values["repository"] != "aios/aios" or not OID_RE.fullmatch(values["base_oid"]): raise BootstrapError("TASK_BINDING_INVALID")
if not validate_target_branch(values["target_branch"]): raise BootstrapError("TARGET_BRANCH_INVALID")
if not BRANCH_RE.fullmatch(values["target_branch"]): raise BootstrapError("TARGET_BRANCH_INVALID")
existing = self._read_existing(values["task_id"])
if existing is not None:
if any(getattr(existing, key) != values[key] for key in ("repository", "base_oid", "target_branch", "risk", "description")): raise BootstrapError("TASK_BINDING_CONFLICT")
@@ -1,56 +0,0 @@
"""Single-source validation for governed implementation branch names.
The policy accepts ordinary implementation branches while rejecting protected
refs, malformed Git ref names, traversal-like components, and characters that
Git treats specially. Callers translate a false result into their own stable
error contract.
"""
from __future__ import annotations
import re
from typing import Any
BRANCH_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
PROTECTED_BRANCHES = frozenset({"HEAD", "main", "master"})
PROTECTED_PREFIXES = (
"main/",
"master/",
"refs/",
"tags/",
"tag/",
"release/",
"releases/",
"baseline/",
"production/",
)
def validate_target_branch(value: Any) -> bool:
"""Return whether *value* is a safe, non-protected implementation branch."""
if not isinstance(value, str) or not 3 <= len(value) <= 180:
return False
if not BRANCH_RE.fullmatch(value):
return False
if value in PROTECTED_BRANCHES or value == "@":
return False
if value.startswith(PROTECTED_PREFIXES):
return False
if value.startswith("/") or value.endswith("/"):
return False
if value.endswith(".") or value.endswith(".lock"):
return False
if ".." in value or "//" in value or "@{" in value:
return False
if any(ord(char) < 0x20 or ord(char) == 0x7F or char in " ~^:?*[\\" for char in value):
return False
components = value.split("/")
if any(
not component
or component in {".", "..", "@"}
or component.startswith(".")
or component.endswith(".lock")
for component in components
):
return False
return True
@@ -33,7 +33,7 @@ from signer_contract_compatibility import (
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
TASK_RE = re.compile(r"^[A-Z0-9][A-Z0-9._:-]{7,127}$")
from aios_target_branch_policy import BRANCH_RE, validate_target_branch
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
OWNER_REF_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/@+-]{3,255}$")
REQUIRED_REQUEST_FIELDS = frozenset(
@@ -353,7 +353,11 @@ def _validate_request(
raise ProvisioningError("BASE_REF_REJECTED", 409)
if not isinstance(raw["base_oid"], str) or not OID_RE.fullmatch(raw["base_oid"]):
raise ProvisioningError("BASE_OID_REJECTED")
if not validate_target_branch(raw["target_branch"]):
if (
not isinstance(raw["target_branch"], str)
or not BRANCH_RE.fullmatch(raw["target_branch"])
or raw["target_branch"] in {"main", "master"}
):
raise ProvisioningError("TARGET_BRANCH_REJECTED")
if not isinstance(raw["task_id"], str) or not TASK_RE.fullmatch(raw["task_id"]):
raise ProvisioningError("TASK_ID_REJECTED")
@@ -23,7 +23,6 @@ from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Mapping, Protocol
from aios_target_branch_policy import validate_target_branch
from authorized_task_writer_provisioning import (
AuthorizedTaskWriterProvisioner,
BRANCH_RE,
@@ -336,7 +335,11 @@ def _validate_request(
or not OID_RE.fullmatch(raw["base_oid"])
):
raise OwnerAuthorizationError("BASE_OID_REJECTED")
if not validate_target_branch(raw["target_branch"]):
if (
not isinstance(raw["target_branch"], str)
or not BRANCH_RE.fullmatch(raw["target_branch"])
or raw["target_branch"] in {"main", "master"}
):
raise OwnerAuthorizationError("TARGET_BRANCH_REJECTED")
if (
not isinstance(raw["task_id"], str)
@@ -49,7 +49,7 @@ OID_RE = re.compile(r"^[0-9a-f]{40}$")
IDENTITY_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._@:+/-]{1,254}$")
SCOPE_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{1,127}$")
TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{1,255}$")
from aios_target_branch_policy import validate_target_branch
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9._/-]{1,220}$")
MAX_FRAME = 64 * 1024
CHALLENGE_TTL_SECONDS = 120
@@ -207,7 +207,10 @@ def validate_binding(value: Mapping[str, Any]) -> dict[str, Any]:
raise BootstrapError("BASE_REF_REJECTED")
if not isinstance(request["base_oid"], str) or not OID_RE.fullmatch(request["base_oid"]):
raise BootstrapError("BASE_OID_REJECTED")
if not validate_target_branch(request["target_branch"]):
if (
not isinstance(request["target_branch"], str)
or not BRANCH_RE.fullmatch(request["target_branch"])
):
raise BootstrapError("TARGET_BRANCH_REJECTED")
if (
not isinstance(request["task_id"], str)
+2 -3
View File
@@ -17,8 +17,6 @@ import hashlib
import json
import os
import re
from aios_target_branch_policy import validate_target_branch
import secrets
import tempfile
from dataclasses import dataclass
@@ -194,7 +192,8 @@ def _validate_config(config: IdentityOverlayConfig) -> None:
if not OID_RE.fullmatch(binding.base_oid):
raise IdentityOverlayError("OVERLAY_BASE_OID_INVALID", 503)
if (
not validate_target_branch(binding.target_branch)
not BRANCH_RE.fullmatch(binding.target_branch)
or binding.target_branch in {"main", "master"}
):
raise IdentityOverlayError("OVERLAY_TARGET_BRANCH_INVALID", 503)
if not OWNER_IDENTITY_RE.fullmatch(config.owner_identity):
@@ -0,0 +1,73 @@
from __future__ import annotations
import inspect
from aios_authority_git_executor import AuthorityGitExecutor
from aios_continuation_executor import ContinuationExecutor, ContinuationPermissions
from aios_continuation_lease import WriterLeaseRequestV2
from authorized_task_writer_provisioning import validate_writer_lease_request_v2
BASE = "a" * 40
BRANCH = "agent/bridge-create-new-normalization-test"
class LocalAuthority:
def __init__(self, branch_head: str | None) -> None:
self._branch_head = branch_head
def branch_exists(self, branch: str) -> bool:
return self._branch_head is not None
def branch_head(self, branch: str) -> str:
assert self._branch_head is not None
return self._branch_head
def commit_exists(self, oid: str) -> bool:
return oid == BASE
def is_ancestor(self, older: str, newer: str) -> bool:
return older == BASE and newer == BASE
def request(branch_mode: str) -> dict[str, object]:
return {
"schema": WriterLeaseRequestV2.schema,
"task_id": "bridge-create-new-normalization-test",
"repository": "aios/aios",
"branch_mode": branch_mode,
"base_oid": BASE,
"expected_branch_oid": BASE,
"target_branch": BRANCH,
"generation": 1,
"lease_seconds": 120,
}
def main() -> None:
create_new = validate_writer_lease_request_v2(request("CREATE_NEW"), LocalAuthority(None))
assert create_new["create_target_branch"] is True
assert create_new["worktree_mode"] == "detached_at_base_oid"
assert create_new["cas_ref_update_required"] is True
continue_existing = validate_writer_lease_request_v2(request("CONTINUE_EXISTING"), LocalAuthority(BASE))
assert continue_existing["create_target_branch"] is False
assert continue_existing["worktree_mode"] == "detached_at_expected_head"
assert continue_existing["cas_ref_update_required"] is True
preflight = inspect.getsource(ContinuationExecutor._validate_preflight)
assert "validate_writer_lease_request_v2" in preflight
assert '"create_target_branch": False' not in preflight
assert '"worktree_mode": "detached_at_expected_head"' not in preflight
ContinuationPermissions(create_target_branch=True).validate(branch_mode="CREATE_NEW")
ContinuationPermissions().validate(branch_mode="CONTINUE_EXISTING")
cas = inspect.getsource(AuthorityGitExecutor.create_task_branch_CAS)
assert "update-ref" in cas
assert '"0" * 40' in cas
print("CREATE_NEW_NORMALIZATION_REGRESSION_PASS")
if __name__ == "__main__":
main()
+1 -1
View File
@@ -53,7 +53,7 @@ def test_exact_binding_passes():
[
(("owner_identity",), "owner", None),
(("authenticated_scope",), "*", "OWNER_SCOPE_INVALID"),
(("request", "target_branch"), "release/not-allowed", "TARGET_BRANCH_REJECTED"),
(("request", "target_branch"), "patch/not-allowed", "TARGET_BRANCH_REJECTED"),
(("request", "production_change_authorized"), True, "CONSEQUENTIAL_CAPABILITY_REJECTED"),
(("request", "tag_change_authorized"), True, "CONSEQUENTIAL_CAPABILITY_REJECTED"),
(("request", "baseline_change_authorized"), True, "CONSEQUENTIAL_CAPABILITY_REJECTED"),
-55
View File
@@ -1,55 +0,0 @@
from __future__ import annotations
from aios_target_branch_policy import validate_target_branch
def test_accepts_ordinary_implementation_branches() -> None:
for branch in (
"agent/worker-change-20260815",
"patch/not-allowed-anymore",
"fix/target-branch-policy",
"feature/ordinary-implementation",
"chore/code_stats-v2",
):
assert validate_target_branch(branch), branch
def test_rejects_protected_namespaces() -> None:
for branch in (
"main",
"master",
"main/hotfix",
"master/hotfix",
"refs/heads/patch/change",
"tags/release-v1",
"tag/release-v1",
"release/v1",
"releases/v1",
"baseline/current",
"production/hotfix",
):
assert not validate_target_branch(branch), branch
def test_rejects_malformed_git_refs_and_traversal() -> None:
for branch in (
"../escape",
"patch/../escape",
"patch//escape",
"patch/.",
"patch/.hidden",
"patch/name.lock",
"patch/name.",
"patch/name~bad",
"patch/name^bad",
"patch/name:bad",
"patch/name?bad",
"patch/name*bad",
"patch/name[bad",
"patch/name\\\\bad",
"patch/name@{bad}",
"patch/name with-space",
"patch/line\\nbreak",
"@",
):
assert not validate_target_branch(branch), repr(branch)