// the default: nothing is hidden
Opening files inside your project is not something Claude Code asks about. The permissions table puts it plainly:
Auto mode goes further: "Read-only actions and file edits in your working directory are auto-approved", and its classifier's default allow list includes "Reading .env and sending credentials to their matching API". And nothing is denied out of the box: permissions.deny has "Default: unset". Whatever Claude opens becomes part of what the model sees. The agent can open your secrets file; nothing here says it will, and there is no attacker in this picture. It is simply not hidden until you hide it.
The block, and what it covers
The documented block is a Read deny rule. The settings page gives the paste-ready version, "to … stop it reading .env files":
Its scope is spelled out on the permissions page, and this is the sentence the whole video is about:
cat, head, tail, sed, and tee, and to the targets of Bash redirections such as > file and < file. They don't apply to a command that reads files without naming them, such as grep -r pattern . run from the directory that holds the file, or to arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself. For OS-level enforcement that blocks all processes from accessing a path, enable the sandbox.So ask Claude to print the file and the rule holds. That is the test most people run, and it passes. The built-in Grep tool is covered too, on a best-effort basis ("Claude makes a best-effort attempt to apply Read rules to all built-in tools that read files like Grep and Glob"). The gap is the shell.
What gets past it
Two ordinary things, neither of them malicious:
The first one does not even prompt. grep is in the built-in set of read-only commands that Claude Code "runs … without a permission prompt in every mode". What comes back is only the matching lines, not the whole file, but those lines are the keys. The second one does prompt in Manual mode, like any command outside the read-only set, and it exposes only what the script prints or logs. Neither is silent by design, and neither makes the deny rule useless. They are simply outside what it was built to cover.
The same applies to anything else that reaches the file without naming it: a test runner, a build step, a dev server that loads .env and echoes its config.
The fix: sandbox + rule + strict mode
The sandbox is what the docs point to for "OS-level enforcement". It is off by default (sandbox.enabled, "Default: false"). When it is on, "the operating system enforces that boundary for every Bash, PowerShell, or Monitor command and its child processes", and your Read deny rules come with it: "Paths and domains from both sandbox settings and permission rules are merged into the final sandbox configuration." A grep across the folder, or a script that opens the file, now hits the OS, not a string match.
Three parts, and each one matters:
- Keep the deny rule. The sandbox applies "only to Bash, PowerShell, and Monitor commands and their child processes". The built-in file tools ("Read, Edit, and Write use the permission system directly") are still stopped by the rule, not the sandbox.
- Strict mode. By default, when the sandbox blocks a command, "Claude analyzes the violation and may retry the command with the
dangerouslyDisableSandboxparameter". That retry goes through the normal permission flow (a prompt in Manual mode), but setting"allowUnsandboxedCommands": falseremoves it: "Claude Code ignores thedangerouslyDisableSandboxparameter, and every command Claude runs must run sandboxed unless you've listed it inexcludedCommands." The/sandboxpanel calls this Strict sandbox mode. - Deny your home-directory credentials too. The sandbox on its own does not hide much from reading: its default is "read access to the entire computer, except certain denied directories. Note that this default still allows reading credential files such as
~/.aws/credentialsand~/.ssh/." Add them todenyRead, or usesandbox.credentials.
Even then it is not airtight in every direction: commands you type yourself at the ! prompt run outside the sandbox in most sessions, and the sandbox is not a boundary for MCP servers or hooks.
Moving keys to your shell is not the fix
A tempting shortcut is to drop the .env and export the keys in your shell instead. The docs close that door: "sandboxed Bash commands inherit the parent process environment by default, including any credentials set there." The documented remedies are sandbox.credentials entries (which can unset or mask named variables for sandboxed commands) or CLAUDE_CODE_SUBPROCESS_ENV_SCRUB, which strips credentials from all subprocesses.
For contrast, not comparison. Cursor ignores .env* by default ("Cursor automatically ignores files in .gitignore and the default ignore list"), and its own docs note the terminal and MCP tools "cannot block access to code governed by .cursorignore". The two tools enforce this differently; this page is only about Claude Code.
Related: how coding agents edit your files covers the built-in file tools this rule applies to; why the model itself can't reach your data covers why it is the agent's tools, not the model, that matter here.
Sources: Claude Code docs — Permissions (permission table, read-only commands, Read and Edit rules), Permission modes (auto mode), Settings (edit a settings file), Settings reference (permissions.deny, sandbox.enabled, sandbox.allowUnsandboxedCommands, sandbox.filesystem.denyRead), Sandboxing (filesystem isolation, the unsandboxed retry escape hatch, scope). Verified 2026-09-26 against Claude Code v2.1.283 docs; these pages are version-gated and change often. Cursor: docs/reference/ignore-file, verified the same day.
One concept a week. Free.
The deeper, copy-paste version of each ToolCall short — in your inbox.
// total: 0.00 · spam: void · unsubscribe: one click
