Claude Code Can’t Read Your .env. Your npm test Still Can Claude Code deny rules block Claude from reading .env but do not stop the 176 distinct packages (208 installed copies) that can run when Claude types npm test in Express 5.3.0, according to Lydia Hallie of Anthropic's Claude Code team in her November 12, 2026 talk "Rethinking Claude Code Best Practices." Hallie's example shows a test runner loading dependencies that can call fs.readFileSync('.env') or reach SSH keys, because permissions decide whether a tool call runs, not what it can reach once running; Anthropic's own permissions documentation recommends enabling the sandbox, which is off by default and uses Seatbelt on macOS and bubblewrap on Linux and WSL2. The talk accompanied a stdlib-only script, npmreach.py, that reads package.json and the lockfile to count reachable packages without executing anything. A deny rule stops Claude from opening .env. It doesn’t stop the 176 packages Express’s tests can load. One stdlib script counts yours. A Claude Code deny rule keeps Claude from reading your .env. It does not stop the 176 packages that npm test can load in Express. The gap comes from Lydia Hallie’s talk “Rethinking Claude Code Best Practices”, posted to the AI Engineer channel on November 12, 2026. Lydia is on Anthropic’s Claude Code team. After reading this you can count the packages that run when your agent types npm test, and turn on the setting that makes your .env block hold for all of them. The talk updates Claude Code habits for auto mode and dozens of parallel sessions. On safety, she separates three layers: Her example: you add a deny rule so Claude never reads .env. Then Claude runs npm test, which auto mode allows. The tests execute code you didn't write. If one dependency is compromised, it can open .env or your SSH keys and send them away. Permissions decide whether a tool call runs, not what it can reach once running. The Claude Code permissions page draws the same line. Read and Edit deny rules cover Claude’s built-in file tools and the file commands Claude Code recognizes in Bash, such as cat, head, tail and sed. They do not cover "arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself." A test runner is exactly that: every file Mocha, Jest or Vitest loads can call fs.readFileSync '.env' . The page’s own advice: for OS-level enforcement, enable the sandbox. The sandboxing page adds that it is off by default, uses Seatbelt on macOS and bubblewrap on Linux and WSL2, and covers commands “and their child processes”. The deny rule checks the command Claude types. The sandbox walls in everything it starts. I counted “code you didn’t write” for one well-known project. I cloned Express 5.3.0, commit 43fca52 and resolved its dependency tree with npm install --package-lock-only --ignore-scripts. That writes only package-lock.json: no tarballs, no install scripts. Express commits no lockfile, so counts drift as versions change. Then a script reads the test command from package.json, maps mocha to its package, scans 124 test, example and library files for the packages they require, and walks the lockfile graph from there. Result: 176 distinct packages 208 installed copies can run when Claude types npm test in Express. The lockfile holds 403; the rest are linters, coverage tools and example-only dependencies. All 176 can run on every test, with your shell's access. It is a lockfile estimate, not a trace. A package in the graph may never load, which inflates it; a computed require name , a .mocharc file or a pretest script are invisible, which deflates it. I did not run the suite; needing no execution means you can point it at a repo you don't trust yet. npmreach.py is stdlib-only. It reads JSON and source files and runs nothing. bash /usr/bin/env python3"""npmreach.py - which npm packages run when your agent types npm test .Reads JSON and source files only. Executes nothing.usage: python3 npmreach.py