diff --git a/crates/agent/src/tool_permissions.rs b/crates/agent/src/tool_permissions.rs index 46313faf05577659a3bdb9b9b70b241ea75256c2..215e979f798addb339f6b5929adea4500fb60e40 100644 --- a/crates/agent/src/tool_permissions.rs +++ b/crates/agent/src/tool_permissions.rs @@ -982,8 +982,8 @@ mod tests { } #[test] - fn nushell_denies_when_always_allow_configured() { - t("ls").allow(&["^ls"]).shell(ShellKind::Nushell).is_deny(); + fn nushell_allows_with_allow_pattern() { + t("ls").allow(&["^ls"]).shell(ShellKind::Nushell).is_allow(); } #[test] @@ -1012,8 +1012,13 @@ mod tests { } #[test] - fn elvish_denies_when_always_allow_configured() { - t("ls").allow(&["^ls"]).shell(ShellKind::Elvish).is_deny(); + fn elvish_allows_with_allow_pattern() { + t("ls").allow(&["^ls"]).shell(ShellKind::Elvish).is_allow(); + } + + #[test] + fn rc_allows_with_allow_pattern() { + t("ls").allow(&["^ls"]).shell(ShellKind::Rc).is_allow(); } #[test] diff --git a/crates/util/src/shell.rs b/crates/util/src/shell.rs index ecac8eaf6aa7d13148d6a5c0770bcedc351c14cf..8ace92bf031070c1f888e6e4a2e2a5cc691e3a51 100644 --- a/crates/util/src/shell.rs +++ b/crates/util/src/shell.rs @@ -256,16 +256,24 @@ impl ShellKind { Self::new(&get_system_shell(), cfg!(windows)) } - /// Returns whether this shell uses POSIX-like command chaining syntax (`&&`, `||`, `;`, `|`). + /// Returns whether this shell's command chaining syntax can be parsed by brush-parser. /// /// This is used to determine if we can safely parse shell commands to extract sub-commands /// for security purposes (e.g., preventing shell injection in "always allow" patterns). /// - /// **Compatible shells:** Posix (sh, bash, dash, zsh), Fish 3.0+, PowerShell 7+/Pwsh, - /// Cmd, Xonsh, Csh, Tcsh + /// The brush-parser handles `;` (sequential execution) and `|` (piping), which are + /// supported by all common shells. It also handles `&&` and `||` for conditional + /// execution, `$()` and backticks for command substitution, and process substitution. /// - /// **Incompatible shells:** Nushell (uses `and`/`or` keywords), Elvish (uses `and`/`or` - /// keywords), Rc (Plan 9 shell - no `&&`/`||` operators) + /// # Shell Notes + /// + /// - **Nushell**: Uses `;` for sequential execution. The `and`/`or` keywords are boolean + /// operators on values (e.g., `$true and $false`), not command chaining operators. + /// - **Elvish**: Uses `;` to separate pipelines, which brush-parser handles. Elvish does + /// not have `&&` or `||` operators. Its `and`/`or` are special commands that operate + /// on values, not command chaining (e.g., `and $true $false`). + /// - **Rc (Plan 9)**: Uses `;` for sequential execution and `|` for piping. Does not + /// have `&&`/`||` operators for conditional chaining. pub fn supports_posix_chaining(&self) -> bool { matches!( self, @@ -277,6 +285,9 @@ impl ShellKind { | ShellKind::Xonsh | ShellKind::Csh | ShellKind::Tcsh + | ShellKind::Nushell + | ShellKind::Elvish + | ShellKind::Rc ) }