Built-in guard against Claude reading/printing credential-file contents (.env, shared.env, etc.)

Open 💬 0 comments Opened Jul 12, 2026 by sckyroom

Title: Built-in guard against Claude reading/printing credential-file contents (.env, shared.env, etc.)

Summary

Request a first-party safeguard in Claude Code that blocks the model from executing tool calls that would read the full contents of an obvious credential file (.env, .env.*, files containing secret/credential, etc.) via unsafe patterns — cat/head/tail/less/more, a grep without -o, or the Read tool — so a printed secret becomes structurally harder rather than depending on the model remembering a written instruction every time.

What happened

Over one working relationship, the same failure shape recurred multiple times despite explicit, repeated project-level instructions (in CLAUDE.md) telling Claude never to Read a credential file directly and always to extract single values via grep -o '^KEY=.*' file | cut -d= -f2-, never a bare cat/grep:

  • Once via the Read tool used directly on a .env file (should have used a safe extraction command instead).
  • Twice via a grep command that omitted the -o flag (e.g. grep -n "SOME_KEY" .env), which prints the entire matching line — KEY=value — including the secret value, instead of just the key name.

Each time, the credential then had to be treated as compromised and rotated, since it had been printed in full into the conversation transcript.

Why a project-level instruction wasn't enough

CLAUDE.md-style instructions are text the model is expected to recall and apply correctly on every single tool call, in the moment, regardless of what else it's focused on in that turn. That's a single point of failure — one lapse (e.g. reaching for a familiar grep pattern while focused on a different task) fully defeats it, and it happened more than once in the same documented shape even after the first occurrence was written up as a lesson-learned.

Workaround currently in place

As a stopgap, I had Claude build a local PreToolUse hook (Bash + Read matchers) that pattern-matches command text / file paths against common credential-file naming conventions (.env, .env.*, shared.env, *credential*, *secret*) and denies the tool call outright if it would cat/head/tail/less/more the file or grep it without -o. This works, but it's user-space config: something I had to know to ask for, that lives only on this machine, that could be silently deleted/disabled/misconfigured, and that a new user wouldn't have by default.

Feature request

Build the equivalent of this protection into Claude Code itself, ahead of the point where a tool call result reaches the model — e.g.:

  • Detect common credential-file naming patterns (.env/.env.*, *secret*, *credential*, cloud-provider key file conventions) and refuse cat/head/tail/less/more/unflagged-grep/raw Read against them by default, independent of any user-authored hook.
  • Surface a clear, actionable error back to the model (not just a silent failure) so it can self-correct to the safe extraction pattern, same behavior a well-written hook already provides.
  • Ideally also cover common secret-shaped strings appearing in other tool outputs (e.g. an API response body that happens to embed a token) — this is a related but distinct gap a Bash/Read-specific guard doesn't close, and was a separate incident in my own case.

Happy to share more detail on the exact recurrence pattern / hook implementation if useful for scoping this.

View original on GitHub ↗