Permission allow-list saves entire heredoc content instead of command pattern

Resolved 💬 3 comments Opened Mar 5, 2026 by japaword Closed Mar 5, 2026

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:

  1. Are never reusable — they match only the exact heredoc content, which will never be run again
  2. Bloat the settings file — a file that should be a few KB can grow to 100KB+
  3. 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"
``

  1. 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

  1. 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)
``

  1. When prompted, select "Always allow"
  2. Check .claude/settings.local.json — the entire heredoc content is saved as a single permission entry
  3. 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 as Bash(/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

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗