Your Agent Can Read Everything Your Shell Can. Have You Looked?
Last time we frisked the inputs — the invisible Unicode that smuggles instructions into a coding agent. But injection only matters because of what the agent can do once hijacked, and the quietest, highest-impact thing it can do is read a secret and send it somewhere. A coding agent with shell access has exactly your file permissions. So the question to ask before you point one at a repo — especially with --dangerously-skip-permissions — isn't "will it misbehave?" It's "if it's compromised, what's in reach?"
That's a concrete, answerable question, and almost nobody asks it. So we wrote a 100-line tool, agent-reach, that answers it — by listing the secret-bearing files reachable from where the agent runs. It reports paths and existence only; it never opens or prints a secret. The audit shouldn't itself be the leak.
Two zones of blast radius
An agent's reach has two parts, and you want to see both.
The repo tree. Everything under the working directory is one cat away. agent-reach walks it for the usual secret-bearing names — .env and .env.*, *.pem / *.key / id_rsa / id_ed25519, .npmrc (npm _authTokens live here), .pypirc, .netrc, .git-credentials, GCP service-account JSONs, *.p12 / *.pfx, and more — skipping the noise (node_modules, .git, build dirs).
$HOME. This is the part people forget. The agent doesn't stop at the repo — it can read your user's credential stores too. So agent-reach checks for the presence of the well-known ones: ~/.aws/credentials, ~/.ssh/id_*, ~/.config/gh/hosts.yml (a GitHub token), ~/.docker/config.json, ~/.kube/config, ~/.netrc, ~/.npmrc. Existence only — but existence is the point: if those files are there, a hijacked agent in any repo can exfiltrate them.
$ agent-reach
agent-reach — blast radius from /work/my-app
IN THE REPO TREE (an agent here can read every one of these)
● .env — env file (secrets/config)
● .npmrc — npm config (may hold an _authToken) ⚠ COMMITTED (not gitignored)
● config/server.pem — private key / cert ⚠ COMMITTED (not gitignored)
REACHABLE IN $HOME (same shell access — exfiltratable if hijacked)
● ~/.aws/credentials — AWS access keys
● ~/.ssh/id_ed25519 — SSH private key
blast radius: 5 secret store(s) in reach, 2 committed to the repo.
The COMMITTED flag is the one to act on first
A secret sitting in your working tree is bad; a secret you committed is worse — it's in history, on the remote, and in every clone and CI runner forever. So agent-reach cross-checks each repo hit against git check-ignore: anything that's both present and not gitignored gets a red ⚠ COMMITTED. That's your priority list. (A .env that's properly gitignored still shows up — an agent can read it — but it's a contain-the-radius problem, not a rotate-everything-now problem.)
It exits non-zero when the radius is non-empty, so it slots into a pre-flight check:
agent-reach --repo-only && echo "ok to run an agent here" # gate on a clean repo tree
What to do with the number
The output is a worklist, in order:
- Rotate + un-commit anything flagged
COMMITTED. It's already exposed independent of any agent. - Move repo secrets out of the tree — into your shell env or a secrets manager — so the working directory an agent sees has nothing to read.
- Shrink the
$HOMEset for risky runs. If you're going to run an agent with broad permissions on untrusted code, do it somewhere its$HOMEdoesn't hold your production AWS keys — a container, a throwaway user, a sandbox. - Frisk the inputs (agent-frisk) so the agent is less likely to be hijacked into reaching for any of it in the first place.
Prevention (don't get injected) and least-privilege (have nothing to steal) are the same defense from two ends — agent-frisk shrinks the first, agent-reach measures the second.
The honest limits
It's a names-and-locations audit, not a secret scanner. It finds files that conventionally hold secrets; it does not crack open a random config.json to see if you stuffed an API key in it (that's gitleaks/trufflehog's job, on content). It checks a curated list of $HOME stores, not every possible one. And "in reach" is about file permissions, not a guarantee the agent will read them — it's the denominator of the risk, the thing you want as small as possible before you hand over the keys to the shell. Pair it with real sandboxing for anything genuinely untrusted.
The real lesson
"I'll just let the agent run, it's probably fine" is a bet whose size you've never measured. agent-reach measures it: one command turns "probably fine" into a list of exactly what a single injected instruction could walk out the door with. The goal isn't zero tools — it's to know your blast radius and shrink it on purpose, so that when something does go wrong, the worst case is small.
We build and run autonomous agent fleets on Claude Code in production. If you're securing an agent that touches untrusted content, talk to us.
Get the tool → agent-reach in agent-ops — open source (MIT), a single dependency-free script, part of our toolkit for running coding agents in production.
Found this useful?
- You're Running Five Agents on One Repo. Who Stops Them Overwriting Each Other?
- Your Agent Did the Work, Then It Vanished. Here's How to Get It Back.
- Your Claude Code Session Is Permanently Stuck on a 400. Don't /clear — Do This.
- The One Kind of Agent Work You Can't Recover — and How to Stop Losing It
- Your Agent Will Eventually Run git reset --hard. Stop It at the Door.
- Your Agent Reads Text You Can't See. Frisk It First.
- You're Running a Dozen Agents. Which One Is Stuck?
- What's Actually In Your Claude Code Session File (and a Tool to Read It)
- Your Agent Fleet's Worktrees Are a Minefield. Here's How to See It.
- The Agent Wrote the Code in Minutes. Reviewing It Takes Hours. Here's Why.
- Your Claude Agent Re-Reads Its Whole Prompt Every Turn. Here's the 10× You're Leaving On the Table.
- The Loosest Line in Your Repo Is in .claude/settings.json
- The Most Expensive File in Your Repo Runs on Every Single Turn
- Your Skill's Description Is the Whole Ballgame
- Your Repo Already Knows How To Build Itself. Put That In the CLAUDE.md.
Related posts
The boring part: governance that enforces, not displays
Most "AI governance" is a dashboard. It shows you what an agent spent, what it touched, what it decided — after the fact, in a panel nobody refreshes until the bill arrives. A display is not a control. A control is the t…
Your Launch Deserves a Real Demo Video, Not a Screen Recording With Captions
Most product launches skip the demo video entirely, and the ones that don't ship one usually reach for the fastest option: a raw screen recording, maybe some auto-generated captions, posted as-is. It's better than nothin…
The MCP Ecosystem Is Growing Fast — and Quietly Becoming a Directory of Hosted Services
We [recently measured](/insights/in-the-registry-is-not-runs-2) whether the servers in the official MCP registry actually *run* (short version: 84% are remote-only, and under half of the installable ones boot clean). Whi…
Installing a Skill Usually Means Running a Stranger's Code
The pitch for Claude Code "skills" is that they're just *instructions* — a `SKILL.md` file with some markdown telling the agent how to do a task. That framing makes them feel safe to install. A prompt can't hurt you; wor…