Bind credential exec to exact owner inputs and approval digest
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
parent
9eb07fd8fc
commit
42b48aa54f
13 changed files with 626 additions and 7 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue