Sample agent-security review: Veilpipe's own repo
What this is. A free sample of our Claude Code agent-security review, run on our own public repository, Veilpipe (veilpipe.deemwar.com) at release v0.2.6. A review asks three questions: what could an agent session working in this repo reach, what was hidden from the model, and what had to be fixed. Nothing here comes from a private repository. Every key shown or planted is fake.
1. What an agent session could reach
A Claude Code session with the Bash tool runs commands as you. Anything your user account can read, the session can read and print into the conversation, where the model sees it and the transcript keeps it on disk. On a typical developer machine that includes:
- the shell environment (
env,printenv), where API keys often live; .envfiles in the project and its parents;- credential files in your home folder (cloud CLIs, package registries, SSH keys);
- shell history, which keeps commands you typed with a token in them;
- earlier Claude Code transcripts under
~/.claude/projects, which keep every secret a past session saw; - on macOS, the login Keychain, through the
securitycommand.
This repository itself:
| Check | Result |
|---|---|
| Tracked files | 30, none of them a .env or credential file |
| CI workflows | none (no .github/), so there are no CI secrets for a session to reach |
| Secret scanner (gitleaks, full history of the tree) | 43 hits, all test fixtures: 42 carry a fake marker in the value, 1 is a detection pattern (a "regex" field) in the vendored rules file |
So the repo does not hand an agent a secret. The exposure is the machine around it: whatever the session reads while you work.
2. What Veilpipe hid
Veilpipe runs inside the session. Before the model sees a prompt, a tool result or a file attachment, it rewrites each
secret-shaped value to a name such as $STRIPE_KEY, and it scrubs the transcript on disk the same way.
-
A
.envread. The model seesSTRIPE_KEY=$STRIPE_KEY, never the value. A later command that uses$STRIPE_KEYgets the real value filled in by Veilpipe in the child process, so the value is not in the command text, the model's context or the transcript. Checked live by the release regress before each tag (Claude Code 2.1.287 to 2.1.296): the model's reply and the transcript hold 0 copies of the fake key. -
Old transcripts.
veilpipe auditlists secrets already sitting in past transcripts and prompt history, by file and rule, counts only. On a throwaway config with one planted transcript holding a fake Stripe key and a fake database password:$ veilpipe audit found projects/-work-veilpipe/s1.jsonl STRIPE_KEY×1 DB_PASSWORD×1 1 file with secrets, 2 values (veilpipe audit --fix rewrites them; values are never printed) $ veilpipe audit --fix fixed projects/-work-veilpipe/s1.jsonl STRIPE_KEY×1 DB_PASSWORD×1 1 file with secrets, 2 values, 1 rewrittenAfter the fix the file held 0 raw copies of either value, only
$STRIPE_KEYand$DB_PASSWORD. A file written in the last 10 minutes is skipped (it may be a live session) and is reported as skipped. -
Commands that go around it. A model-written command that reads the Keychain entry or Veilpipe's own store files is refused before it runs, including when it is wrapped in
sh -c,bash -lc,ssh,$(...)or backticks. A message that only quotes such a command as text still goes through. -
Commands you run yourself.
veilpipe exec 'npm run deploy'fills in the stored names the command uses and shows$NAMEin its output instead of the value.
3. Fixes this review led to
Each of these shipped as a release.
| Release | Found | Fix |
|---|---|---|
| v0.2.2 | Auto-save stored the example keys from a README the agent read | Values are saved only from what you type or paste and your own environment (env dumps, .env reads); other file reads never are |
| v0.2.3 | An example key took the free name, so the real key became $STRIPE_KEY_2 | Values auto-save will not keep never take the base name |
| v0.2.5 | Uninstall removed the tool but said nothing about the saved secrets left behind | Uninstall says where the store is and how to delete it, including its Keychain key |
| v0.2.5, v0.2.6 | The store-read guard refused a chat message that only quoted a Keychain command | The guard reads quoted prose and heredoc bodies as text, unless they go to a shell or interpreter; a real read is still refused |
Limits we tell you about
- Detection is by pattern. It finds secret shapes, not meaning; an unusual home-grown token format needs a pattern
added (
veilpipe add). - A prompt-injected command can still use a stored
$NAMEin this project. It cannot print the value back into the session, but it can call the service the key opens, or send the key over the network, which Veilpipe does not see. Store only the keys a project needs. - In opencode, values are replaced and scrubbed but not saved to the store.
Get this review for your setup
This sample covers one public repo. The paid version reviews your own Claude Code setup: what your sessions can reach on your machines and CI, what is already in your transcripts, and the fixes, installed and checked.
Want this for your team? Our one-week Claude Code setup puts a team CLAUDE.md, hooks, skills and MCP servers in your repos, with secrets kept out of the model. Tell us about your setup: deemwar.com/consulting
Veilpipe: veilpipe.deemwar.com
Or install it as a Claude Code plugin: claude plugin marketplace add deemwar-products/veilpipe, then claude plugin install veilpipe@deemwar. For the veilpipe command used above, the README has one more line that links it.
Found this useful?
Related posts
Veilpipe is open source. Your coding assistant never sees the value it replaced.
When you debug a payment or a login with a coding assistant, the key tends to end up in the conversation. You paste it into a prompt, or the assistant runs `cat .env`, or a tool prints a connection string. Whatever reach…
The agent was in staging. The token it found was for production.
In April 2026, a coding agent working on a routine task deleted a startup's production database and all of its backups in a single API call. It took nine seconds ([Giskard](https://www.giskard.ai/knowledge/a-cursor-ai-ag…
Run several coding agents on one repo, and their pull requests collide
Running several coding agents in parallel feels like free throughput. Each one takes a task, opens a pull request, and moves on. The cost shows up later, at merge time.
Agents write the code. Review is now the bottleneck.
Coding agents can open pull requests faster than any team can read them. The work did not disappear. It moved to review.