[Bug] "Allow always" saves full command text leaking secrets and causing parse errors

Resolved 💬 3 comments Opened Feb 11, 2026 by eprouveze Closed Feb 15, 2026

Bug Description

Bug: "Allow always" saves full command text as permission pattern, leaking secrets

Summary

When a user clicks "Allow always" on a permission prompt, Claude Code saves the entire command text verbatim into .claude/settings.local.json. For short commands like git add . this works fine, but for long commands (heredoc appends, inline scripts, commit messages with bodies, env-prefixed commands with API keys), this creates three problems:

  1. Secrets leak into the settings file — API keys, tokens, passwords, and JWTs embedded in one-off commands get persisted in plaintext
  2. Invalid patterns — Multi-line commands containing : followed by * (e.g., file paths like decisions/log.md) are parsed as pattern separators, causing settings file parse errors on next startup
  3. Unbounded file growth — The file accumulates hundreds of entries that will never match again, since they're full commands rather than reusable patterns

Reproduction

  1. Start a Claude Code session in a project
  2. Run a command that includes a heredoc, e.g.:

``
cat >> docs/sessions/log.md << 'EOF'
### Session entry with decisions/log.md reference
EOF
``

  1. When prompted, click "Allow always"
  2. Inspect .claude/settings.local.json — the entire heredoc is saved as one permission entry
  3. Restart Claude Code — parse error on decisions/log.md because :* inside the path is interpreted as a pattern

Real-world example

After ~2 months of use, my settings.local.json had grown to 311 entries (31KB) including:

  • Full Stripe live secret keys (sk_live_...)
  • Supabase service role JWTs
  • HeyGen API keys
  • Vercel API tokens
  • Supabase Postgres passwords
  • Complete git commit messages with heredocs
  • Inline Node.js scripts (50+ lines)
  • Bash for-loop fragments (while read f, do echo, done)

Two entries containing heredocs with paths like decisions/log.md caused a parse error that blocked Claude Code from starting.

Expected behavior

"Allow always" should extract a reusable prefix pattern from the command rather than saving it verbatim. For example:

| Command | Current (saved verbatim) | Expected (extracted pattern) |
|---------|--------------------------|------------------------------|
| cat >> docs/log.md << 'EOF'... | Full heredoc text | Bash(cat:*) or not saved at all |
| STRIPE_SECRET_KEY_LIVE=sk_live_... npx tsx script.ts | Full command with key | Bash(STRIPE_SECRET_KEY_LIVE=:*) or prompt to not save |
| git commit -m "$(cat <<'EOF'...full message...EOF)" | Full commit message | Bash(git commit:*) (already exists) |
| node -e "const OpenAI = require...50 lines..." | Full inline script | Bash(node -e:*) or not saved |

Suggested fixes

  1. Length limit: Don't save commands longer than ~100 characters as "Allow always" patterns. Show a warning instead: "This command is too long to save as a reusable pattern."
  2. Secret detection: Scan for patterns that look like API keys/tokens (e.g., sk_live_, sk-ant-, eyJhbG, whsec_) and refuse to save them, or redact to a prefix pattern.
  3. Pattern extraction: For commands with env var prefixes (KEY=value command), extract Bash(KEY=:*) instead of saving the full value.
  4. Deduplication: Before saving, check if a broader pattern already covers the command (e.g., git commit:* already covers any specific git commit -m "..." command).

Additional note

settings.local.json probably belongs in Claude Code's default .gitignore template, since it's machine-specific and can accumulate secrets. Currently it's not gitignored by default.

Environment

  • Claude Code (latest as of Feb 2026)
  • macOS, Warp terminal
  • ~2 months of active use on a Next.js project

Environment Info

  • Platform: darwin
  • Terminal: WarpTerminal
  • Version: 2.1.39
  • Feedback ID: b8c2b038-17e3-4070-a23e-a341dcb0c777

Errors

[{"error":"Error: ENOENT: no such file or directory, open '/Users/emmanuel/.claude/remote-settings.json'\n    at openSync (unknown)\n    at <anonymous> (/$bunfs/root/claude:12:947)\n    at B2 (/$bunfs/root/claude:11:49917)\n    at readSync (/$bunfs/root/claude:12:875)\n    at $2 (/$bunfs/root/claude:6133:113)\n    at Q5 (/$bunfs/root/claude:6135:226)\n    at S9A (/$bunfs/root/claude:178:8273)\n    at s7R (/$bunfs/root/claude:178:8436)\n    at ZFD (/$bunfs/root/claude:179:1202)\n    at H9T (/$bunfs/root/claude:179:2080)","timestamp":"2026-02-11T13:15:11.567Z"},{"error":"Error: NON-FATAL: Lock acquisition failed for /Users/emmanuel/.local/share/claude/versions/2.1.39 (expected in multi-process scenarios)\n    at wVR (/$bunfs/root/claude:3626:2098)\n    at DjA (/$bunfs/root/claude:3626:1209)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-11T13:15:…

Note: Content was truncated.

View original on GitHub ↗

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