@@ -275,6 +275,100 @@ describe("RooIgnoreController", () => {
275275 expect ( controller . validateCommand ( "npm install" ) ) . toBeUndefined ( )
276276 } )
277277
278+ /**
279+ * Tests validation of shell redirections and command substitutions
280+ */
281+ it ( "should block shell redirections that read ignored files" , ( ) => {
282+ // Input redirection
283+ expect ( controller . validateCommand ( "wc -l < node_modules/package.json" ) ) . toBe ( "node_modules/package.json" )
284+ expect ( controller . validateCommand ( "sort < secrets/api-keys.json" ) ) . toBe ( "secrets/api-keys.json" )
285+ expect ( controller . validateCommand ( "grep pattern <.git/config" ) ) . toBe ( ".git/config" )
286+
287+ // Should allow non-ignored files
288+ expect ( controller . validateCommand ( "wc -l < README.md" ) ) . toBeUndefined ( )
289+ } )
290+
291+ it ( "should block command substitutions that read ignored files" , ( ) => {
292+ // $() command substitution
293+ expect ( controller . validateCommand ( "echo $(cat node_modules/package.json)" ) ) . toBe (
294+ "node_modules/package.json" ,
295+ )
296+ expect ( controller . validateCommand ( "result=$(head secrets/api-keys.json)" ) ) . toBe ( "secrets/api-keys.json" )
297+
298+ // Backtick command substitution
299+ expect ( controller . validateCommand ( "echo `cat .git/config`" ) ) . toBe ( ".git/config" )
300+ expect ( controller . validateCommand ( "data=`tail error.log`" ) ) . toBe ( "error.log" )
301+
302+ // Process substitution
303+ expect ( controller . validateCommand ( "diff <(cat node_modules/index.js) file2" ) ) . toBe ( "node_modules/index.js" )
304+
305+ // Should allow non-ignored files
306+ expect ( controller . validateCommand ( "echo $(cat README.md)" ) ) . toBeUndefined ( )
307+ } )
308+
309+ it ( "should block piped commands that read ignored files" , ( ) => {
310+ // Commands in pipelines
311+ expect ( controller . validateCommand ( "cat node_modules/package.json | grep version" ) ) . toBe (
312+ "node_modules/package.json" ,
313+ )
314+ expect ( controller . validateCommand ( "echo test | tee secrets/output.log; cat secrets/output.log" ) ) . toBe (
315+ "secrets/output.log" ,
316+ )
317+ expect ( controller . validateCommand ( "ls && head .git/config" ) ) . toBe ( ".git/config" )
318+
319+ // Should allow non-ignored files in pipelines
320+ expect ( controller . validateCommand ( "cat README.md | grep title" ) ) . toBeUndefined ( )
321+ } )
322+
323+ it ( "should detect additional file reading commands" , ( ) => {
324+ // Additional Unix utilities
325+ expect ( controller . validateCommand ( "nl node_modules/package.json" ) ) . toBe ( "node_modules/package.json" )
326+ expect ( controller . validateCommand ( "tac .git/config" ) ) . toBe ( ".git/config" )
327+ expect ( controller . validateCommand ( "strings secrets/binary.dat" ) ) . toBe ( "secrets/binary.dat" )
328+ expect ( controller . validateCommand ( "hexdump error.log" ) ) . toBe ( "error.log" )
329+ expect ( controller . validateCommand ( "od -c node_modules/index.js" ) ) . toBe ( "node_modules/index.js" )
330+
331+ // Compressed file readers
332+ expect ( controller . validateCommand ( "zcat secrets/data.gz" ) ) . toBe ( "secrets/data.gz" )
333+ expect ( controller . validateCommand ( "bzcat node_modules/archive.bz2" ) ) . toBe ( "node_modules/archive.bz2" )
334+
335+ // File comparison utilities
336+ expect ( controller . validateCommand ( "diff .git/config file2" ) ) . toBe ( ".git/config" )
337+ expect ( controller . validateCommand ( "cmp secrets/key1 secrets/key2" ) ) . toBe ( "secrets/key1" )
338+
339+ // Windows/PowerShell commands
340+ expect ( controller . validateCommand ( "findstr pattern node_modules/package.json" ) ) . toBe (
341+ "node_modules/package.json" ,
342+ )
343+ expect ( controller . validateCommand ( "fc .git/config file2" ) ) . toBe ( ".git/config" )
344+ } )
345+
346+ it ( "should handle complex command patterns" , ( ) => {
347+ // Commands with quotes
348+ expect ( controller . validateCommand ( 'cat "node_modules/package.json"' ) ) . toBe ( "node_modules/package.json" )
349+ expect ( controller . validateCommand ( "cat 'secrets/api-keys.json'" ) ) . toBe ( "secrets/api-keys.json" )
350+
351+ // Multiple files in one command
352+ expect ( controller . validateCommand ( "cat README.md node_modules/index.js" ) ) . toBe ( "node_modules/index.js" )
353+
354+ // Nested command substitutions
355+ expect ( controller . validateCommand ( "echo $(echo $(cat .git/config))" ) ) . toBe ( ".git/config" )
356+
357+ // Mixed patterns
358+ expect ( controller . validateCommand ( "cat < node_modules/data.txt | grep pattern" ) ) . toBe (
359+ "node_modules/data.txt" ,
360+ )
361+ } )
362+
363+ it ( "should handle xargs and find commands that might read files" , ( ) => {
364+ // xargs with file reading commands
365+ expect ( controller . validateCommand ( "find . -name '*.json' | xargs cat" ) ) . toBeUndefined ( ) // Can't determine specific files
366+ expect ( controller . validateCommand ( "echo node_modules/package.json | xargs cat" ) ) . toBeUndefined ( ) // File path is in stdin, not command
367+
368+ // find with -exec
369+ expect ( controller . validateCommand ( "find . -name 'package.json' -exec cat {} \\;" ) ) . toBeUndefined ( ) // Can't determine specific files
370+ } )
371+
278372 /**
279373 * Tests behavior when no .rooignore exists
280374 */
@@ -287,6 +381,8 @@ describe("RooIgnoreController", () => {
287381 // All commands should be allowed
288382 expect ( emptyController . validateCommand ( "cat node_modules/package.json" ) ) . toBeUndefined ( )
289383 expect ( emptyController . validateCommand ( "grep pattern .git/config" ) ) . toBeUndefined ( )
384+ expect ( emptyController . validateCommand ( "echo $(cat secrets/file)" ) ) . toBeUndefined ( )
385+ expect ( emptyController . validateCommand ( "wc < .git/config" ) ) . toBeUndefined ( )
290386 } )
291387 } )
292388
0 commit comments