Fix attended login result auditing and reconcile blocked workplans
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e6ef-4273-7fc2-8741-dc96b3e5fe0d
This commit is contained in:
parent
940e164c9b
commit
4c3a0f4d3a
11 changed files with 260 additions and 32 deletions
|
|
@ -1212,6 +1212,7 @@ def _access_proxy(
|
|||
goes to stderr so --fetch stdout carries only the secret.
|
||||
"""
|
||||
from warden.proxy import (
|
||||
AttendedLoginError,
|
||||
ProxyError,
|
||||
build_wrapped_fetch,
|
||||
caller_auth_present,
|
||||
|
|
@ -1350,6 +1351,8 @@ def _access_proxy(
|
|||
f"[dim]proxy {action}: {entry.id} → {entry.owner_repo} "
|
||||
f"(caller identity; value not persisted)[/dim]"
|
||||
)
|
||||
rc = 10 if is_login else 5
|
||||
outcome = "login_failed" if is_login else "error"
|
||||
try:
|
||||
if is_login:
|
||||
rc = proxy_attended_login_exec(resolved, child_argv=child_argv)
|
||||
|
|
@ -1381,9 +1384,12 @@ def _access_proxy(
|
|||
rc = 0
|
||||
else:
|
||||
rc = proxy_fetch(resolved)
|
||||
outcome = "ok" if rc == 0 else "error"
|
||||
except ProxyError as e:
|
||||
if isinstance(e, AttendedLoginError):
|
||||
rc, outcome = e.exit_code, e.outcome
|
||||
err.print(f"[red]{e}[/red]")
|
||||
raise typer.Exit(5)
|
||||
raise typer.Exit(rc)
|
||||
finally:
|
||||
try:
|
||||
write_audit(
|
||||
|
|
@ -1393,6 +1399,8 @@ def _access_proxy(
|
|||
domain=domain,
|
||||
action=action,
|
||||
decision_id=decision_id,
|
||||
exit_code=rc,
|
||||
outcome=outcome,
|
||||
)
|
||||
except OSError as e:
|
||||
err.print(f"[yellow]audit write failed:[/yellow] {e}")
|
||||
|
|
@ -1564,6 +1572,10 @@ def access(
|
|||
f" {label:<8} : [dim]{proxy} --exec -- <reviewed-command>[/dim] "
|
||||
"[yellow](contained login + command; output suppressed)[/yellow]"
|
||||
)
|
||||
console.print(
|
||||
" contract : reviewed command must be silent; any stdout/stderr "
|
||||
"fails closed. Redirect both streams inside the reviewed command."
|
||||
)
|
||||
else:
|
||||
console.print(
|
||||
f" {label:<8} : [dim]{proxy} --fetch[/dim] "
|
||||
|
|
|
|||
|
|
@ -61,6 +61,15 @@ class ProxyError(Exception):
|
|||
"""Raised when a proxy fetch cannot be performed safely."""
|
||||
|
||||
|
||||
class AttendedLoginError(ProxyError):
|
||||
"""Value-free phase result for the attended command envelope."""
|
||||
|
||||
def __init__(self, message: str, *, exit_code: int, outcome: str):
|
||||
super().__init__(message)
|
||||
self.exit_code = exit_code
|
||||
self.outcome = outcome
|
||||
|
||||
|
||||
def _has_shell_pipe(cmd: str) -> bool:
|
||||
"""True when ``cmd`` contains an unquoted shell pipe operator."""
|
||||
in_single = in_double = False
|
||||
|
|
@ -144,6 +153,7 @@ def write_audit(
|
|||
action: str,
|
||||
decision_id: Optional[str],
|
||||
exit_code: Optional[int] = None,
|
||||
outcome: Optional[str] = None,
|
||||
) -> Path:
|
||||
"""Append a metadata-only audit record. Never contains a secret value (G2)."""
|
||||
state_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
|
@ -157,6 +167,7 @@ def write_audit(
|
|||
"subject": os.environ.get("WARDEN_POLICY_SUBJECT", "").strip() or "operator",
|
||||
"policy_decision_id": decision_id,
|
||||
"exit_code": exit_code,
|
||||
"outcome": outcome or ("ok" if exit_code in (None, 0) else "error"),
|
||||
}
|
||||
record.update(recording_time())
|
||||
with log_path.open("a") as f:
|
||||
|
|
@ -171,10 +182,11 @@ def write_audit(
|
|||
subject=record["subject"],
|
||||
target=need_id,
|
||||
decision_id=decision_id,
|
||||
outcome="ok" if exit_code in (None, 0) else "error",
|
||||
outcome=record["outcome"],
|
||||
source="access",
|
||||
owner_repo=owner_repo,
|
||||
domain=domain,
|
||||
exit_code=exit_code,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
|
@ -443,9 +455,10 @@ def proxy_attended_login_exec(
|
|||
resolved.argv[0], env=env, possible_output=login_output
|
||||
)
|
||||
status = "revoked" if revoked else "revocation could not be confirmed"
|
||||
raise ProxyError(
|
||||
raise AttendedLoginError(
|
||||
"attended login failed closed before command handoff; any possible "
|
||||
f"issued session was contained and {status}"
|
||||
f"issued session was contained and {status}",
|
||||
exit_code=10, outcome="login_failed",
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
@ -455,23 +468,33 @@ def proxy_attended_login_exec(
|
|||
resolved.argv[0], env=env, possible_output=b""
|
||||
)
|
||||
status = "revoked" if revoked else "revocation could not be confirmed"
|
||||
raise ProxyError(
|
||||
"attended command could not start; the login session was " + status
|
||||
raise AttendedLoginError(
|
||||
"attended command could not start; the login session was " + status,
|
||||
exit_code=11, outcome="child_failed",
|
||||
) from exc
|
||||
|
||||
child_output = _output_bytes(child)
|
||||
revoked = _revoke_contained(
|
||||
resolved.argv[0], env=env, possible_output=child_output
|
||||
)
|
||||
if child.returncode != 0 or child_output.strip():
|
||||
# Even whitespace is output. The separator added by _output_bytes is not.
|
||||
if child.stdout or child.stderr:
|
||||
status = "revoked" if revoked else "revocation could not be confirmed"
|
||||
raise ProxyError(
|
||||
"attended command failed closed because it returned a failure or "
|
||||
f"unexpected output; the login session was {status}"
|
||||
raise AttendedLoginError(
|
||||
"attended command produced output; reviewed commands must remain "
|
||||
f"silent (redirect both streams); the login session was {status}",
|
||||
exit_code=12, outcome="child_output",
|
||||
)
|
||||
if child.returncode != 0:
|
||||
status = "revoked" if revoked else "revocation could not be confirmed"
|
||||
raise AttendedLoginError(
|
||||
f"attended command exited non-zero; the login session was {status}",
|
||||
exit_code=11, outcome="child_failed",
|
||||
)
|
||||
if not revoked:
|
||||
raise ProxyError(
|
||||
"attended command completed but session revocation could not be confirmed"
|
||||
raise AttendedLoginError(
|
||||
"attended command completed but session revocation could not be confirmed",
|
||||
exit_code=13, outcome="revoke_unconfirmed",
|
||||
)
|
||||
return 0
|
||||
finally:
|
||||
|
|
@ -480,7 +503,11 @@ def proxy_attended_login_exec(
|
|||
if root_created:
|
||||
root.rmdir()
|
||||
except OSError as exc:
|
||||
raise ProxyError("attended login private storage cleanup failed") from exc
|
||||
raise AttendedLoginError(
|
||||
"attended login private storage cleanup failed; inspect the prior phase "
|
||||
"before retrying the reviewed command",
|
||||
exit_code=14, outcome="cleanup_failed",
|
||||
) from exc
|
||||
|
||||
|
||||
def _capture_value(resolved: ResolvedFetch) -> str:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue