Permission allow-list saves entire heredoc content instead of command pattern
Problem
When a user approves a Bash command that contains a heredoc (e.g., writing a file to /tmp), the entire command including all heredoc content is saved as a permission entry in .claude/settings.local.json.
This results in permission entries that are 7,000–15,000+ characters long, containing full markdown documents or Python scripts. These entries:
- Are never reusable — they match only the exact heredoc content, which will never be run again
- Bloat the settings file — a file that should be a few KB can grow to 100KB+
- Cause parse errors — the
:*pattern matching breaks on these entries, producing errors like:
````
"The :* pattern must be at the end. Move :* to the end for prefix matching"
- Skip the entire settings file — Claude Code reports "Files with errors are skipped entirely, not just the invalid settings", so all other valid permissions are also lost
Reproduction
- In a session, have Claude run a bash command that writes a large file via heredoc:
````
Bash(/tmp/some_analysis.md << 'EOF'
# Large Document
...thousands of characters...
EOF)
- When prompted, select "Always allow"
- Check
.claude/settings.local.json— the entire heredoc content is saved as a single permission entry - On next session launch, Claude Code fails to parse these entries and skips the settings file
Real-world example
These entries were saved in a real settings file (truncated):
"Bash(/tmp/migration_false_positive_analysis.md << 'EOF'\n# Migration Files: False Positive Risk Analysis for auth.uid() Replacement\n\n## Executive Summary\n\n**Risk Level: MODERATE-LOW with specific mitigations needed**\n\nThe migration files contain potential false positives... [7,358 chars total] ...EOF)"
8 such entries were found in one settings file, totaling ~77,000 characters of useless permission data.
Suggested fix
When saving a permission entry, distinguish between generalizable command patterns and highly specific one-shot commands:
- Option A: If the command exceeds a character threshold (e.g., 200–500 chars), default to "Allow once" instead of offering "Always allow", or warn the user
- Option B: Extract the command prefix as the pattern instead of saving the literal content. For example,
Bash(/tmp/analysis.md << 'EOF'...[10K chars]...EOF)could be saved asBash(/tmp/analysis.md *)or simply not persisted - Option C: Add a max-length validation when persisting permission entries, rejecting or truncating entries above a reasonable limit
- Option D: Detect heredoc patterns (
<< 'EOF',<< 'PYEOF', etc.) and never persist the content portion — only the command prefix before the heredoc
Environment
- Claude Code CLI
- macOS (Darwin 23.6.0)
- Settings file:
.claude/settings.local.json
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗