[BUG] Bash security heuristic false-positives on Next.js dynamic route brackets [param] — blocks write commands despite allow rules
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
The Bash security heuristic layer treats [param] in file paths as glob patterns and blocks all Bash write commands targeting those paths with:
"Glob patterns are not allowed in write operations. Please specify an exact file path."
This is a critical false positive for Next.js App Router projects, where [param] dynamic route directories (e.g., [barId], [articleId], [userId]) are fundamental to the framework's file-based routing.
The block cannot be bypassed via settings.json allow rules, PreToolUse Hooks, or any quoting/escaping of the path — it operates on a separate heuristic layer that inspects the raw command string.
Systematic Test Results (50 patterns tested)
We conducted a comprehensive 50-pattern test to isolate the exact triggering conditions.
Trigger condition: Bash write command + glob characters in path
| # | Command | Glob char | Write? | Prompted? | Message |
|---|---------|-----------|--------|-----------|---------|
| 1 | touch .../app/test-plain.tmp | None | Yes | No | — |
| 2 | touch .../app/test-{a,b}.tmp | { } | Yes | Yes | Glob patterns... |
| 3 | touch .../app/test-star*.tmp | * | Yes | Yes | Glob patterns... |
| 4 | touch .../app/test-question?.tmp | ? | Yes | Yes | Glob patterns... |
| 5 | touch .../app/test-tilde~.tmp | ~ | Yes | No | — |
| 6 | touch .../app/test-dollar$var.tmp | $ | Yes | Yes | Shell expansion syntax... |
| 7 | touch .../app/test-backtick\.tmp | ` | Yes | **No** | — |touch .../app/test-paren(1).tmp
| 8 | | ( ) | Yes | **No** | — |touch .../app/test-space name.tmp
| 9 | | space | Yes | **No** | — |touch .../app/test-excl!.tmp
| 10 | | ! | Yes | **No** | — |touch .../app/test-hash#.tmp
| 11 | | # | Yes | **No** | — |touch .../app/test-at@.tmp
| 12 | | @` | Yes | No | — |
Read vs Write with [barId]
| # | Command | Read/Write | Prompted? |
|---|---------|-----------|-----------|
| A | ls .../bars/[barId] | Read | No |
| F | cat .../bars/[barId]/route.ts | Read | No |
| B | touch .../bars/[barId]/file.tmp | Write | Yes |
| E | cp .../bars/[barId]/a .../bars/[barId]/b | Write | Yes |
| 50 | chmod 644 .../bars/[barId]/file.tmp | Write | Yes |
| 15 | echo "test" > .../bars/[barId]/file.tmp | Write | Yes |
| 47 | echo "test" >> .../bars/[barId]/file.tmp | Write | Yes |
Tools that are NOT affected (same [barId] path)
| # | Tool/Method | Prompted? |
|---|------------|-----------|
| D | Write tool | No |
| 48 | Edit tool | No |
| 37 | node -e "fs.writeFileSync('.../[barId]/...', ...)" | No |
| 26 | cat <<'EOF' > .../[barId]/file.tmp (heredoc) | No |
| 40 | mkdir .../bars/%5BbarId%5D/... (URL-encoded) | No |
Shell expansion variants
| # | Command | Prompted? | Message |
|---|---------|-----------|---------|
| 13 | touch .../test-$(echo hi).tmp | Yes | Shell expansion syntax... |
| 16 | touch ~/Documents/.../file.tmp | No | — |
| 17 | touch $HOME/Documents/.../file.tmp | Yes | Contains simple_expansion |
Structural commands
| # | Command | Prompted? | Message |
|---|---------|-----------|---------|
| 30 | if [ -d ... ]; then echo "exists"; fi | No | — |
| 31 | for f in .../test-*.tmp; do echo "$f"; done | Yes | Unhandled node type: string |
| 32 | for f in a b c; do echo "$f"; done | Yes | Unhandled node type: string |
| 33 | while false; do echo "x"; done | No | — |
Key Findings
- The heuristic inspects the raw command string for glob characters (
[,],*,?,{,}) and shell expansion ($), then blocks if the command is classified as a "write operation" settings.jsonallow rules have no effect —Bash(mkdir:*),Bash(touch:*),Bash(cp:*)are all in the allow list but the heuristic fires on a separate layer- Quoting does not help — single quotes, double quotes, backslash escaping all still trigger the prompt
- Variable indirection does not help —
DIR=".../[barId]" && mkdir "$DIR"still triggers - Write/Edit tools are unaffected — the heuristic only applies to Bash tool commands
forloops always trigger regardless of content ("Unhandled node type: string")
Impact on Next.js Development
Next.js App Router uses [param] directories as a core routing convention:
app/
├── bars/
│ └── [barId]/ ← Every Bash write command here triggers a prompt
│ ├── route.ts
│ ├── menus/
│ │ └── [menuId]/ ← Also triggers
│ ├── articles/
│ │ └── [articleId]/ ← Also triggers
│ └── events/
│ └── [eventId]/ ← Also triggers
In a typical Next.js project, mkdir -p, touch, cp, echo > into these directories happen dozens of times per session. Each one stalls the session waiting for manual approval.
What Should Happen?
[param]in file paths should not be treated as glob patterns when the path is quoted or escapedsettings.jsonallow rules should take precedence over the heuristic layer- At minimum, the heuristic should check whether the bracketed content actually looks like a glob range (
[a-z],[0-9]) vs. a named parameter ([barId],[userId])
Workarounds (all unsatisfactory)
--dangerously-skip-permissions— disables all safety, too broad- "Yes, and always allow access to [barId]/" — must be done per-directory, new
[param]dirs trigger again - Use
Write/Edittools instead of Bash — not always possible (mkdir, cp, chmod) - URL-encode brackets as
%5BbarId%5D— creates wrong directory names
Related Issues
- #36341 (closed as duplicate)
- #32212 (closed as stale — not fixed)
- #34106
- #35183
Error Messages/Logs
Glob patterns are not allowed in write operations. Please specify an exact file path.
Shell expansion syntax in paths requires manual approval
Contains simple_expansion
Unhandled node type: string
This command requires approval
Steps to Reproduce
- Create a Next.js App Router project with
[param]directories - Add
Bash(mkdir:*),Bash(touch:*),Bash(cp:*)tosettings.jsonallow list - Run any Bash write command targeting a
[param]path:
``bash``
mkdir -p ./app/api/bars/\[barId\]/portal
touch ./app/api/bars/\[barId\]/route.ts
cp ./source.ts ./app/api/bars/\[barId\]/dest.ts
- Observe: permission prompt appears despite allow rules
Claude Model
Opus
Is this a regression?
No, this has been present since at least v2.1.71
Claude Code Version
2.1.98 (Claude Code)
Platform
Anthropic API
Operating System
macOS 26.3.1
Terminal/Shell
VS Code integrated terminal (zsh)
Additional Information
This was tested with a comprehensive 50-pattern test matrix. The project is a Next.js monorepo with both user-facing and admin apps, both using App Router's [param] convention extensively.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗