Skip to content

Commit f6b53f2

Browse files
committed
fix: add more safety checks for execute_bash readonly checks
1 parent e25efa8 commit f6b53f2

File tree

1 file changed

+23
-2
lines changed
  • crates/chat-cli/src/cli/chat/tools/execute

1 file changed

+23
-2
lines changed

crates/chat-cli/src/cli/chat/tools/execute/mod.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ pub struct ExecuteCommand {
4848

4949
impl ExecuteCommand {
5050
pub fn requires_acceptance(&self, allowed_commands: Option<&Vec<String>>, allow_read_only: bool) -> bool {
51+
// Always require acceptance for multi-line commands.
52+
if self.command.contains("\n") {
53+
return true;
54+
}
55+
5156
let default_arr = vec![];
5257
let allowed_commands = allowed_commands.unwrap_or(&default_arr);
5358

@@ -64,7 +69,7 @@ impl ExecuteCommand {
6469
let Some(args) = shlex::split(&self.command) else {
6570
return true;
6671
};
67-
const DANGEROUS_PATTERNS: &[&str] = &["<(", "$(", "`", ">", "&&", "||", "&", ";"];
72+
const DANGEROUS_PATTERNS: &[&str] = &["<(", "$(", "`", ">", "&&", "||", "&", ";", "${", "\n", "IFS"];
6873

6974
if args
7075
.iter()
@@ -106,6 +111,7 @@ impl ExecuteCommand {
106111
arg.contains("-exec") // includes -execdir
107112
|| arg.contains("-delete")
108113
|| arg.contains("-ok") // includes -okdir
114+
|| arg.contains("-fprint") // includes -fprint0 and -fprintf
109115
}) =>
110116
{
111117
return true;
@@ -114,7 +120,11 @@ impl ExecuteCommand {
114120
// Special casing for `grep`. -P flag for perl regexp has RCE issues, apparently
115121
// should not be supported within grep but is flagged as a possibility since this is perl
116122
// regexp.
117-
if cmd == "grep" && cmd_args.iter().any(|arg| arg.contains("-P")) {
123+
if cmd == "grep"
124+
&& cmd_args
125+
.iter()
126+
.any(|arg| arg.contains("-P") || arg.contains("--perl-regexp"))
127+
{
118128
return true;
119129
}
120130
let is_cmd_read_only = READONLY_COMMANDS.contains(&cmd.as_str());
@@ -290,6 +300,13 @@ mod tests {
290300
("cat <<< 'some string here' > myimportantfile", true),
291301
("echo '\n#!/usr/bin/env bash\necho hello\n' > myscript.sh", true),
292302
("cat <<EOF > myimportantfile\nhello world\nEOF", true),
303+
// newline check
304+
("which ls\ntouch asdf", true),
305+
// $IFS check
306+
(
307+
r#"IFS=';'; for cmd in "which ls;touch asdf"; do eval "$cmd"; done"#,
308+
true,
309+
),
293310
// Safe piped commands
294311
("find . -name '*.rs' | grep main", false),
295312
("ls -la | grep .git", false),
@@ -307,8 +324,12 @@ mod tests {
307324
true,
308325
),
309326
("find important-dir/ -name '*.txt'", false),
327+
(r#"find / -fprintf "/path/to/file" <data-to-write> -quit"#, true),
328+
(r"find . -${t}exec touch asdf \{\} +", true),
329+
(r"find . -${t:=exec} touch asdf2 \{\} +", true),
310330
// `grep` command arguments
311331
("echo 'test data' | grep -P '(?{system(\"date\")})'", true),
332+
("echo 'test data' | grep --perl-regexp '(?{system(\"date\")})'", true),
312333
];
313334
for (cmd, expected) in cmds {
314335
let tool = serde_json::from_value::<ExecuteCommand>(serde_json::json!({

0 commit comments

Comments
 (0)