Include terminal launch commands in Guardian stdin reviews (#57758)

## Why

Guardian's `write_stdin` approval requests lacked the command used to launch the receiving terminal, leaving reviews without that context.

## What changed

- Carry `original_command` from the terminal's launch command into the approval request and Guardian action JSON. This describes the original launch, not the terminal's current state.
- Exclude `original_command` from the 8,000-byte stdin approval size check so launch context does not consume the budget for input, permission details, and the approval reason. Launch context remains subject to Guardian's normal request budget.

## Testing

Extend serialization coverage to check `original_command` and the strict stdin review integration test to verify that a launch command longer than 8,000 bytes reaches Guardian intact.

GitOrigin-RevId: cc765a6bf80d080baf94c959eafa6227b2889e1f
This commit is contained in:
jif
2026-10-28 15:10:52 +00:00
committed by copyberry
parent fc916a22b5
commit 7c11b8074d
9 changed files with 41 additions and 13 deletions
-1
View File
@@ -195,7 +195,6 @@ impl From<&Arc<TurnContext>> for GuardianReviewContext {
#[cfg(test)]
use codex_guardian_reviewer::guardian_output_schema;
pub(crate) use approval_request::format_guardian_action_pretty;
#[cfg(test)]
use approval_request::guardian_assessment_action;
#[cfg(test)]
+1
View File
@@ -653,6 +653,7 @@ async fn approval_permissions_use_the_owning_environment() -> anyhow::Result<()>
approval_id: "stdin-secondary".to_string(),
environment_id: "secondary".to_string(),
process_id: 42,
original_command: "bash".to_string(),
input: "cat private/secret.txt\n".to_string(),
cwd: cwd.clone().into(),
tty: true,
+4
View File
@@ -82,6 +82,7 @@ pub(crate) enum ApprovalAction {
approval_id: String,
environment_id: String,
process_id: i32,
original_command: String,
input: String,
cwd: PathUri,
tty: bool,
@@ -182,6 +183,7 @@ impl ApprovalAction {
approval_id,
environment_id,
process_id,
original_command: _,
input,
cwd,
tty,
@@ -337,6 +339,7 @@ impl ApprovalAction {
approval_id,
environment_id,
process_id,
original_command,
input,
cwd,
tty,
@@ -348,6 +351,7 @@ impl ApprovalAction {
approval_id,
environment_id,
process_id,
original_command,
input,
cwd,
tty,
@@ -934,7 +934,7 @@ impl UnifiedExecProcessManager {
"terminal input and permission details are too large to review safely; use a smaller input or start a new terminal with fewer grants".to_string(),
))
};
let reviewed = crate::guardian::format_guardian_action_pretty(
let mut reviewed = codex_guardian_reviewer::guardian_approval_request_to_json(
&approval
.clone()
.into_guardian_request(/*exec_command_cwd_convention*/ None)
@@ -943,8 +943,14 @@ impl UnifiedExecProcessManager {
})?,
)
.map_err(|_| unreviewable_input_error())?;
// Bound the entire serialized action plus its reason, including JSON
// escaping. Reject, never execute an unreviewed tail.
// Launch context uses Guardian's normal request budget, not the stdin limit.
if let Some(fields) = reviewed.as_object_mut() {
fields.remove("original_command");
}
let reviewed =
serde_json::to_string_pretty(&reviewed).map_err(|_| unreviewable_input_error())?;
// Bound the input and permission details plus their reason, including
// JSON escaping. Reject, never execute an unreviewed tail.
let oversized =
reviewed.len().saturating_add(approval_reason.len()) > MAX_STDIN_APPROVAL_BYTES;
let size_check_result = if oversized {
@@ -276,6 +276,7 @@ impl ProcessEntry {
approval_id: context.call_id.clone(),
environment_id: self.environment_id.clone(),
process_id: self.process_id,
original_command: self.hook_command.clone(),
input: input.to_string(),
cwd: self.cwd.clone(),
tty: self.tty,