CLI-Driven AI: How Claude Code, Cursor, and Copilot CLI Expose Your Secrets
Agentic CLI tools read your files and ship them to a cloud LLM. Here are the concrete leak vectors and how to scan the read path, not just the write path.
Your senior engineers now type claude . in the terminal
Three years ago, the AI risk for a security team was a junior dev pasting code into ChatGPT. That problem is mostly solved with a browser extension and a written policy. The new problem is different and much harder: your most senior engineers are running agentic CLI tools that actively read files from disk and stream them to a frontier model, often hundreds of times per session.
Claude Code refactors a service. Cursor's agent mode rewrites a directory. Copilot CLI explains a shell error. Aider pairs on a feature. Codex CLI runs a long-horizon task. Every one of these tools is doing the same thing under the hood: read file, send to model, accept tool call, read more files. The developer never sees the bytes leave the machine.
This is fine, until one of those reads is .env, ~/.aws/credentials, or ~/.ssh/id_rsa. Then your AWS root key, your Stripe live key, or your production SSH identity is sitting in an LLM provider's context cache, possibly in training-eligible logs, possibly in a third-party MCP server's audit table.
The new normal: file-reading agents on developer laptops
Look at what a typical engineer at a Series B startup has running right now:
- Claude Code open in a project root, with permission to read any file in the workspace.
- Cursor with agent mode enabled, indexing the entire repo for context.
- GitHub Copilot CLI answering questions about shell commands, which means it reads whatever the shell error references.
- Aider in a side terminal, sometimes pointed at
~/scriptsor~/dotfiles. - An MCP server connecting one of the above to Postgres, GitHub, Linear, or a custom internal tool.
Each one is a legitimate productivity multiplier. Each one is also a wire from your local filesystem to a cloud LLM. Your existing controls — gitleaks in CI, secret scanning on push, a DLP browser extension — sit on the wrong side of that wire.
Five leak vectors you can reproduce in five minutes
1. The helpful .env read
The most common leak. A developer is wiring up a new service and asks the agent for help:
$ claude
> help me set this Stripe webhook up, the keys are in .env
[Claude Code] I'll read your .env file to see your current configuration.
[reading .env]
[reading webhook_handler.py]The entire .env — including STRIPE_SECRET_KEY=sk_live_…, DATABASE_URL=postgres://user:password@…, and the OpenAI key the team forgot was there — just shipped to a frontier model in plaintext, plus into the provider's context cache, plus into any MCP-server audit log on the path.
2. AWS credentials during a debug session
An engineer is debugging a boto3 script that 403s against S3. The agent asks to inspect credentials:
$ claude
> this script keeps getting AccessDenied on s3:GetObject
[Claude Code] Let me check your AWS configuration.
[reading ~/.aws/credentials]
[reading ~/.aws/config]
[reading deploy.py]~/.aws/credentials typically contains every profile the engineer has ever used: personal sandbox, the staging account, sometimes a production break-glass role. All of it lands in the model's context.
3. Hardcoded keys in source — with surrounding context
This one is the worst because the leak feels invisible. A file has a key that was never supposed to be there:
$ claude
> refactor billing_service.py to extract the Stripe logic
[Claude Code] Reading billing_service.py (412 lines)…Line 38 of that file is STRIPE_KEY = "sk_live_51H…". The agent sees it, refactors around it, and — depending on the model — may helpfully echo it back in its planned diff. Even if it doesn't, the full file with that key is now in the provider's logs.
4. git log -p archaeology
Agents love running git to understand history. This is one of the most damaging patterns:
$ claude
> when did we change the auth flow last?
[Claude Code] I'll check the git history.
[running: git log -p --all -- src/auth/]That command surfaces every patch ever applied to auth files. The credential someone committed in 2022, force-pushed out of main, and never rotated because "it's not in the current tree" — it's still in the reflog. git log -p --all hands it to the model verbatim.
5. SSH private key on a connection-error rabbit hole
An engineer can't ssh into a jumpbox. They ask the agent:
$ claude
> my ssh to bastion-prod hangs, can you check my config
[Claude Code] I'll look at your SSH setup.
[reading ~/.ssh/config]
[reading ~/.ssh/id_rsa]
[reading ~/.ssh/known_hosts]The private key itself goes to the model. Whether the model "remembers" it is the wrong question — the bytes are now in transit logs, provider caches, and any logging proxy in between.
Why gitleaks and truffleHog don't save you
Your team already runs gitleaks or truffleHog as a pre-commit hook or in CI. That is a good control. It is also entirely on the wrong side of the problem.
Those tools fire when you try to write a secret into version control. The CLI agent leak happens when you read a secret off disk and stream it to a third party. By the time gitleaks runs, the bytes have been in a frontier model's context for ten minutes. Pre-commit scanning protects your git history. It does not protect your laptop's filesystem from being exfiltrated one helpful tool call at a time.
~/.aws/credentials exfiltrates every profile in one tool call — and triggers no existing control on the path.The fix: scan the read path, not just the write path
The control you actually need sits between the agent and the filesystem. Every file the agent opens and every shell command it runs should pass through a scanner before the bytes reach the model provider. Zeuslock CLI implements this as a wrapper:
zeuslock cli wrap claude-code -- claude-code .The wrapper intercepts the agent's file reads and shell invocations, runs the Zeuslock three-layer detection pipeline (regex, NER, context heuristics) on the bytes, and applies the policy mode you've configured per finding type:
- Monitor — let the read through but log the finding to the Operator Console.
- Anonymize — replace the secret with a structurally valid fake (a real-looking
sk_live_stub) so the model's logic still works, but the live key never leaves. - Block — refuse the read entirely and return a clear error to the agent.
Rolling it out follows the same pattern we recommend everywhere: Monitor for two weeks so engineers see what their agents have been doing, Anonymize for three weeks to remove the live secrets without breaking workflows, then Block from week six onward.
Three additional controls that pair well
Directory allowlists
Most CLI agents will happily read any path the OS lets them. Configure the wrapper so the agent can only read inside the current repo root. ~/.env, ~/.aws, ~/.ssh, and ~/Documents become structurally unreachable. This alone closes the top three leak vectors above.
MCP server allowlisting
MCP is where this gets harder. A misconfigured Postgres MCP server can stream production rows into the model. A custom internal MCP server might expose Vault. Zeuslock CLI maintains a per-user allowlist of MCP servers and intercepts the JSON-RPC payloads on the wire — same detection pipeline, same three policy modes, applied to tools/call responses before they reach the agent.
Per-tool policy modes
You probably want different rules for Claude Code on a developer laptop than for an Aider job running in CI. The Operator Console lets you scope policies per binary, per host, and per environment. A CI runner can be Block-by-default for everything; an interactive Claude Code session might Anonymize secrets and Monitor PII.
What the tool vendors already do — and where the gap remains
Credit where it's due. Claude Code sandboxes to the project directory by default, prompts before reading anything outside it, and warns on common secret filenames. Cursor's agent mode has similar guardrails. Copilot CLI scopes its reads carefully. None of these tools want to leak your secrets.
The gap is the human in the loop. When the agent asks "can I read .env to help you set up Stripe?" the engineer clicks Allow because the request is reasonable in context. When the engineer pastes a stack trace that happens to include an Authorization header, no permission prompt fires at all. When git log -p surfaces a 2022 credential, the agent did exactly what was asked.
Tool-level sandboxing handles the obvious cases. A separate DLP layer is what handles the cases where the engineer said yes — and shouldn't have.
What to do this quarter
- Inventory which CLI agents your engineers actually use. Ask in #engineering, not in a survey. The answer will include tools security hasn't heard of.
- Run
zeuslock cli scan ~/on a volunteer's machine. Count the secrets that would be readable by any agent with home-directory access. The number will be uncomfortable. - Wrap one tool — start with whichever agent your highest-tenure engineer uses most. Set it to Monitor for two weeks.
- Review findings in the Operator Console. Share the top five with the engineering team. The conversation about Block week six writes itself.
- Extend to MCP servers, then to CI runners.
The principle is one line: add a secret scanner to the read path, not just the write path. Every other AI-DLP control your team buys this year follows from that.
Configuration walk-through for the CLI wrapper lives in the docs: Wrapping AI agents with Zeuslock CLI.
Protect your data from AI leaks
Try Zeuslock free — DLP for ChatGPT, Claude, Gemini and more.
Book a demo →