Skip to content

Commit ba531b1

Browse files
committed
disallowed dangerous patterns as a first priority for executing tools
1 parent a259f8b commit ba531b1

File tree

2 files changed

+27
-16
lines changed

2 files changed

+27
-16
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2166,7 +2166,7 @@ impl ChatSession {
21662166
}
21672167
tool_use.accepted = true;
21682168

2169-
return Ok(ChatState::ExecuteTools); //REMOVEMEBUTIMPT
2169+
return Ok(ChatState::ExecuteTools);
21702170
}
21712171
} else if !self.pending_prompts.is_empty() {
21722172
let prompts = self.pending_prompts.drain(0..).collect();

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

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,14 @@ pub struct ExecuteCommand {
4747
pub summary: Option<String>,
4848
}
4949

50-
impl ExecuteCommand { //REMOVEMEBUTIMPT actual tool call verification
50+
impl ExecuteCommand { //REMOVEME guard point
5151
pub fn requires_acceptance(&self, allowed_commands: Option<&Vec<String>>, allow_read_only: bool) -> bool {
5252
// Always require acceptance for multi-line commands.
5353
if self.command.contains("\n") || self.command.contains("\r") {
5454
return true;
5555
}
5656

5757
let default_arr = vec![];
58-
let allowed_commands = allowed_commands.unwrap_or(&default_arr);
59-
60-
let has_regex_match = allowed_commands
61-
.iter()
62-
.map(|cmd| Regex::new(&format!(r"\A{}\z", cmd)))
63-
.filter(Result::is_ok)
64-
.flatten()
65-
.any(|regex| regex.is_match(&self.command));
66-
if has_regex_match {
67-
return false;
68-
}
6958

7059
let Some(args) = shlex::split(&self.command) else {
7160
return true;
@@ -100,6 +89,17 @@ impl ExecuteCommand { //REMOVEMEBUTIMPT actual tool call verification
10089
if !current_cmd.is_empty() {
10190
all_commands.push(current_cmd);
10291
}
92+
let allowed_commands = allowed_commands.unwrap_or(&default_arr);
93+
94+
let has_regex_match = allowed_commands
95+
.iter()
96+
.map(|cmd| Regex::new(&format!(r"\A{}\z", cmd)))
97+
.filter(Result::is_ok)
98+
.flatten()
99+
.any(|regex| regex.is_match(&self.command));
100+
if has_regex_match {
101+
return false;
102+
}
103103

104104
// Check if each command in the pipe chain starts with a safe command
105105
for cmd_args in all_commands {
@@ -294,7 +294,7 @@ mod tests {
294294
};
295295

296296
#[test]
297-
fn test_requires_acceptance_for_readonly_commands() { //REMOVEMEBUTIMPT tests unit tests you should write some
297+
fn test_requires_acceptance_for_readonly_commands() {
298298
let cmds = &[
299299
// Safe commands
300300
("ls ~", false),
@@ -423,8 +423,19 @@ mod tests {
423423
("command subcommand a=0123456789 b=0123456789", false),
424424
("command subcommand a=0123456789 b=012345678", true),
425425
("command subcommand alternate a=0123456789 b=0123456789", true),
426-
// Control characters ignored due to direct allowed_command_regex match
427-
("command subcommand && command subcommand", false),
426+
// dangerous patterns
427+
("echo 'test<(data'", true),
428+
("echo 'test$(data)'", true),
429+
("echo 'test`data`'", true),
430+
("echo 'test' > output.txt", true),
431+
("echo 'test data' && touch main.py", true),
432+
("echo 'test' || rm file", true),
433+
("echo 'test' & background", true),
434+
("echo 'test data'; touch main.py", true),
435+
("echo $HOME", true),
436+
("echo 'test\nrm file'", true),
437+
("echo 'test\rrm file'", true),
438+
("IFS=/ malicious", true),
428439
];
429440
for (cmd, expected) in cmds {
430441
let tool = serde_json::from_value::<ExecuteCommand>(serde_json::json!({

0 commit comments

Comments
 (0)