@@ -81,12 +81,20 @@ impl ExecuteCommand {
81
81
// against unwanted mutations
82
82
Some ( cmd)
83
83
if cmd == "find"
84
- && cmd_args
85
- . iter ( )
86
- . any ( |arg| arg. contains ( "-exec" ) || arg. contains ( "-delete" ) ) =>
84
+ && cmd_args. iter ( ) . any ( |arg| {
85
+ arg. contains ( "-exec" ) // includes -execdir
86
+ || arg. contains ( "-delete" )
87
+ || arg. contains ( "-ok" ) // includes -okdir
88
+ } ) =>
87
89
{
88
90
return true ;
89
91
} ,
92
+ // Special casing for `grep`. -P flag for perl regexp has RCE issues, apparently
93
+ // should not be supported within grep but is flagged as a possibility since this is perl
94
+ // regexp.
95
+ Some ( cmd) if cmd == "grep" && cmd_args. iter ( ) . any ( |arg| arg. contains ( "-P" ) ) => {
96
+ return true ;
97
+ } ,
90
98
Some ( cmd) if !READONLY_COMMANDS . contains ( & cmd. as_str ( ) ) => return true ,
91
99
None => return true ,
92
100
_ => ( ) ,
@@ -162,6 +170,66 @@ pub fn format_output(output: &str, max_size: usize) -> String {
162
170
mod tests {
163
171
use super :: * ;
164
172
173
+ #[ test]
174
+ fn test_requires_acceptance_for_readonly_commands ( ) {
175
+ let cmds = & [
176
+ // Safe commands
177
+ ( "ls ~" , false ) ,
178
+ ( "ls -al ~" , false ) ,
179
+ ( "pwd" , false ) ,
180
+ ( "echo 'Hello, world!'" , false ) ,
181
+ ( "which aws" , false ) ,
182
+ // Potentially dangerous readonly commands
183
+ ( "echo hi > myimportantfile" , true ) ,
184
+ ( "ls -al >myimportantfile" , true ) ,
185
+ ( "echo hi 2> myimportantfile" , true ) ,
186
+ ( "echo hi >> myimportantfile" , true ) ,
187
+ ( "echo $(rm myimportantfile)" , true ) ,
188
+ ( "echo `rm myimportantfile`" , true ) ,
189
+ ( "echo hello && rm myimportantfile" , true ) ,
190
+ ( "echo hello&&rm myimportantfile" , true ) ,
191
+ ( "ls nonexistantpath || rm myimportantfile" , true ) ,
192
+ ( "echo myimportantfile | xargs rm" , true ) ,
193
+ ( "echo myimportantfile|args rm" , true ) ,
194
+ ( "echo <(rm myimportantfile)" , true ) ,
195
+ ( "cat <<< 'some string here' > myimportantfile" , true ) ,
196
+ ( "echo '\n #!/usr/bin/env bash\n echo hello\n ' > myscript.sh" , true ) ,
197
+ ( "cat <<EOF > myimportantfile\n hello world\n EOF" , true ) ,
198
+ // Safe piped commands
199
+ ( "find . -name '*.rs' | grep main" , false ) ,
200
+ ( "ls -la | grep .git" , false ) ,
201
+ ( "cat file.txt | grep pattern | head -n 5" , false ) ,
202
+ // Unsafe piped commands
203
+ ( "find . -name '*.rs' | rm" , true ) ,
204
+ ( "ls -la | grep .git | rm -rf" , true ) ,
205
+ ( "echo hello | sudo rm -rf /" , true ) ,
206
+ // `find` command arguments
207
+ ( "find important-dir/ -exec rm {} \\ ;" , true ) ,
208
+ ( "find . -name '*.c' -execdir gcc -o '{}.out' '{}' \\ ;" , true ) ,
209
+ ( "find important-dir/ -delete" , true ) ,
210
+ (
211
+ "echo y | find . -type f -maxdepth 1 -okdir open -a Calculator {} +" ,
212
+ true ,
213
+ ) ,
214
+ ( "find important-dir/ -name '*.txt'" , false ) ,
215
+ // `grep` command arguments
216
+ ( "echo 'test data' | grep -P '(?{system(\" date\" )})'" , true ) ,
217
+ ] ;
218
+ for ( cmd, expected) in cmds {
219
+ let tool = serde_json:: from_value :: < ExecuteCommand > ( serde_json:: json!( {
220
+ "command" : cmd,
221
+ } ) )
222
+ . unwrap ( ) ;
223
+ assert_eq ! (
224
+ tool. requires_acceptance( ) ,
225
+ * expected,
226
+ "expected command: `{}` to have requires_acceptance: `{}`" ,
227
+ cmd,
228
+ expected
229
+ ) ;
230
+ }
231
+ }
232
+
165
233
#[ test]
166
234
fn test_requires_acceptance_for_windows_commands ( ) {
167
235
let cmds = & [
0 commit comments