Files
aios/active-runtime/app/gitea_transport_consistency_provider.py
T

350 lines
12 KiB
Python

from __future__ import annotations
"""Authenticated Gitea consistency provider using the real governed Git transport.
The provider proves Gitea consistency without inventing a REST API:
* local HEAD == source_oid,
* local source_oid^{tree} == tree_oid,
* origin identifies the governed repository,
* exact checked-out branch exists remotely, and
* git ls-remote reports exactly source_oid for that branch.
It then writes an append-only root-owned v2 provider receipt using only governed
policy for provider identity, operation, store root, store id and modes.
The result explicitly records ``transport=git-ls-remote`` and
``api_verified=false``. It must never be described as REST/API evidence.
"""
from dataclasses import dataclass
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import re
import stat
import subprocess
from typing import Callable, Mapping
from urllib.parse import urlsplit, urlunsplit
RECEIPT_SCHEMA = "aios.trusted-evidence-provider-receipt.v2"
RECEIPT_VERSION = 2
_OID_RE = re.compile(r"^[0-9a-f]{40}$")
_REF_RE = re.compile(r"^refs/heads/[A-Za-z0-9._/-]+$")
_REPO_RE = re.compile(r"^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$")
class GiteaProviderError(RuntimeError):
pass
@dataclass(frozen=True)
class GitCommandResult:
returncode: int
stdout: str = ""
stderr: str = ""
GitRunner = Callable[[tuple[str, ...], Path], GitCommandResult]
def canonical_json_bytes(payload: Mapping) -> bytes:
return json.dumps(
dict(payload),
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def canonical_sha256(payload: Mapping) -> str:
return hashlib.sha256(canonical_json_bytes(payload)).hexdigest()
def _runner(args: tuple[str, ...], cwd: Path) -> GitCommandResult:
proc = subprocess.run(
list(args),
cwd=str(cwd),
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=45,
check=False,
)
return GitCommandResult(proc.returncode, proc.stdout, proc.stderr)
def _git(run: GitRunner, cwd: Path, *args: str, error: str) -> str:
result = run(("git", *args), cwd)
if result.returncode != 0:
raise GiteaProviderError(f"{error}: {result.stderr.strip()[:300]}")
return result.stdout.strip()
def _oid(value: str, field: str) -> str:
value = str(value).strip().lower()
if not _OID_RE.fullmatch(value):
raise GiteaProviderError(f"{field.upper()}_INVALID")
return value
def _mode(value) -> int:
if isinstance(value, int):
return value
text = str(value).strip()
if text.isdigit():
return int(text, 8)
raise GiteaProviderError("PROVIDER_MODE_INVALID")
def _safe_ref(value: str) -> Path:
raw = str(value).strip()
p = Path(raw)
if not raw or p.is_absolute() or any(part in ("", ".", "..") for part in p.parts):
raise GiteaProviderError("PROVIDER_RECEIPT_REF_INVALID")
return p
def _safe_remote_url(url: str) -> str:
url = str(url).strip()
if "://" in url:
parts = urlsplit(url)
host = parts.hostname or ""
port = f":{parts.port}" if parts.port else ""
return urlunsplit((parts.scheme, host + port, parts.path, "", ""))
if ":" in url and not url.startswith(("/", "./", "../")):
left, right = url.split(":", 1)
return f"{left.rsplit('@', 1)[-1]}:{right}"
return url
def repository_identity_from_remote(url: str) -> str:
safe = _safe_remote_url(url)
if "://" in safe:
path = urlsplit(safe).path
elif ":" in safe and not safe.startswith("/"):
path = safe.split(":", 1)[1]
else:
path = safe
parts = [x for x in path.strip("/").split("/") if x]
if parts and parts[0] == "git":
parts = parts[1:]
if len(parts) < 2:
raise GiteaProviderError("GITEA_REPOSITORY_IDENTITY_UNAVAILABLE")
owner, repo = parts[-2], parts[-1]
if repo.endswith(".git"):
repo = repo[:-4]
identity = f"{owner}/{repo}".lower()
if not _REPO_RE.fullmatch(identity):
raise GiteaProviderError("GITEA_REPOSITORY_IDENTITY_INVALID")
return identity
def load_gitea_policy(policy_path: str | Path) -> dict:
raw = json.loads(Path(policy_path).read_text(encoding="utf-8"))
defaults = dict(raw.get("defaults") or {})
providers = raw.get("providers")
if not isinstance(providers, dict) or not isinstance(providers.get("gitea"), dict):
raise GiteaProviderError("GITEA_PROVIDER_POLICY_MISSING")
cfg = dict(providers["gitea"])
return {
"provider_identity": str(cfg["provider_identity"]),
"store_id": str(cfg["store_id"]),
"store_root": Path(str(cfg["store_root"])),
"provider_operation": str(cfg["allowed_operations"][0]),
"expected_owner_uid": int(cfg.get("expected_owner_uid", defaults.get("expected_owner_uid", 0))),
"directory_mode": min(
_mode(x) for x in cfg.get(
"allowed_directory_modes",
defaults.get("allowed_directory_modes", ["0700", "0750"]),
)
),
"receipt_mode": min(
_mode(x) for x in cfg.get(
"allowed_receipt_modes",
defaults.get("allowed_receipt_modes", ["0600", "0640"]),
)
),
"authentication_kind": str(
cfg.get(
"authentication_kind",
defaults.get("authentication_kind", "root-owned-store-v1"),
)
),
}
def collect_gitea_transport_consistency(
*,
repo_root: str | Path,
source_oid: str,
tree_oid: str,
expected_repository: str = "aios/aios",
remote: str = "origin",
git_runner: GitRunner | None = None,
) -> dict:
repo_root = Path(repo_root).resolve()
if not repo_root.is_dir():
raise GiteaProviderError("GITEA_CHECKOUT_UNAVAILABLE")
source_oid = _oid(source_oid, "source_oid")
tree_oid = _oid(tree_oid, "tree_oid")
expected_repository = str(expected_repository).strip().lower()
if not _REPO_RE.fullmatch(expected_repository):
raise GiteaProviderError("GITEA_EXPECTED_REPOSITORY_INVALID")
if not re.fullmatch(r"[A-Za-z0-9._/-]+", remote) or remote.startswith(("/", "-")):
raise GiteaProviderError("GITEA_REMOTE_NAME_INVALID")
run = git_runner or _runner
if _git(run, repo_root, "rev-parse", "--is-inside-work-tree",
error="GITEA_LOCAL_REPOSITORY_REQUIRED").lower() != "true":
raise GiteaProviderError("GITEA_LOCAL_REPOSITORY_REQUIRED")
actual_source = _oid(
_git(run, repo_root, "rev-parse", "HEAD^{commit}",
error="GITEA_LOCAL_SOURCE_UNAVAILABLE"),
"actual_source_oid",
)
if actual_source != source_oid:
raise GiteaProviderError("GITEA_LOCAL_SOURCE_OID_MISMATCH")
actual_tree = _oid(
_git(run, repo_root, "rev-parse", f"{source_oid}^{{tree}}",
error="GITEA_LOCAL_TREE_UNAVAILABLE"),
"actual_tree_oid",
)
if actual_tree != tree_oid:
raise GiteaProviderError("GITEA_LOCAL_TREE_OID_MISMATCH")
branch = _git(
run, repo_root, "symbolic-ref", "--quiet", "HEAD",
error="GITEA_GOVERNED_BRANCH_REQUIRED",
)
if not _REF_RE.fullmatch(branch):
raise GiteaProviderError("GITEA_GOVERNED_BRANCH_INVALID")
remote_url = _git(
run, repo_root, "remote", "get-url", remote,
error="GITEA_REMOTE_UNAVAILABLE",
)
identity = repository_identity_from_remote(remote_url)
if identity != expected_repository:
raise GiteaProviderError("GITEA_REPOSITORY_IDENTITY_MISMATCH")
output = _git(
run, repo_root, "ls-remote", "--exit-code", remote, branch,
error="GITEA_REMOTE_REF_UNAVAILABLE",
)
rows = []
for line in output.splitlines():
parts = line.split()
if len(parts) >= 2 and parts[1] == branch:
rows.append((parts[0].lower(), parts[1]))
if len(rows) != 1:
raise GiteaProviderError("GITEA_REMOTE_REF_AMBIGUOUS")
remote_oid = _oid(rows[0][0], "remote_ref_oid")
if remote_oid != source_oid:
raise GiteaProviderError("GITEA_REMOTE_REF_OID_MISMATCH")
return {
"consistent": True,
"repository": identity,
"source_commit": source_oid,
"remote_ref_oid": remote_oid,
"tree_oid": tree_oid,
"remote_ref": branch,
"remote_url": _safe_remote_url(remote_url),
"transport": "git-ls-remote",
"api_verified": False,
}
def _ensure_root_directory(path: Path, mode: int, expected_uid: int) -> None:
if os.geteuid() != expected_uid:
raise GiteaProviderError("GITEA_PROVIDER_ROOT_REQUIRED")
path.mkdir(parents=True, exist_ok=True, mode=mode)
st = os.lstat(path)
if stat.S_ISLNK(st.st_mode) or not stat.S_ISDIR(st.st_mode):
raise GiteaProviderError("GITEA_PROVIDER_STORE_INVALID")
if st.st_uid != expected_uid:
raise GiteaProviderError("GITEA_PROVIDER_STORE_OWNER_INVALID")
if stat.S_IMODE(st.st_mode) & 0o022:
raise GiteaProviderError("GITEA_PROVIDER_STORE_MODE_INVALID")
def _write_receipt_exclusive(path: Path, payload: Mapping, mode: int) -> str:
data = canonical_json_bytes(payload)
if path.exists() or path.is_symlink():
raise GiteaProviderError("GITEA_PROVIDER_RECEIPT_EXISTS")
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
flags |= getattr(os, "O_NOFOLLOW", 0)
flags |= getattr(os, "O_BINARY", 0)
fd = os.open(path, flags, mode)
try:
view = memoryview(data)
while view:
written = os.write(fd, view)
if written <= 0:
raise GiteaProviderError("GITEA_PROVIDER_RECEIPT_WRITE_FAILED")
view = view[written:]
os.fsync(fd)
finally:
os.close(fd)
os.chmod(path, mode)
return hashlib.sha256(data).hexdigest()
def produce_gitea_consistency_receipt(
*,
repo_root: str | Path,
source_oid: str,
tree_oid: str,
policy_path: str | Path,
receipt_ref: str,
expected_repository: str = "aios/aios",
) -> tuple[str, dict]:
cfg = load_gitea_policy(policy_path)
if cfg["authentication_kind"] != "root-owned-store-v1":
raise GiteaProviderError("GITEA_PROVIDER_AUTH_KIND_UNSUPPORTED")
result = collect_gitea_transport_consistency(
repo_root=repo_root,
source_oid=source_oid,
tree_oid=tree_oid,
expected_repository=expected_repository,
)
now = datetime.now(timezone.utc).isoformat()
run_id = f"gitea-git-transport-{source_oid[:12]}-{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}"
rel = _safe_ref(receipt_ref)
target = cfg["store_root"].joinpath(rel)
root = cfg["store_root"].resolve()
if not str(target.resolve(strict=False)).startswith(str(root) + os.sep):
raise GiteaProviderError("GITEA_PROVIDER_RECEIPT_REF_ESCAPE")
_ensure_root_directory(root, cfg["directory_mode"], cfg["expected_owner_uid"])
parent = root
for part in rel.parts[:-1]:
parent = parent / part
_ensure_root_directory(parent, cfg["directory_mode"], cfg["expected_owner_uid"])
payload = {
"schema": RECEIPT_SCHEMA,
"version": RECEIPT_VERSION,
"provider_identity": cfg["provider_identity"],
"provider_operation": cfg["provider_operation"],
"operation_run_id": run_id,
"result_reference": f"git:{result['remote_url']}#{result['remote_ref']}",
"content_sha256": canonical_sha256(result),
"source_oid": _oid(source_oid, "source_oid"),
"tree_oid": _oid(tree_oid, "tree_oid"),
"environment": "hw-isolated-linux-git-transport",
"issued_at": now,
"result": result,
"authentication": {
"kind": "root-owned-store-v1",
"store_id": cfg["store_id"],
},
}
sha = _write_receipt_exclusive(target, payload, cfg["receipt_mode"])
return sha, payload