Claude can read your secrets - Dejan
Dejan
SubscribeSign in
Claude Code can read your plaintext secrets<br>Filing this under "well, of course".
Dejan<br>Aug 03, 2026
Share
During a routine task, Claude executed a grep for few secrets and read them. I caught it, accidentally.<br>⏺ Bash(ls -R applications/nowwhat-dot-page/infra/ | head -20; grep -rn "CAPTCHA_SECRET\|JWT_SIGNING_KEY" applications/nowwhat-dot-page/infra/ | head)<br>⎿ kamal-dev
applications/nowwhat-dot-page/infra/kamal-dev:<br>config
applications/nowwhat-dot-page/infra/kamal-dev/config:<br>deploy.yml<br>applications/nowwhat-dot-page/infra/kamal-dev/.kamal/secrets:9:JWT_SIGNING_KEY=<br>applications/nowwhat-dot-page/infra/kamal-dev/.kamal/secrets:23:CAPTCHA_SECRET=<br>applications/nowwhat-dot-page/infra/kamal-dev/config/deploy.yml:65: - JWT_SIGNING_KEY
That’s a problem. Especially when using an LLM that retains data for 30 days.<br>I did think this might happen. The permissions.deny rules in the project’s .claude/settings.local.json were not empty:<br>"permissions": {<br>"allow": [<br>...<br>],<br>"deny": [<br>"Read(**/secrets/**)",<br>"Read(applications/nowwhat-dot-page/infra/kamal-dev/.kamal)",<br>"Read(applications/nowwhat-dot-page/infra/kamal-dev/.kamal/**)",<br>...
Why was the read allowed?
The rules above are activated when Claude Code invokes the Read(*) tool. This is a good approach to deny Claude access to reading a file using that specific tool. It falls short when the goal is to deny Claude access to the file no matter what.<br>There is a separate tool for shell commands - Bash(*). Deny rules there fall short of a full solution as you would need to list all possible ways a file can be read (sed ‘‘ secrets; xxd secrets; strings secrets; and so on).<br>The workaround
There is a way to prevent Claude doing arbitrary reads. The docs explain:<br>The Bash sandbox let’s … you define which files and network domains commands can touch, and the operating system enforces that boundary for every Bash command and its child processes.
A few few lines in .claude/settings.local.json. It helps - but it’s not the fix.<br>The fix
Do no put plaintext secrets in your repository.<br>“But the secrets file is in a .gitignore”, and “my AI agent was explicitly instructed to never read the secrets”. Doesn’t matter. Just don’t.<br>My trust in denylists as a guarantee is gone. The secrets are now in a vault.<br>Thanks for reading! Subscribe for free to receive new posts.
Subscribe
Share
Discussion about this post<br>CommentsRestacks
Ready for more?
Subscribe
© 2026 Dejan · Privacy ∙ Terms ∙ Collection notice<br>Start your SubstackGet the app<br>Substack is the home for great culture
This site requires JavaScript to run correctly. Please turn on JavaScript or unblock scripts