"""Fail-closed access to the authoritative bare Git repository.""" from __future__ import annotations import os import re import subprocess import urllib.parse from pathlib import Path from typing import Iterable _ALLOWED_COMMANDS = frozenset( { "cat-file", "config", "diff", "for-each-ref", "ls-remote", "merge-base", "push", "remote", "rev-parse", "show-ref", "tag", } ) _OID_RE = re.compile(r"^[0-9a-f]{40}$") _MAX_OUTPUT = 1_048_576 _TIMEOUT = 8.0 class AuthoritativeGitError(RuntimeError): """A fail-closed authoritative repository or ref query error.""" class AuthoritativeGitNotFound(AuthoritativeGitError): """A validated revision does not exist in the authoritative repository.""" class AuthoritativeGitOutputTooLarge(AuthoritativeGitError): """A controlled Git query exceeded the response-size boundary.""" def _redact(text: str) -> str: value = str(text or "") value = re.sub(r"(?i)(authorization\s*[:=]\s*)(?:bearer|token)\s+\S+", r"\1REDACTED", value) value = re.sub(r"(?i)((?:access_)?token\s*[:=]\s*)\S+", r"\1REDACTED", value) value = re.sub(r"(?i)(https?://[^/\s:@]+:)[^@\s/]+@", r"\1REDACTED@", value) return value def _configured_path(git_dir: str | Path | None = None) -> Path: raw = str(git_dir or os.getenv("AIOS_GITOPS_AUTHORITATIVE_GIT_DIR", "")).strip() if not raw: raise AuthoritativeGitError("AUTHORITATIVE_REPOSITORY_UNAVAILABLE") path = Path(raw) if not path.is_absolute() or ".." in path.parts: raise AuthoritativeGitError("AUTHORITATIVE_REPOSITORY_UNAVAILABLE") try: resolved = path.resolve(strict=True) except OSError as exc: raise AuthoritativeGitError("AUTHORITATIVE_REPOSITORY_UNAVAILABLE") from exc if not resolved.is_dir(): raise AuthoritativeGitError("AUTHORITATIVE_REPOSITORY_UNAVAILABLE") return resolved def _run( git_dir: Path, args: list[str], *, check: bool, timeout: float = _TIMEOUT, ) -> subprocess.CompletedProcess[str]: if not args or args[0] not in _ALLOWED_COMMANDS: raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") try: result = subprocess.run( ["git", "--git-dir", str(git_dir), *args], text=True, capture_output=True, timeout=timeout, env={**os.environ, "GIT_OPTIONAL_LOCKS": "0"}, ) except (OSError, subprocess.TimeoutExpired) as exc: raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") from exc if len(result.stdout) + len(result.stderr) > _MAX_OUTPUT: raise AuthoritativeGitOutputTooLarge("GIT_OUTPUT_LIMIT_EXCEEDED") if check and result.returncode != 0: detail = _redact(result.stderr.strip() or result.stdout.strip()) raise AuthoritativeGitError( "GIT_REF_QUERY_FAILED" + (f": {detail[:160]}" if detail else "") ) return result def _repository_identity_from_remote(url: str) -> str: value = str(url or "").strip().split("?", 1)[0].rstrip("/") if not value: return "" if "://" in value: value = urllib.parse.urlparse(value).path elif ":" in value: value = value.rsplit(":", 1)[1] value = value.rsplit("@", 1)[-1].strip("/") value = re.sub(r"\.git$", "", value, flags=re.IGNORECASE) parts = [part for part in value.split("/") if part] if len(parts) >= 2: value = "/".join(parts[-2:]) return value.strip("/").lower() def verify_repository( git_dir: str | Path | None = None, repository_identity: str | None = None, ) -> tuple[Path, str]: path = _configured_path(git_dir) expected = str( repository_identity or os.getenv("AIOS_GITOPS_REPOSITORY_IDENTITY", "") ).strip().strip("/") if not expected or expected.count("/") != 1: raise AuthoritativeGitError("REPOSITORY_IDENTITY_MISMATCH") bare = _run(path, ["rev-parse", "--is-bare-repository"], check=True) if bare.stdout.strip().lower() != "true": raise AuthoritativeGitError("AUTHORITATIVE_REPOSITORY_UNAVAILABLE") remote = _run(path, ["config", "--get", "remote.gitea.url"], check=False) actual = _repository_identity_from_remote(remote.stdout) if actual != expected.lower(): raise AuthoritativeGitError("REPOSITORY_IDENTITY_MISMATCH") return path, expected def run_authoritative_git( args: list[str], *, check: bool = True, git_dir: str | Path | None = None, timeout: float = _TIMEOUT, ) -> subprocess.CompletedProcess[str]: path, _ = verify_repository(git_dir=git_dir) return _run(path, list(args), check=check, timeout=timeout) def authoritative_head_oid( *, git_dir: str | Path | None = None, ) -> str: result = run_authoritative_git(["rev-parse", "--verify", "refs/heads/main"], git_dir=git_dir) oid = result.stdout.strip().lower() if not _OID_RE.fullmatch(oid): raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") return oid def authoritative_tags( *, git_dir: str | Path | None = None, ) -> list[dict[str, str | None]]: result = run_authoritative_git( [ "for-each-ref", "refs/tags", "--format=%(refname:strip=2)\t%(objectname)\t%(objecttype)", ], git_dir=git_dir, ) tags: list[dict[str, str | None]] = [] for line in result.stdout.splitlines(): name, oid, object_type = (line.split("\t", 2) + ["", ""])[:3] if not name or not _OID_RE.fullmatch(oid.lower()): raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") peeled_result = run_authoritative_git( ["rev-parse", "--verify", f"refs/tags/{name}^{{commit}}"], git_dir=git_dir, ) peeled = peeled_result.stdout.strip().lower() if not _OID_RE.fullmatch(peeled): raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") tags.append( { "name": name, "tag_oid": oid.lower(), "object_type": object_type, "peeled_target_oid": peeled, } ) return tags def authoritative_branches( *, git_dir: str | Path | None = None, ) -> list[dict[str, str]]: result = run_authoritative_git( [ "for-each-ref", "refs/heads", "--format=%(refname:strip=2)\t%(objectname)", ], git_dir=git_dir, ) branches: list[dict[str, str]] = [] for line in result.stdout.splitlines(): name, oid = (line.split("\t", 1) + [""])[:2] if not name or not _OID_RE.fullmatch(oid.lower()): raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") branches.append({"name": name, "oid": oid.lower()}) return branches def authoritative_commit( oid: str, *, git_dir: str | Path | None = None, ) -> str: value = str(oid or "").strip().lower() if not _OID_RE.fullmatch(value): raise ValueError("commit OID must be a full 40-character value") result = run_authoritative_git( ["rev-parse", "--verify", f"{value}^{{commit}}"], git_dir=git_dir, ) resolved = result.stdout.strip().lower() if resolved != value: raise AuthoritativeGitError("GIT_REF_QUERY_FAILED") return resolved def authoritative_lineage( ancestor: str, descendant: str, *, git_dir: str | Path | None = None, ) -> None: authoritative_commit(ancestor, git_dir=git_dir) authoritative_commit(descendant, git_dir=git_dir) result = run_authoritative_git( ["merge-base", "--is-ancestor", ancestor, descendant], check=False, git_dir=git_dir, ) if result.returncode != 0: raise AuthoritativeGitError("evidence lineage mismatch") _SAFE_REF_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._/-]{0,239}$") def _safe_ref_name(value: str) -> bool: return bool( _SAFE_REF_RE.fullmatch(value) and ".." not in value and "//" not in value and "@{" not in value and not value.endswith(("/", ".", ".lock")) and not any(part in {"", ".", ".."} for part in value.split("/")) ) def validate_git_revision( value: str, field: str, *, git_dir: str | Path | None = None, ) -> str: revision = str(value or "").strip() if not revision or revision != str(value or "") or revision.startswith("-"): raise ValueError(f"invalid {field} revision") if _OID_RE.fullmatch(revision.lower()): try: return authoritative_commit(revision.lower(), git_dir=git_dir) except AuthoritativeGitError as exc: raise AuthoritativeGitNotFound(f"{field} revision not found") from exc if not _safe_ref_name(revision) or any(token in revision for token in ("^", "~", ":", "\\")): raise ValueError(f"invalid {field} revision") if revision.startswith("refs/"): if not revision.startswith(("refs/heads/", "refs/tags/")): raise ValueError(f"invalid {field} revision") candidates = [revision] else: candidates = [f"refs/heads/{revision}", f"refs/tags/{revision}"] matches: list[str] = [] for candidate in candidates: result = run_authoritative_git( ["rev-parse", "--verify", f"{candidate}^{{commit}}"], check=False, git_dir=git_dir, ) oid = result.stdout.strip().lower() if result.returncode == 0 and _OID_RE.fullmatch(oid): matches.append(oid) if not matches: raise AuthoritativeGitNotFound(f"{field} revision not found") if len(set(matches)) != 1 or len(matches) != 1: raise ValueError(f"ambiguous {field} revision") return matches[0] def authoritative_diff( from_revision: str, to_revision: str, *, git_dir: str | Path | None = None, ) -> dict[str, object]: path, _ = verify_repository(git_dir=git_dir) from_oid = validate_git_revision(from_revision, "from", git_dir=path) to_oid = validate_git_revision(to_revision, "to", git_dir=path) args = ["diff", "--no-ext-diff", "--no-textconv", from_oid, to_oid] result = run_authoritative_git(args, git_dir=path) return { "code": 0, "stdout": result.stdout, "stderr": "", "cmd": ["git", f"--git-dir={path}", *args], "from_oid": from_oid, "to_oid": to_oid, } def repository_snapshot( *, git_dir: str | Path | None = None, repository_identity: str | None = None, ) -> dict[str, object]: configured = bool( str(git_dir or os.getenv("AIOS_GITOPS_AUTHORITATIVE_GIT_DIR", "")).strip() ) try: path, identity = verify_repository( git_dir=git_dir, repository_identity=repository_identity, ) head = authoritative_head_oid(git_dir=path) return { "repository_available": True, "authoritative_git_dir_configured": configured, "authoritative_git_dir_readable": True, "authoritative_git_dir": str(path), "authoritative_head_oid": head, "repository_identity": identity, "query_error": None, } except (AuthoritativeGitError, ValueError) as exc: return { "repository_available": False, "authoritative_git_dir_configured": configured, "authoritative_git_dir_readable": False, "authoritative_git_dir": None, "authoritative_head_oid": None, "repository_identity": str( repository_identity or os.getenv("AIOS_GITOPS_REPOSITORY_IDENTITY", "") ).strip() or None, "query_error": _redact(str(exc)), }