The uncomfortable thing about agentjacking is that every step in it is authorized. An attacker posts a fake error to your Sentry project using the public, write-only key that ships in your website's JavaScript. Your coding agent later pulls recent errors over MCP to help you debug, reads the planted instruction as if it were a stack trace, and runs it — with your privileges, from your machine. No server is breached. No human clicks "approve." Tenet Security, which disclosed the attack, reported roughly 85% success across Claude Code, Cursor, and Codex in testing.

Because the attack rides in on trusted data, the fix cannot be "trust the data less" as a vibe. It has to be a set of controls at the point where the agent acts. Here is the playbook, ordered by how much attack surface each control removes.

The one thing to know: you cannot prompt your way out of this. A line in the system prompt telling the agent to ignore instructions in tool output is documentation, not a control — the injected text competes with it as equal input. The defenses that work all live below the model.

Control 1 — Sandbox the agent (deny rules are not enough)#

The most common mistake is treating Claude Code's deny rules as the security layer. They are not. Deny rules only gate the built-in tools; a bash subprocess the agent spawns runs outside that gate. So a denied action can still happen through the shell. The real boundary is a sandbox.

Claude Code ships an example dev container with a default-deny firewall precisely so you can run the agent inside a box where an escaped command has nowhere to go. Run the agent sandboxed, and treat the deny list as convenience, not defense.

Control 2 — Deny network egress by default, allowlist the rest#

This is the highest-leverage single control, so if you do nothing else, do this. Almost every agentjacking payload ends the same way: exfiltrate a secret, or curl down a second-stage script. Cut off the network and the injected command mostly dies even if it runs.

Set egress to deny-by-default and allowlist only what the agent genuinely needs — your model API and your package registry:

# egress proxy allowlist — everything else gets CONNECT 403
api.anthropic.com
registry.npmjs.org
pypi.org

Claude Code's sandbox routes traffic through a local proxy that returns 403 for anything not on the list, with a socket-layer backstop for tools that try to ignore the proxy. The agent can still npm install; it cannot curl an attacker's collector. If you run untrusted execution alongside this, the same principle applies at the runtime layer — a Cloud Run sandbox denies egress by default and makes you opt in per call.

Control 3 — Pin tool execution to an allowlist, keep humans on writes#

Put access control at the tool-execution layer, not in the prompt, so an injected instruction cannot argue its way past it. Two rules:

Microsoft's MCP injection guidance and OWASP's tool-poisoning entry both land on the same principle: controls enforced below the model survive prompt injection; instructions to the model do not.

Control 4 — Treat every fetched record as untrusted, and scope your MCP servers#

Reframe the mental model. A Sentry error, a Jira ticket, a log line, a PagerDuty alert — these are not facts. They are input that an outside party may have written, and your agent should handle them the way a web app handles a form field: as hostile until proven otherwise. Two practical moves:

Put it together#

The four controls stack into defense in depth, and each one alone still helps:

  1. Sandbox the agent so escaped commands have nowhere to land.
  2. Deny egress by default, allowlist your model API and registries — kills the exfil path.
  3. Allowlist tools and gate writes on human approval, enforced below the LLM.
  4. Treat fetched records as untrusted and scope the MCP servers in reach.

None of these depend on Sentry shipping a patch — which, per the disclosure, it declined to fully do, because the root cause isn't in Sentry. It is in the assumption that data an agent reads and instructions it follows are different things. Build as if they're the same, because to your model, they are. For the full anatomy of the attack, see our original breakdown.