Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
120 lines
4.8 KiB
Python
120 lines
4.8 KiB
Python
"""Owner-configured credential provider envelope; no secret retrieval in Glas."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import selectors
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from contextlib import suppress
|
|
from pathlib import Path
|
|
|
|
|
|
def provider_argv(config: dict, refs: list[str], context: dict) -> list[str]:
|
|
if len(refs) != 1:
|
|
raise ValueError("credential execution requires exactly one configured route")
|
|
route = config.get("credential_routes", {}).get(refs[0])
|
|
if not isinstance(route, dict):
|
|
raise ValueError("credential route is not configured by the owner")
|
|
for field in ("profiles", "projects", "actors"):
|
|
choices = route.get(field)
|
|
if (
|
|
not isinstance(choices, list)
|
|
or not choices
|
|
or any(not isinstance(item, str) or not item for item in choices)
|
|
):
|
|
raise ValueError("credential route requires explicit consumer allowlists")
|
|
if (
|
|
context.get("profile_id") not in route.get("profiles", [])
|
|
or context.get("project") not in route.get("projects", [])
|
|
or context.get("actor") not in route.get("actors", [])
|
|
or not context.get("run_id")
|
|
):
|
|
raise ValueError("credential route consumer binding denied")
|
|
command = route.get("exec_argv")
|
|
if (
|
|
not isinstance(command, list)
|
|
or not command
|
|
or any(not isinstance(arg, str) or not arg or "\0" in arg for arg in command)
|
|
or not Path(command[0]).is_absolute()
|
|
or command[-1] != "--"
|
|
):
|
|
raise ValueError("credential provider requires an absolute executable and trailing --")
|
|
return command
|
|
|
|
|
|
def execute(config: dict, request: dict, socket_path: Path) -> dict:
|
|
prefix = provider_argv(config, request["credential_route_refs"], request["execution_context"])
|
|
argv = [
|
|
*prefix,
|
|
sys.executable,
|
|
str(Path(__file__).with_name("credential_exec.py")),
|
|
str(socket_path),
|
|
]
|
|
# Never let an interactive provider key stand in for owner acquisition.
|
|
env = os.environ.copy()
|
|
env.pop("ANTHROPIC_API_KEY", None)
|
|
env.pop("ANTHROPIC_AUTH_TOKEN", None)
|
|
proc = subprocess.Popen(
|
|
argv,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.DEVNULL,
|
|
env=env,
|
|
start_new_session=True,
|
|
)
|
|
assert proc.stdin is not None and proc.stdout is not None
|
|
budget = request["timeout_seconds"] + 30
|
|
deadline = time.monotonic() + budget
|
|
limit = request["max_output_bytes"] * 2 + 65536
|
|
output = bytearray()
|
|
pending = memoryview(json.dumps(request).encode())
|
|
try:
|
|
os.set_blocking(proc.stdin.fileno(), False)
|
|
os.set_blocking(proc.stdout.fileno(), False)
|
|
with selectors.DefaultSelector() as selector:
|
|
selector.register(proc.stdin, selectors.EVENT_WRITE)
|
|
selector.register(proc.stdout, selectors.EVENT_READ)
|
|
while selector.get_map():
|
|
remaining = deadline - time.monotonic()
|
|
if remaining <= 0:
|
|
raise RuntimeError("credential delivery deadline exceeded")
|
|
for key, _ in selector.select(min(remaining, 1)):
|
|
if key.fileobj is proc.stdin:
|
|
written = os.write(proc.stdin.fileno(), pending[:65536])
|
|
pending = pending[written:]
|
|
if not pending:
|
|
selector.unregister(proc.stdin)
|
|
proc.stdin.close()
|
|
else:
|
|
chunk = os.read(proc.stdout.fileno(), 65536)
|
|
if not chunk:
|
|
selector.unregister(proc.stdout)
|
|
output.extend(chunk)
|
|
if len(output) > limit:
|
|
raise RuntimeError("credential delivery output exceeded bound")
|
|
if proc.wait(timeout=max(0.1, deadline - time.monotonic())) != 0:
|
|
raise RuntimeError("credential provider refused or failed")
|
|
response = json.loads(output)
|
|
if not isinstance(response, dict) or "boundary_error" in response:
|
|
raise RuntimeError("credential broker refused or failed")
|
|
return response
|
|
except Exception:
|
|
# Provider stdout/stderr and parser exception excerpts must never escape.
|
|
raise RuntimeError("owner credential delivery failed") from None
|
|
finally:
|
|
if proc.poll() is None:
|
|
with suppress(ProcessLookupError):
|
|
os.killpg(proc.pid, signal.SIGTERM)
|
|
try:
|
|
proc.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
with suppress(ProcessLookupError):
|
|
os.killpg(proc.pid, signal.SIGKILL)
|
|
proc.wait()
|
|
proc.stdout.close()
|
|
if not proc.stdin.closed:
|
|
proc.stdin.close()
|