@@ -480,9 +480,48 @@ export function getCommandDecision(
480480) : CommandDecision {
481481 if ( ! command ?. trim ( ) ) return "auto_approve"
482482
483- // Only block subshell execution attempts if there's a denylist configured
483+ // Check if subshells contain denied prefixes
484484 if ( ( command . includes ( "$(" ) || command . includes ( "`" ) ) && deniedCommands ?. length ) {
485- return "auto_deny"
485+ // Extract subshell content and check if it contains denied prefixes
486+ let match : RegExpExecArray | null
487+
488+ // Check $() subshells
489+ const dollarRegex = / \$ \( ( .* ?) \) / g
490+ while ( ( match = dollarRegex . exec ( command ) ) !== null ) {
491+ const subshellContent = match [ 1 ] . trim ( )
492+ // Check if the subshell content matches any denied prefix
493+ if (
494+ deniedCommands . some ( ( denied ) => {
495+ const lowerDenied = denied . toLowerCase ( )
496+ const lowerContent = subshellContent . toLowerCase ( )
497+ return lowerContent . startsWith ( lowerDenied )
498+ } )
499+ ) {
500+ return "auto_deny"
501+ }
502+ }
503+
504+ // Check `` subshells
505+ const backtickRegex = / ` ( .* ?) ` / g
506+ while ( ( match = backtickRegex . exec ( command ) ) !== null ) {
507+ const subshellContent = match [ 1 ] . trim ( )
508+ // Check if the subshell content matches any denied prefix
509+ if (
510+ deniedCommands . some ( ( denied ) => {
511+ const lowerDenied = denied . toLowerCase ( )
512+ const lowerContent = subshellContent . toLowerCase ( )
513+ return lowerContent . startsWith ( lowerDenied )
514+ } )
515+ ) {
516+ return "auto_deny"
517+ }
518+ }
519+
520+ // Also check if the main command contains denied prefixes
521+ const mainCommandLower = command . toLowerCase ( )
522+ if ( deniedCommands . some ( ( denied ) => mainCommandLower . includes ( denied . toLowerCase ( ) ) ) ) {
523+ return "auto_deny"
524+ }
486525 }
487526
488527 // Parse into sub-commands (split by &&, ||, ;, |)
0 commit comments