From d32d3f8b719eeeb7df664ed5a326f6f381ef4df3 Mon Sep 17 00:00:00 2001 From: Gabriel Peal Date: Mon, 20 Oct 2025 15:25:43 -0700 Subject: [PATCH] Works --- codex-rs/core/src/bash.rs | 26 +++++++++----- .../command_safety/is_dangerous_command.rs | 13 +++++-- .../src/command_safety/is_safe_command.rs | 9 +++-- codex-rs/core/src/parse_command.rs | 36 +++++++++++++------ 4 files changed, 60 insertions(+), 24 deletions(-) diff --git a/codex-rs/core/src/bash.rs b/codex-rs/core/src/bash.rs index c87f2764b1..07fc125f16 100644 --- a/codex-rs/core/src/bash.rs +++ b/codex-rs/core/src/bash.rs @@ -5,13 +5,13 @@ use tree_sitter_bash::LANGUAGE as BASH; /// Parse the provided bash source using tree-sitter-bash, returning a Tree on /// success or None if parsing failed. -pub fn try_parse_bash(bash_lc_arg: &str) -> Option { +pub fn try_parse_shell(shell_lc_arg: &str) -> Option { let lang = BASH.into(); let mut parser = Parser::new(); #[expect(clippy::expect_used)] parser.set_language(&lang).expect("load bash grammar"); let old_tree: Option<&Tree> = None; - parser.parse(bash_lc_arg, old_tree) + parser.parse(shell_lc_arg, old_tree) } /// Parse a script which may contain multiple simple commands joined only by @@ -88,18 +88,19 @@ pub fn try_parse_word_only_commands_sequence(tree: &Tree, src: &str) -> Option Option>> { - let [bash, flag, script] = command else { +/// Returns the sequence of plain commands within a `bash -lc "..."` or +/// `zsh -lc "..."` invocation when the script only contains word-only commands +/// joined by safe operators. +pub fn parse_shell_lc_plain_commands(command: &[String]) -> Option>> { + let [shell, flag, script] = command else { return None; }; - if bash != "bash" || flag != "-lc" { + if flag != "-lc" || !(shell == "bash" || shell == "zsh") { return None; } - let tree = try_parse_bash(script)?; + let tree = try_parse_shell(script)?; try_parse_word_only_commands_sequence(&tree, script) } @@ -154,7 +155,7 @@ mod tests { use super::*; fn parse_seq(src: &str) -> Option>> { - let tree = try_parse_bash(src)?; + let tree = try_parse_shell(src)?; try_parse_word_only_commands_sequence(&tree, src) } @@ -234,4 +235,11 @@ mod tests { fn rejects_trailing_operator_parse_error() { assert!(parse_seq("ls &&").is_none()); } + + #[test] + fn parse_zsh_lc_plain_commands() { + let command = vec!["zsh".to_string(), "-lc".to_string(), "ls".to_string()]; + let parsed = parse_shell_lc_plain_commands(&command).unwrap(); + assert_eq!(parsed, vec![vec!["ls".to_string()]]); + } } diff --git a/codex-rs/core/src/command_safety/is_dangerous_command.rs b/codex-rs/core/src/command_safety/is_dangerous_command.rs index 852af93ef9..b422513674 100644 --- a/codex-rs/core/src/command_safety/is_dangerous_command.rs +++ b/codex-rs/core/src/command_safety/is_dangerous_command.rs @@ -1,4 +1,4 @@ -use crate::bash::parse_bash_lc_plain_commands; +use crate::bash::parse_shell_lc_plain_commands; pub fn command_might_be_dangerous(command: &[String]) -> bool { if is_dangerous_to_call_with_exec(command) { @@ -6,7 +6,7 @@ pub fn command_might_be_dangerous(command: &[String]) -> bool { } // Support `bash -lc "