Read/cat on config files leaks API keys into conversation transcript

Resolved 💬 3 comments Opened May 11, 2026 by arturo-natella Closed Jun 12, 2026

Summary

When Claude Code is asked to diagnose any MCP or plugin problem, it commonly reads ~/.claude/settings.json (or .env files, ~/.aws/credentials, etc.) using cat via Bash or the Read tool. These files routinely contain plaintext API keys in env blocks. The default behavior displays the full file content, embedding the secret into the conversation transcript permanently.

Reproduction

  1. Configure an MCP plugin that requires credentials (Pinecone, OpenAI, Anthropic, etc.) with the key in ~/.claude/settings.json's env block
  2. Ask Claude to diagnose any plugin or MCP issue
  3. Claude runs cat ~/.claude/settings.json or Read ~/.claude/settings.json
  4. The full key value is now in conversation history (and any logs/exports/screenshots derived from it)

Real-world incident (2026-05-11)

Claude Opus 4.7, Claude Code session. User reported "the new chat says it can't access pinecone." Agent ran cat ~/.claude/settings.json to inspect plugin config; the full PINECONE_API_KEY value was displayed and now lives in the transcript. User had a global CLAUDE.md rule explicitly forbidding credential display. The rule was followed in conversation behavior elsewhere in the session but did not prevent the cat command. Required key rotation.

After being called out, the agent acknowledged the failure in its own words:

Fair. Concretely what I did wrong this stretch: 1. Exposed your PINECONE_API_KEY by running cat ~/.claude/settings.json — direct violation of your global "never display credentials" rule. The key needs to be rotated. 2. Spun a long speculative diagnostic about your Pinecone setup after you'd told me it had worked for weeks. Should have just asked for the actual error. 3. Kept producing verbose responses when the situation called for less. I'll stop. If you want me to do one specific thing (rotate-the-key procedure, anything actionable), say it and I'll do exactly that and nothing more. Otherwise it's a good time to end this session.

The model self-identified the violation but only after the user had already paid the cost (transcript pollution, mandatory key rotation). Self-acknowledgment after the fact is not a substitute for the tool refusing to display the value in the first place.

Impact

  • Secrets leak to whoever has access to the transcript (server-side logs, exported conversation files, screenshots, shared sessions, anyone who pages back through the chat)
  • Even with a global CLAUDE.md rule forbidding credential display ("NEVER print, log, echo, grep, or display API keys"), the rule is read by the model but not operationalized — reading the rule is not the same as the tool refusing to display the value
  • The model can recognize the violation in retrospect (see the quoted acknowledgment above) but the harm is already done by the time recognition happens

Expected behavior

The Bash tool and/or Read tool should detect when a target path is known to contain secrets and either:

  • Auto-redact secret-shaped values (sk-*, pcsk_*, pk_*, xoxb-*, JWTs, etc.) before display
  • Refuse the read without explicit confirmation, suggesting a redaction-aware alternative
  • Provide a built-in inspect-config style command that does the redaction

Known sensitive paths to handle by default: ~/.claude/settings.json, ~/.claude/settings.local.json, ~/.aws/credentials, ~/.aws/config, anything matching *.env*, ~/.kube/config, ~/.netrc.

Suggested fix

  • Pre-Bash hook for cat/grep/head/tail/less against known secret-containing paths
  • Read tool redaction pass when file path matches sensitive patterns OR file content matches secret-shaped strings
  • The CLAUDE.md rule should be enforceable at the tool layer, not just at the prompt-instruction layer — model adherence to instructions is unreliable; tool-level enforcement is not

View original on GitHub ↗

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