If a coding agent on your machine got prompt‑injected right now, what could it reach?
ifinjected reads how Claude Code, Codex, Gemini CLI, Cursor, opencode and Copilot CLI are set up, finds the credentials on your machine without reading their values, and shows, agent by agent, what an injected agent could read and where it could send it.
uvx ifinjected
Read-only and offline. Needs Python 3.11 or newer; uv tool install ifinjected or
pipx install ifinjected keeps it.
ifinjected scan sees it.| On this laptop | Claude Codedefault settings, no sandbox | Codexread-only + on-request, sandbox on | opencodedefault settings, no sandbox |
|---|---|---|---|
~/.ssh/id_ed25519SSH private key (no passphrase) |
around a deny rulethe deny rule for ~/.ssh/** does not cover shell commands | no approval | no approval |
~/.aws/credentialsAWS credentials file |
no approval | no approval | no approval |
~/.config/gh/hosts.ymlGitHub CLI login |
no approval | no approval | no approval |
~/.npmrcnpm config (may hold a token) |
no approval | no approval | no approval |
~/src/app/.env.env file in the workspace |
no approval | no approval | no approval |
| the shell's environmentService credentials in the environment | no approval | no approval | no approval |
~/.claude/.credentials.jsonClaude Code login token |
no approval | no approval | no approval |
~/.codex/auth.jsonCodex login token |
no approval | no approval | no approval |
| Send it outa way to get data off the machine | no approvala shell command with full network access (curl, git push) | after approvala shell command with full network access (curl, git push) | no approvala shell command with full network access (curl, git push) |
| Grade | F | C | F |
Highlighted means the agent can do it with nobody's approval, pink only after you approve a step. All three agents can read every credential here. Codex gets a C, not an F, because its sandbox closes the network and the only way out needs your approval.
What the agents' own documentation says
Every behaviour ifinjected relies on is cited, with the date it was checked, in agents/data.json. A few of them surprise most people:
- Claude Code runs read-only commands such as
catwithout a prompt, in every mode. In the default mode,cat ~/.ssh/id_ed25519needs nobody's approval. Permissions docs - A
Read(~/.ssh/**)deny rule does not stop a Python one-liner. Deny rules cover Claude Code's file tools and the commands it recognises, not "arbitrary subprocesses that read or write files indirectly". ifinjected reports such a rule as walked around, with its line. Permissions docs - Turning a sandbox on does not protect your keys. Claude Code's sandbox reads "most of the
machine, including credential files such as
~/.sshand~/.aws/credentials", and Codex's read-only and workspace-write sandboxes start from read access to the whole disk. Sandboxing docs, Codex source - opencode has no sandbox and allows most tools by default. A default setup with credentials on disk is an F. Permissions docs
- One committed
.mcp.jsonconfigures two agents. Copilot CLI also loads Claude Code's.mcp.jsonand its hooks. Copilot CLI reference
How the grade is decided
Each agent gets a grade from its lethal trifecta, Simon Willison's name for the three things that together let an attacker steal through an agent:
- Private data
- a credential it can read, with or without an approval;
- Untrusted input
- text someone else wrote: the repository, an issue, a dependency, a web page;
- A way out
- a shell with network access, a web fetch, an MCP server that writes elsewhere.
With all three open and no approval anywhere, one poisoned README can make the agent read a key and send it away on its own: that is an F. A human approval in the way makes it a C; closing a leg makes it a B; an A has at most one open. The machine's grade is its worst agent's, because an attacker picks the easiest one. The full rules
Prove it on your machine
A grade comes from configuration and documentation. ifinjected prove --run checks the claim: a
fixed probe calls open() and close() on each credential, never read(),
once directly and once inside Codex's own sandbox. With --network it also tries one connection out.
$ ifinjected prove --run --network
Codex: opened directly / inside codex sandbox
opened opened AWS config ~/.aws/config
opened opened Docker config (may hold registry passwords) ~/.docker/config.json
opened opened GitHub CLI login ~/.config/gh/hosts.yml
opened opened npm config (may hold a token) ~/.npmrc
…
network: refused to example.com:443 from inside the sandbox
Part of what it printed on a Linux laptop running Codex: every credential file opened inside the sandbox, and the network stayed closed.
Catch pull requests that widen agent permissions
A pull request that adds an MCP server, an allow rule or a hook to .claude/settings.json,
.mcp.json, .codex/config.toml, .cursor/ or opencode.json
changes what every agent in the repository may do. The Action compares that configuration with the base branch,
fails the check when a change fails your policy, and marks the line that widened it.
# .github/workflows/agents.yml
on: pull_request
permissions:
contents: read
security-events: write
jobs:
ifinjected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: BhuwanJoshi-01/ifinjected@v0.1.0
id: ifinjected
- uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: ${{ steps.ifinjected.outputs.sarif }}
Locally: ifinjected diff --base origin/main.
What it promises
- It never prints, stores or logs a secret value. Credential files are found by location and permissions; SSH keys are read up to 192 bytes to tell an encrypted key from an unencrypted one. A test plants fake secrets everywhere and checks every output format for them.
- It never runs what an agent is configured to run. No MCP server, hook or helper is started.
- It reads only what it declares. A test records every file access during whole scans and fails on anything not listed in what is read.
- It writes nothing and sends nothing, except a file you name with
--outputand the one connectionprove --networkasks for.
ifinjected describes what a configuration allows. It is not a sandbox, and a clean report is not a guarantee.