[BUG] `--allowedTools` has no effect when permission bypass flags are active
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
--allowedTools does not restrict tool availability when used alongside --dangerously-skip-permissions or
--permission-mode bypassPermissions. The model can call any tool regardless of what's specified in the allowed list.
This means there is no secure non-interactive mode for Claude Code. Headless consumers (daemons, cron jobs, CI pipelines) require a permission bypass to avoid hanging on interactive prompts, but enabling any bypass defeats tool restrictions entirely.
What Should Happen?
--allowedTools should be enforced regardless of permission mode. Specifically:
- Tools in the allowed list: auto-accept without prompting (non-interactive safe)
- Tools not in the allowed list: hard-reject (return error to model, do not execute)
This would allow a secure non-interactive mode where the tool boundary is real, not advisory.
Error Messages/Logs
Steps to Reproduce
```bash
# Expected: Bash tool should be unavailable (only Read, Grep, Glob allowed)
# Actual: Bash works, model runs the command successfully
echo 'Run echo BASH_WORKS using the Bash tool' \
| claude --allowedTools "Read Grep Glob" --dangerously-skip-permissions -p
# Same result with --permission-mode:
echo 'Run echo BASH_WORKS using the Bash tool' \
| claude --allowedTools "Read Grep Glob" --permission-mode bypassPermissions -p
Both commands succeed. The model calls Bash and executes the command despite Bash not being in --allowedTools.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.111
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
Actual behavior
Both --dangerously-skip-permissions and --permission-mode bypassPermissions override --allowedTools entirely. All toolsare available regardless of the allowed list. --disallowedTools is similarly defeated.
Secondary issue: Bash command patterns are cosmetic
Separately, --allowedTools "Bash(python3:,cat:,ls:)" does not restrict which commands execute inside Bash. The pattern filter applies to tool name matching, not command content. A model given Bash(cat:) can still run sed -i, curl, rm -rf, etc.
Impact
- No secure headless mode exists. Every non-interactive Claude Code deployment (daemons, cron, CI) has unrestricted tool access.
- Defense-in-depth is illusory. Developers who specify --allowedTools alongside a permission bypass believe they have tool restrictions. They do not.
- Prompt injection attack surface is maximized. Any agent processing external input (chat messages, emails, documents) with a permission bypass has no tool boundary between injected content and arbitrary code execution.
Suggested fix
A new permission mode (e.g., --permission-mode allowlistOnly) that:
- Does not prompt the user (works non-interactively)
- Auto-accepts tool calls matching --allowedTools patterns
- Hard-rejects tool calls not matching --allowedTools (returns error to model, does not execute)
- Enforces Bash command patterns as actual restrictions on command content, not just tool name matching
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗