Back to Insights
14 Oct 20264 min readAI Agents

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;
  • .env files 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 security command.

This repository itself:

CheckResult
Tracked files30, none of them a .env or credential file
CI workflowsnone (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 .env read. The model sees STRIPE_KEY=$STRIPE_KEY, never the value. A later command that uses $STRIPE_KEY gets 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 audit lists 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 rewritten
    

    After the fix the file held 0 raw copies of either value, only $STRIPE_KEY and $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 $NAME in its output instead of the value.

3. Fixes this review led to

Each of these shipped as a release.

ReleaseFoundFix
v0.2.2Auto-save stored the example keys from a README the agent readValues are saved only from what you type or paste and your own environment (env dumps, .env reads); other file reads never are
v0.2.3An example key took the free name, so the real key became $STRIPE_KEY_2Values auto-save will not keep never take the base name
v0.2.5Uninstall removed the tool but said nothing about the saved secrets left behindUninstall says where the store is and how to delete it, including its Keychain key
v0.2.5, v0.2.6The store-read guard refused a chat message that only quoted a Keychain commandThe 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 $NAME in 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.

AI agentscoding agentssecurity