Tired of Claude Code Leaking Your Secrets? I Built Guardrails for It
If you use Claude Code a lot, you've probably seen this:
“Let me inspect the configuration.”
And then:
cat .env
Great.
Your database password or API key is now sitting in the conversation.
Claude apologizes.
You rotate the key.
A few sessions later:
grep SOMETHING ~/.pgpass
Same story.
After doing this enough times, I got tired of relying on:
“Claude, please remember not to print secrets.”
So I built claude-code-guardrails.
It's a small open-source set of user-level hooks for Claude Code.
Stop the leak before it happens
The main hook, deny-secrets, intercepts Bash tool calls before execution.
If Claude tries something like:
cat .env
or attempts to expose .pgpass, private keys, credential files, environment variables, or other secret-looking data, the hook can deny the command before Bash runs it.
Conceptually:
Claude Code
↓
Bash command
↓
PreToolUse hook
↓
secret?
↓
NOPE.
It's not a security sandbox and it's definitely not 100% protection.
But it stops a surprisingly large class of accidental:
“Oops, I shouldn't have printed that.”
And there's a canary
The second hook is session-heartbeat.
Long Claude Code sessions can gradually lose earlier instructions after context compaction.
The dangerous part is that Claude doesn't suddenly stop working.
It keeps answering.
It keeps sounding confident.
It keeps writing code.
Only the decisions start getting... weird.
So the heartbeat acts as a canary for context degradation.
It injects a timestamp and turn counter into every prompt. If Claude suddenly stops returning the expected marker, that's an early signal that the session may be starting to drift.
Maybe it's time to kill the session and start a fresh one — before Claude starts confidently producing nonsense.
Is it proof that the context is healthy?
No.
It's a canary.
That's the point.
That's basically it
This is not intended to replace proper secret managers, filesystem permissions, gitleaks, trufflehog, or real sandboxing.
It's just a pragmatic extra layer for problems I kept encountering myself.
MIT licensed. Download it, modify it, add your own rules, break it, fix it, send a PR.
Full documentation and installation instructions are on GitHub:
https://github.com/ineron/claude-code-guardrails
Sometimes AI coding agents don't need another prompt.
They need a fence.
And maybe a canary.
Top comments (15)
Secret exposure is not just a model problem; it is a workspace design problem. The safer setup is least-privilege files, explicit secret boundaries, scrubbed logs, and tests that fail when sensitive values appear in prompts or tool output.
Totally agree - good workspace design should be the first line of defense.
My only addition is that I wouldn't rely on discipline alone. Once an autonomous agent is making hundreds of tool calls, sooner or later someone forgets a boundary, a script exposes something unexpected, or the agent finds a path nobody anticipated.
That's how I see guardrails: not a replacement for least privilege and proper secret handling, but an additional safety net when those controls inevitably aren't perfect.
Defense in depth applies to AI agents too.
Defense in depth is the right frame. The workspace boundary lowers the blast radius; the scanner catches the failure mode where something still crosses it. The part I would add is an explicit "public output" gate, because many leaks happen at the final summary layer after all the tool calls were technically allowed.
You're right - and I think the more important boundary is actually earlier than the output.
Once a secret reaches the model through
Read, Bash, or another tool, it has already left the workspace and entered the model context. An output gate would only catch it downstream.So I pushed the guard one step earlier: the repo now protects both
BashandReadviaPreToolUse, blocking the secret before it enters context.Still not perfect - MCP and other content-producing tools need their own guards - but I think this is the right boundary to defend.
Moving the guard before context entry is the right direction. For Maps or local SEO automation, I think the same rule applies to customer/location data: the agent should not even read raw credentials, private review exports, or owner-only GBP artifacts unless the task actually requires that scope. Output filtering is a last net, not the boundary.
Exactly. I think that's the honest takeaway: we can keep adding layers and closing more failure modes, but we'll probably never get to 100%.
Yes. The honest target is not perfect prevention; it is reducing the number of places a secret can enter context and making the remaining paths visible enough to audit. Once the model has seen the value, you are already doing incident response, not prevention.
Moving the guard before context entry is the right direction. Output scanning is useful as a last receipt, but the cleaner boundary is preventing the secret from becoming model-readable state in the first place.
Agreed. Discipline is not a control; it is a hope that nobody forgets the control. The useful guardrail is the one that still fires when the workspace is messy, the script is surprising, or the agent finds a path the designer did not anticipate.
Exactly. I think we've converged on the same principle: prevent as early as possible, make the remaining paths visible, and never pretend the system is complete.
That is the whole difference. Discipline depends on everyone remembering the rule under pressure. A control makes the wrong path harder or impossible. For agent work, I trust boring boundaries a lot more than polished instructions.
Exactly. Prompts guide behavior. Boundaries enforce it.
Imo, the better approach would be middleware between the agent and the MCP it uses to execute. Essentially a private/public key encryption, so it sees gibberish, it returns gibberish, it parses with your private key and it executes as if it had real credentials
I like that model for established credentials, and I use a similar approach in my own agent architecture: the agent gets a capability/reference, while the real credential stays behind the execution layer.
The problem I'm trying to cover is a bit earlier in the lifecycle though. During active development, .env often contains many secrets and new credentials are constantly being added before they have a proper broker/MCP abstraction around them. One accidental Read of the file can expose all of them at once.
So I see the two approaches as complementary: hide mature credentials behind capabilities whenever possible, and still guard raw secret files because development is exactly where those boundaries are incomplete.
Yeah it's a bit difficult running a linter to detect it, you'd need to tag everything as secret before you start using AI, or you'd need to run an airgapped local model to tag it... Hm. An embedding model might work for that? I thought before to maybe look into setting up something like it, you use a tiny, fast model locally to run pre-processing, akin to how a draft model works for outputs, but for inputs, so if user gives credentials, it gives the cloud model an alias, if the tool pulls IPs, or secrets, it aliases them. You send apple@apple.com it sees it, flags it, filters it, the cloud model gets pear@peach.com so credentials are kept safe. If trained specifically for the task, it's not too impossible for a model to also act as a gatekeeper for any security risk code, etc. where it just flags 'common mistakes' like unrestricted endpoints, plaintext credentials, etc. and recommends the cloud model fixes it. That way it's never actually 'doing' anything, other than detection and aliasing, with such a small scope, even a 250m model train for that exact purpose can do the job. Unfortunately, I dont have the resources to train 1 from scratch, but I think that's the next evolution of agentic coding safety, a localized filter middleman, which if trained appropriately, can also improve code quality, by being a standards checker.