"""Response-wrapped operator handoff. Writes a single-use wrap token to a mode-0600 file outside Git. The wrap token is secret material and is never printed or recorded in evidence. """ from __future__ import annotations import os import re from dataclasses import dataclass from pathlib import Path from secrets_engine.catalog import CatalogEntry from secrets_engine.errors import ProvisioningError from secrets_engine.openbao import OpenBaoClient, WrappedResponse from secrets_engine.safe_paths import containing_git_worktree _TTL_RE = re.compile(r"^([1-9][0-9]*)([smh])$") MAX_WRAP_SECONDS = 15 * 60 @dataclass(frozen=True) class WrapHandoffResult: catalog_id: str kind: str out_file: str ttl: str wrap_handle: str creation_path: str def normalize_wrap_ttl(value: str) -> str: text = (value or "").strip() match = _TTL_RE.fullmatch(text) if not match: raise ProvisioningError("wrap ttl must be like 60s or 15m") amount = int(match.group(1)) unit = match.group(2) seconds = amount * {"s": 1, "m": 60, "h": 3600}[unit] if seconds > MAX_WRAP_SECONDS: raise ProvisioningError("wrap ttl must not exceed 15m") return text def _validate_output_path(path: Path) -> Path: resolved = path.expanduser().resolve() worktree = containing_git_worktree(resolved) if worktree is not None: raise ProvisioningError( f"wrap file {resolved} is inside a Git worktree ({worktree}); " "keep wrap tokens outside repos" ) if resolved.exists() and resolved.stat().st_mode & 0o077: raise ProvisioningError( f"wrap file {resolved} is group/other-accessible " f"(mode {oct(resolved.stat().st_mode & 0o777)}); must be 0600" ) return resolved def _write_mode_0600(path: Path, value: str) -> None: path.parent.mkdir(mode=0o700, parents=True, exist_ok=True) fd: int | None = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) try: os.fchmod(fd, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as fh: fd = None fh.write(value) fh.write("\n") finally: if fd is not None: os.close(fd) def write_wrapped_handoff( client: OpenBaoClient, entry: CatalogEntry, *, out_file: Path, ttl: str, ) -> WrapHandoffResult: """Mint a wrap token and write it without printing it.""" ttl = normalize_wrap_ttl(ttl) out_path = _validate_output_path(out_file) wrapped: WrappedResponse if entry.kind == "auth-capability": wrapped = client.wrap_approle_secret_id(entry.role_name, ttl=ttl) elif entry.stores_kv_value(): wrapped = client.wrap_kv_get(entry.mount, entry.path, ttl=ttl) else: raise ProvisioningError( f"lane '{entry.id}' kind {entry.kind} has no wrapped handoff" ) token = wrapped.wrap_token try: _write_mode_0600(out_path, token) finally: token = "" return WrapHandoffResult( catalog_id=entry.id, kind=entry.kind, out_file=str(out_path), ttl=ttl, wrap_handle=wrapped.accessor_fingerprint, creation_path=wrapped.creation_path, )