ifinjected

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.

The example laptop in examples/messy-laptop, as 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:

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

ifinjected describes what a configuration allows. It is not a sandbox, and a clean report is not a guarantee.