From cbdd8493ed47dfe06944b90e16e417bac7c1070e Mon Sep 17 00:00:00 2001 From: iris <2+iris@noreply.localhost> Date: Wed, 9 Sep 2026 22:17:14 -0400 Subject: [PATCH] Unwrap double-quoted Codex Bash commands --- server/src/session/codex/translate.rs | 43 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/server/src/session/codex/translate.rs b/server/src/session/codex/translate.rs index 47c2281..78bb61a 100644 --- a/server/src/session/codex/translate.rs +++ b/server/src/session/codex/translate.rs @@ -291,13 +291,16 @@ fn command_tool(item: &Value) -> (String, Value) { ("Shell".to_string(), json!({"command": command})) } -/// App-server renders the executor argv into one shell-quoted string. Unwrap only the exact Bash -/// form Codex currently emits; anything with another argument or quoting form stays visible rather -/// than being decoded into a command that was not actually run. +/// App-server renders the executor argv into one shell-quoted string. Unwrap only an exact Bash +/// invocation containing one shell word; extra arguments stay visible rather than being decoded +/// into a command that was not actually run. fn rendered_bash_script(command: &str) -> Option { let quoted = ["/usr/bin/bash -lc ", "/bin/bash -lc ", "bash -lc "] .iter() .find_map(|prefix| command.strip_prefix(prefix))?; + if quoted.starts_with('"') { + return double_quoted_shell_word(quoted); + } if !quoted.starts_with('\'') { return (!quoted.is_empty() && !quoted.chars().any(char::is_whitespace)) .then(|| quoted.to_string()); @@ -324,6 +327,26 @@ fn rendered_bash_script(command: &str) -> Option { } } +fn double_quoted_shell_word(quoted: &str) -> Option { + let mut chars = quoted.strip_prefix('"')?.chars(); + let mut script = String::new(); + while let Some(character) = chars.next() { + match character { + '"' => return chars.next().is_none().then_some(script), + '\\' => match chars.next()? { + escaped @ ('$' | '`' | '"' | '\\') => script.push(escaped), + '\n' => {} + escaped => { + script.push('\\'); + script.push(escaped); + } + }, + character => script.push(character), + } + } + None +} + fn shell_word(word: &Value) -> String { let Some(word) = word.as_str() else { return word.to_string(); @@ -587,6 +610,20 @@ mod tests { )) ); + let double_quoted = tool(&json!({ + "id": "double-quoted", + "type": "commandExecution", + "command": r#"/usr/bin/bash -lc "printf '%s\\n' \"\$HOME\" \\path""# + })); + assert_eq!( + double_quoted, + Some(( + "double-quoted".to_string(), + "Bash".to_string(), + json!({"command": "printf '%s\\n' \"$HOME\" \\path"}) + )) + ); + let fish = tool(&line( r#"{"id":"fish","type":"commandExecution","command":["/usr/bin/fish","-c","pwd"]}"#, ));