Bind credential exec to exact owner inputs and approval digest
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-10 09:29:38 +02:00
parent 9eb07fd8fc
commit 42b48aa54f
13 changed files with 626 additions and 7 deletions

View file

@ -27,6 +27,7 @@ from typing import Iterator
from secrets_engine.catalog import CatalogEntry
from secrets_engine.errors import DeliveryError
from secrets_engine.exec_owner import owner_binding, validate_delivery_target
from secrets_engine.openbao import OpenBaoClient
from secrets_engine.publication_policy import PublicationPolicy, resolve
from secrets_engine.redact import redact_text
@ -180,6 +181,7 @@ def exec_with_secret(
mode: str = "auto",
policy_dir=None,
session_evidence: dict[str, object] | None = None,
expected_owner_digest: str | None = None,
) -> int:
"""Run `command` with the lane's secret injected for the child only.
@ -192,6 +194,10 @@ def exec_with_secret(
f"field '{field}' not declared in lane '{entry.id}' fields {entry.fields}"
)
binding_digest = validate_delivery_target(entry, field, command, mode)
if expected_owner_digest is not None and binding_digest != expected_owner_digest:
raise DeliveryError("exec owner binding changed after action admission")
declared = set(entry.delivery_modes)
if mode == "auto":
if "npm-config" in declared:
@ -219,7 +225,12 @@ def exec_with_secret(
value = _fetch_value(
client, entry, field, session_evidence=session_evidence
)
child_env = dict(os.environ)
# Recheck after retrieval too: a config changed during auth/read cannot be
# launched with a value authorized for the previous recipient.
if validate_delivery_target(entry, field, command, mode) != binding_digest:
raise DeliveryError("exec owner binding changed during credential retrieval")
binding = owner_binding(entry)
child_env = dict(binding["environment"]) if binding is not None else dict(os.environ)
if mode == "npm-config":
npm = entry.npm
@ -241,6 +252,8 @@ def exec_with_secret(
# Inject under a conventional name derived from the field.
env_name = field.upper()
child_env[env_name] = value
if binding is not None:
return _spawn(command, child_env, value, cwd=binding["cwd"])
return _spawn(command, child_env, value)
if mode == "exec-file":
@ -252,7 +265,7 @@ def exec_with_secret(
raise DeliveryError(f"unsupported delivery mode '{mode}'")
def _spawn(command: list[str], env: dict[str, str], secret: str) -> int:
def _spawn(command: list[str], env: dict[str, str], secret: str, *, cwd: str | None = None) -> int:
"""Spawn the child, stream redacted output, propagate signals, ensure cleanup."""
try:
proc = subprocess.Popen(
@ -261,6 +274,8 @@ def _spawn(command: list[str], env: dict[str, str], secret: str) -> int:
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
cwd=cwd,
stdin=subprocess.DEVNULL if cwd is not None else None,
)
except FileNotFoundError as e:
raise DeliveryError(f"command not found: {command[0]}") from e