Back to all posts
Published on · by Renaud Deraison

The bug report ran the command

Agentjacking, disclosed by Tenet Security in June 2026, needs no malware and no access to your infrastructure. An attacker files a fake error against your Sentry project using a public write-only key, hides instructions inside it, and waits. When you ask your coding agent to fix the issue, it reads the attacker's text as guidance and runs it with your credentials. Tenet found at least 2,388 exposed projects and an 85% success rate across Claude Code, Cursor, and Codex. Bromure Agentic Coding runs that whole read-and-run path inside a disposable Linux VM, scores the fetched text before the agent acts on it, and leaves the credential thief only decoys.

You asked your coding agent to fix an error. It opened the issue tracker, read the top result, and did what it said. The problem is that anyone on the internet could write that error — and the one your agent read was written to be obeyed. No malware landed. No server was breached. A bug report ran a command on your machine.

On June 12, 2026, researchers at Tenet Security — Ron Bobrov, Barak Sternberg, and Nevo Poran — published an attack they call Agentjacking, through the Cloud Security Alliance's AI Safety Initiative. It does not use a phishing email, a poisoned package, or a stolen password. It uses a bug report. Specifically, it uses the fact that a coding agent asked to "fix the open Sentry issues" will read an error report and treat its contents as instructions.

The setup starts with something most web apps hand out on purpose. A Sentry DSN — the key a site uses to report its own crashes to Sentry, the error-tracking platform — is a write-only public credential. It ships in the client-side JavaScript of the page, and Sentry's ingest endpoint accepts unauthenticated POST requests to it by design. That is how your browser tab reports a crash without logging in. It also means anyone who reads your page source can send events to your Sentry project. Tenet scanned for these and found 71 injectable DSNs among the top one million websites, and at least 2,388 organizations with exposed DSNs across the wider internet.

How a crash report becomes a command

The attacker takes a DSN they found and POSTs a crafted error event to it. The event looks like an ordinary crash, except the message field and the context keys are written in markdown — headings, code blocks, a section that reads like a "Resolution" — formatted to be visually and syntactically indistinguishable from Sentry's own diagnostic templates. It sits in the project's issue list like any other unresolved error.

Then the attacker waits for you. When a developer asks their coding agent to investigate unresolved Sentry issues, the agent queries Sentry — usually through the Sentry MCP server, the standard connector that lets an agent pull data from an outside tool — and gets the event back. As Tenet put it, the agent "receives the response and acts on it, just as a developer would." The response carries no signal that its contents were authored by an attacker rather than by the application's own runtime. The "Resolution" says to run a command. The agent runs it, with the developer's own privileges, on the developer's own machine.

Step 1 — the attacker plants it (any time)Attackerreads your DSNPOST fake crashYour Sentry projectevent = markdown "Resolution""run this to fix it"Step 2 — you ask your agent to fix it (later, separately)The agent"fix openSentry issues"MCP querySentry MCP serverreturns the eventno "attacker wrote this" flagreads as planYour machineagent runs the commandyour privileges,your real tokensexfiltrateAttacker's serverNo phishing. No malware. Every step is an authorized action.
Agentjacking has two halves that never meet. First the attacker POSTs a fake crash to your Sentry project using the public write-only DSN, with commands hidden in the message as a fake 'Resolution.' Later, and separately, you ask your agent to fix open issues; it pulls the poisoned event through the Sentry MCP server, reads the Resolution as guidance, and runs it with your credentials — exfiltrating tokens to the attacker. The attacker never touches your infrastructure.

Tenet reported an 85% success rate across the agents they tested — Claude Code, Cursor, and Codex, the tools most developers reach for. Their proof-of-concept command walked off with environment variables, AWS credentials, GitHub and GitLab OAuth tokens, npm registry tokens, Docker configuration, Kubernetes cluster tokens, and CI/CD pipeline secrets. Sentry, told about the flaw on June 3, declined to fix the underlying behavior — it is, in their words, technically not defensible — and added a global content filter that blocks one specific payload string.

Why every existing defense walked right past it

Agentjacking matters not for the cleverness of the payload but because nothing in a normal security stack fires. There is no malware on disk for an endpoint agent to catch. There is no unusual login for identity monitoring to flag. There is no outbound connection from a process that has no business making one — the agent is supposed to talk to Sentry, and the developer's credentials are supposed to work. Every step in the chain is an authorized action performed with legitimate access. As Tenet put it, the attack breaks no policy and trips no anomaly threshold.

This is the same wall the rest of agentic security keeps hitting. A command denylist loses to the shell that rewrites the command. A workspace you trusted loses to the servers it silently launches. A security review the agent runs for you becomes the thing that attacks you. Agentjacking is the version where the poisoned input arrives through a tool you were right to trust, carrying data you had every reason to read. The Sentry MCP server did its job perfectly. The problem is that "fetch the error and act on it" and "fetch the attacker's instructions and act on them" are, to the agent, the same sentence.

Tenet's own recommendations point at the shape of the fix: run the agent in a sandbox with restricted network egress, treat anything an MCP server returns as potentially adversarial input, and stop letting a fetched string become a command without a boundary in between. Those describe an architecture, not a patch.

Where Bromure draws the line

Bromure Agentic Coding does not try to teach the agent which bug reports to believe. It changes what a believed one can reach. Every agent Bromure runs — Claude Code, Codex, Grok Build — runs inside a disposable Linux VM on your Mac, not on macOS itself. So when the agent pulls the poisoned Sentry event and does exactly what Tenet watched these agents do, several things are already true.

First, the fetched text is read against a detector before it becomes a plan. Agentjacking is a reading attack before it is an execution attack: the whole exploit hinges on the agent ingesting the attacker's "Resolution" and treating it as guidance. That is precisely the untrusted authority Bromure watches — the same class as a rogue CLAUDE.md or a poisoned AGENTS.md. Bromure's on-device prompt-injection detection scores the content the agent reads against local models that never leave the Mac, plus a deterministic scanner for invisible-Unicode tricks. A crash report whose "fix" is a shell command that reads your tokens is exactly the case that detector exists to flag.

Second, the command runs in a box you throw away. Suppose the lure is phrased well enough to slip the scorer and the command runs. It runs inside the VM. The "persistent backdoor access" in Tenet's threat model has nothing to persist on: reset the profile to base and the dropped script, the added SSH key, the scheduled job — all of it is gone. Nothing the command did survives the window closing.

Third, there are no real credentials to steal. The entire loot list from the proof-of-concept — AWS keys, GitHub and GitLab tokens, npm and Docker credentials, Kubernetes and CI/CD secrets, environment variables — is what Bromure keeps out of the box in the first place. Bromure injects placeholder values (brm_…, a synthetic ~/.kube/config, throwaway cloud and registry tokens) into the VM and swaps the real ones back only on the host, in the proxy, on the way out to the genuine API. The command scrapes the environment and disk and finds decoys. Whatever it ships to the attacker's server is a bag of strings that authenticate to nothing.

Fourth, the exfiltration is visible. Tenet's attack ends with the stolen tokens leaving the machine. Bromure routes the VM's traffic through a host proxy, so the connection to the attacker's server shows up in the Security Log as outbound traffic you can see — not a quiet callback from a laptop you assumed was clean. The "restricted network egress" Tenet recommends is where the agent already lives.

Disposable Linux VMPoisoned eventthe agent fetchesthe "Resolution"Injection detectoron-device scoringscored before it runsWhat the command would stealAWS_SECRET = brm_9f2a… (fake)GitHub / npm / kube tokens = decoysbox resets to base afterwardexfil (logged)Attacker gets decoysstrings that log in nowhereReal secretsstay on the host proxyswapped in on the wireThe report still arrives and the agent still reads it. It just does so somewhere disposable, with keys that aren't real.
Same fake report, same fetch, same command — different machine. Inside Bromure the agent runs on a throwaway Linux VM: the poisoned 'Resolution' is scored by an on-device detector before it becomes a plan, the command runs somewhere disposable, the tokens it scrapes are decoys swapped for the real ones only on the host proxy, and the exfiltration is logged. The report still arrives. There is nothing worth taking.

What this draws a line around

Isolation and scoring are containment, not a cure for a trusting agent. It is worth being precise about what Bromure changes here and what it does not.

The report still arrives

Bromure does not fix Sentry's DSN design or stop an attacker from filing a poisoned crash — that write-only public key is Sentry's boundary to draw, and they have declined to. What Bromure changes is what happens after the agent reads the report: where the command runs and what it can reach.

Detection is a net, not a wall

On-device injection scoring flags a "run this to fix it" resolution more often than not, but a careful author can phrase a lure to slip a single scanner. Treat the detector as one layer; the containment — a throwaway box with fake keys and a logged egress — is what holds when a detector misses.

Substitution covers the secrets you configure

The decoy swap protects credentials you put in a profile: model keys, git and cloud tokens, registry and cluster tokens, SSH keys. A token a script writes to disk mid-run, or a session you establish by hand inside the box, is just data. Keep secrets in the broker, not in the workspace.

Isolation is not a reason to auto-run everything

A disposable VM shrinks the blast radius; it does not make review pointless. For actions that reach outside the box — opening a pull request, pushing to a remote — the confirmation step still earns its place. Containment lowers the stakes of a mistake; it does not delete them.

Tenet's finding is not that one platform has a bug. It is that the trust mechanism underneath — an agent that treats a fetched string as a fact and a "Resolution" as a plan — is shared across every tool that connects an agent to the outside world, and it is not getting patched this quarter. Sentry is the messenger; the next one will be a Jira ticket, a Linear comment, a code review left by a stranger. Your agent is going to read something an attacker wrote, and at an 85% success rate it will sometimes do what it says. The question is not whether it ever gets fooled. The question is what runs when it does: your machine, your real tokens, and a foothold that stays — or a Linux box you can throw away, with decoys inside it and the egress on the record. Bromure makes it the second one.