Heredoc-containing Bash commands split into multiple permission entries
Description
When a user approves a Bash command containing a heredoc (e.g., git commit -m "$(cat <<'EOF'...)""), the permission parser splits on newlines and saves each line of the heredoc body as a separate permission entry in settings.local.json.
Expected Behavior
The entire command should be saved as a single permission entry, or the heredoc should be collapsed into a wildcard pattern (e.g., Bash(git commit *)).
Actual Behavior
A single git commit approval creates 10+ entries, one per line of the commit message body. Examples of corrupted entries found:
"Bash(MY ecosystem?\" Leading to either premature implementation...)",
"Bash(- Layer 1: Permission deny rules...)",
"Bash(versions\" error during migration script execution...)",
"Bash(__NEW_LINE_e5fdbe8e55f17f4e__ echo \"\")"
Internal __NEW_LINE_<hash>__ markers also leak into saved permissions.
Reproduction
- Start a Claude Code session
- Ask Claude to make a git commit with a multi-line message using the HEREDOC pattern:
```bash
git commit -m "$(cat <<'EOF'
Some commit title
Multi-line body with details.
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
```
- When prompted, approve the command ("Always allow")
- Inspect
<project>/.claude/settings.local.json— each line of the commit message is a separate entry
Impact
- Permission list bloats with invalid entries (observed 43 corrupted entries in a single session)
- Entries like
Bash(for:*),Bash(EOF),Bash(exec)could inadvertently grant overly broad permissions - Ongoing issue: corruption recurs every session since heredoc commits are common
Workaround
We've built governance hooks that detect and auto-repair these entries post-write (permission_validator.py, settings_guard.py, permission_auto_sanitizer.py), but this is reactive — corruption still hits disk before being cleaned up.
Environment
- Claude Code CLI (latest as of Feb 2026)
- WSL2, Ubuntu
- Occurs with both
settings.local.json(project) and could affectsettings.json(global)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗