[FEATURE] Auto-allow piped/chained commands when all components are individually allowed
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Problem
When individual commands are in the permissions allow list (e.g. Bash(.venv/bin/pylint:*) and Bash(grep:*)), piping or chaining them together (e.g. .venv/bin/pylint apps/ | grep "^apps") still triggers a permission prompt. The matcher evaluates the full compound command string as one unit rather than decomposing it by shell operators.
This means every pipeline of two allowed commands requires manual approval, even though neither component would prompt on its own.
Expected behavior
When a Bash command contains shell operators (|, &&, ;), decompose the compound command into its individual subcommands and check each against the allow list independently. If every subcommand matches an allowed pattern, auto-allow the full command without prompting.
Current workaround
None that works reliably at a global level. Users must either approve each specific compound command one at a time as they encounter it, or avoid piping altogether and run commands separately.
Why this matters
Piping allowed commands is a routine developer pattern (pylint ... | grep ..., git log ... | head, find ... | wc -l). Prompting on every such combination creates friction that trains users to approve without reading — the opposite of the security intent.
Security consideration
The decomposition should respect disallow rules: if any subcommand in the pipeline matches a disallow pattern, the full command should still be blocked. The check is: every component is individually allowed AND no component is individually disallowed.
Proposed Solution
When the Bash permission checker encounters a compound command, split it by shell operators (|, &&, ;, ||) into subcommands, trim each, and check every subcommand independently against the allow and disallow lists. Auto-allow the full command only if every subcommand passes.
Pseudocode:
def is_allowed(full_command, allow_rules, disallow_rules):
subcommands = split_by_shell_operators(full_command) # split on |, &&, ;, ||
for sub in subcommands:
sub = sub.strip()
if matches_any(sub, disallow_rules):
return False
if not matches_any(sub, allow_rules):
return False
return True
Example
Given these settings:
{
"permissions": {
"allow": [
"Bash(.venv/bin/pylint:*)",
"Bash(grep:*)"
]
}
}
| Command | Today | After this change |
|---|---|---|
| .venv/bin/pylint apps/ | auto-allowed | auto-allowed (no change) |
| grep -E "^apps" | auto-allowed | auto-allowed (no change) |
| .venv/bin/pylint apps/ \| grep "^apps" | prompts | auto-allowed (both components match) |
| .venv/bin/pylint apps/ && rm -rf / | prompts | still prompts (rm -rf / matches no allow rule) |
Security consideration
The decomposition should respect disallow rules: if any subcommand in the pipeline matches a disallow pattern, the full command should still be blocked. The check is: every component is individually allowed AND no component is individually disallowed.
Alternative Solutions
_No response_
Priority
High - Significant impact on productivity
Feature Category
CLI commands and flags
Use Case Example
_No response_
Additional Context
_No response_
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗