Security heuristics override explicit permission allowlist
Problem
When a Bash command is explicitly authorized in settings.local.json via permissions.allow, Claude Code's security heuristics still trigger approval prompts, effectively bypassing the allowlist.
Configuration
{
"permissions": {
"allow": [
"Bash(git:*)",
"Bash(pnpm:*)",
"Bash(gh pr:*)",
"Bash(gh issue:*)"
]
}
}
Reproducible cases
1. $() command substitution
Command:
BRANCH=$(git branch --show-current) && ISSUE_NUMBER=$(echo "$BRANCH" | grep -oE '^[0-9]+')
- Should match
Bash(git:*)but doesn't because the prefix isBRANCH=, notgit - Warning: "Command contains $() command substitution"
2. Heredoc with # in gh pr create body
Command:
gh pr create --title "feat: ..." --body "$(cat <<'EOF'
## Summary
- [x] item
Closes #18
EOF
)"
- Should match
Bash(gh pr:*)and the prefix does match - But the warning "Command contains a quoted newline followed by a #-prefixed line, which can hide arguments" overrides the permission
3. 2>&1 redirection in chained commands
Command:
pnpm build 2>&1 && echo "--- BUILD OK ---" && pnpm typecheck 2>&1
- Should match
Bash(pnpm:*) - Warning: "Command contains quoted characters in flag names"
Expected behavior
If the user has explicitly authorized a pattern in the allowlist, security warnings should either:
- Still be displayed (informational) but not block execution
- Or provide an override mechanism (e.g.,
"allowUnsafe": trueon the pattern)
Actual behavior
Security warnings are an independent layer that always requires approval, completely ignoring the allowlist. This generates dozens of prompts per session in repetitive workflows (CI-like commands, PR creation, branch management).
Impact
In workflows with multiple worktrees/terminals running simultaneously, the same command pattern requires approval repeatedly in each session, creating significant friction with no real security benefit (the user already authorized the pattern).
Environment
- Claude Code CLI (latest version)
- Linux (Ubuntu)
- Model: Claude Opus 4.6
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗