@@ -6,6 +6,7 @@ use crossterm::style::{
66 Color ,
77} ;
88use eyre:: Result ;
9+ use regex:: Regex ;
910use serde:: Deserialize ;
1011use 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}
0 commit comments