Skip to content

Commit a3a1777

Browse files
authored
feat: regex-formatted allowed commands for execute_bash tool (#2483)
1 parent fdb4900 commit a3a1777

File tree

2 files changed

+52
-7
lines changed

2 files changed

+52
-7
lines changed

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

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crossterm::style::{
66
Color,
77
};
88
use eyre::Result;
9+
use regex::Regex;
910
use serde::Deserialize;
1011
use tracing::error;
1112

@@ -48,6 +49,17 @@ impl ExecuteCommand {
4849
pub fn requires_acceptance(&self, allowed_commands: Option<&Vec<String>>, allow_read_only: bool) -> bool {
4950
let default_arr = vec![];
5051
let allowed_commands = allowed_commands.unwrap_or(&default_arr);
52+
53+
let has_regex_match = allowed_commands
54+
.iter()
55+
.map(|cmd| Regex::new(&format!(r"\A{}\z", cmd)))
56+
.filter(Result::is_ok)
57+
.flatten()
58+
.any(|regex| regex.is_match(&self.command));
59+
if has_regex_match {
60+
return false;
61+
}
62+
5163
let Some(args) = shlex::split(&self.command) else {
5264
return true;
5365
};
@@ -98,9 +110,6 @@ impl ExecuteCommand {
98110
return true;
99111
},
100112
Some(cmd) => {
101-
if allowed_commands.contains(cmd) {
102-
continue;
103-
}
104113
// Special casing for `grep`. -P flag for perl regexp has RCE issues, apparently
105114
// should not be supported within grep but is flagged as a possibility since this is perl
106115
// regexp.
@@ -339,4 +348,40 @@ mod tests {
339348
);
340349
}
341350
}
351+
352+
#[test]
353+
fn test_requires_acceptance_allowed_commands() {
354+
let allowed_cmds: &[String] = &[
355+
String::from("git status"),
356+
String::from("root"),
357+
String::from("command subcommand a=[0-9]{10} b=[0-9]{10}"),
358+
String::from("command subcommand && command subcommand"),
359+
];
360+
let cmds = &[
361+
// Command first argument 'root' allowed (allows all subcommands)
362+
("root", false),
363+
("root subcommand", true),
364+
// Valid allowed_command_regex matching
365+
("git", true),
366+
("git status", false),
367+
("command subcommand a=0123456789 b=0123456789", false),
368+
("command subcommand a=0123456789 b=012345678", true),
369+
("command subcommand alternate a=0123456789 b=0123456789", true),
370+
// Control characters ignored due to direct allowed_command_regex match
371+
("command subcommand && command subcommand", false),
372+
];
373+
for (cmd, expected) in cmds {
374+
let tool = serde_json::from_value::<ExecuteCommand>(serde_json::json!({
375+
"command": cmd,
376+
}))
377+
.unwrap();
378+
assert_eq!(
379+
tool.requires_acceptance(Option::from(&allowed_cmds.to_vec()), true),
380+
*expected,
381+
"expected command: `{}` to have requires_acceptance: `{}`",
382+
cmd,
383+
expected
384+
);
385+
}
386+
}
342387
}

docs/built-in-tools.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ Execute the specified bash command.
2929

3030
### Configuration Options
3131

32-
| Option | Type | Default | Description |
33-
|--------|------|---------|-------------|
34-
| `allowedCommands` | array of strings | `[]` | List of specific commands that are allowed without prompting |
35-
| `allowReadOnly` | boolean | `true` | Whether to allow read-only commands without prompting |
32+
| Option | Type | Default | Description |
33+
|--------|------|---------|------------------------------------------------------------------------------------------|
34+
| `allowedCommands` | array of strings | `[]` | List of specific commands that are allowed without prompting. Supports regex formatting. Note that regex entered are anchored with \A and \z. |
35+
| `allowReadOnly` | boolean | `true` | Whether to allow read-only commands without prompting |
3636

3737
## Fs_read Tool
3838

0 commit comments

Comments
 (0)