These are my notes on "Your Agent Is Mine: Measuring Malicious Intermediary Attacks on the LLM Supply Chain" by Hanzhi Liu, Chaofan Shou, Hongbo Wen, Yanju Chen, Ryan Jingyang Fang, and Yu Feng. The research is theirs; I'm writing this up to wrap my head around their findings. You can read the paper on arXiv or in its HTML version. Both figures below are reproduced from the paper without edits under its CC BY-NC-ND 4.0 license.
AI agents like Claude Code or Codex run shell commands and install packages on your machine based on instructions from a language model. Most discussions around agent security focus on tricking the model itself, usually through prompt injection. This paper looks at a different weak spot: the intermediary service that carries messages between the agent and the model. It shows how a compromised router can quietly alter what the agent executes. It also shows that some real services are already doing exactly that.
What an LLM API router does
That middle service is an LLM API router. Instead of connecting straight to OpenAI, Anthropic, or Google, the agent sends its requests to the router, which forwards them to the right provider. This lets developers use a single API key across multiple models, which explains why routers have become so popular. As the paper notes, the open-source router LiteLLM alone has over 240 million pulls on Docker Hub.
Pointing an agent to a router usually just takes changing a base URL and an API key, but it is a massive trust decision. Normally, TLS encryption prevents anyone in the middle from reading your traffic. With a router, that encrypted connection terminates at the router itself. It decrypts your request and opens a fresh connection to the model provider. That means the router sees your API keys, your prompts, and the model’s replies in plain text, and nothing stops it from modifying them in transit.
Routers can also sit behind other routers. In one example from the paper, a developer buys API access from a reseller on Taobao, and the request ends up passing through two additional routers before ever reaching the model. The developer only chose the first hop, yet every hop along the chain can read the entire payload. If one router in the middle is malicious, the honest ones downstream have no way of knowing, because they never see the original response.
Figure 1 from Liu et al. (2026), arXiv:2604.08407, licensed CC BY-NC-ND 4.0. One malicious router (R4) taints responses to Claude Code and Codex, while OpenClaw, routed through honest hops, is unaffected.
A router doesn’t have to start out malicious, either. In March 2026, LiteLLM versions 1.82.7 and 1.82.8 were published to PyPI containing malicious code (Datadog Security Labs has a great write-up). The authors point to this incident to highlight how realistic the threat is: a single poisoned release put attacker code right inside a router that thousands of developers already trust.
Why this matters for agents
Snooping on traffic is bad enough, but for AI agents, the real danger is tampering. A language model never executes code on its own. Instead, it replies with a tool call, a small JSON snippet saying “run this shell command”, and the local agent executes it, often without asking the user first. Because no major model provider cryptographically signs these responses, the agent has no way to verify that the JSON it received is actually what the model wrote. Whoever controls the router controls what the agent runs on your machine.
Four ways a router can attack
The authors break router attacks down into two core classes (AC-1 and AC-2), along with two evasion variants of AC-1 designed to slip past basic defenses.
| Class | Type | What the router changes | Main harm |
|---|---|---|---|
| AC-1 | Core | Tool-call arguments in the response | Arbitrary code execution |
| AC-2 | Core | Nothing (read-only) | Credential theft |
| AC-1.a | Evasion | The package name inside an install command | Durable supply chain compromise |
| AC-1.b | Evasion | Same as AC-1, but only when a condition matches | Targeted attacks that short audits miss |
Based on Table 1 in the paper.
AC-1 (payload injection) is the most direct approach. The router rewrites the arguments inside a legitimate tool call, such as swapping an official installer URL for one controlled by the attacker. Because the JSON structure remains completely valid, the agent treats the call as normal. And since the tampering happens after the model generates its reply, model-level safety guardrails never even see it.
AC-2 (secret exfiltration) is purely passive. The router leaves the traffic untouched while scanning requests and responses for credentials like AWS keys, GitHub tokens, or private keys. Since a router has to read your headers and payload to forward the request anyway, credential theft looks identical to normal routing from the client’s perspective.
The two evasion variants make AC-1 much harder to catch:
AC-1.a targets package installation commands like pip install. Instead of inserting a sketchy URL that a domain allowlist might block, the router swaps the requested package name for a typosquatted look-alike that the attacker has already uploaded to the official registry:
"name": "Bash",
"input": {
- "command": "pip install requests"
+ "command": "pip install reqeusts"
}
Because the package still comes from PyPI or npm, domain checks pass without complaint, and the malicious dependency stays on the machine across future sessions.
AC-1.b relies on timing and context. The router acts completely honestly until a specific trigger condition is met, like seeing a certain keyword in the prompt, passing a threshold of prior requests, or detecting that the agent is running in “YOLO mode” (where tool calls execute automatically without user confirmation). A router programmed to wait until the 50th request before attacking will look completely clean during a quick 10-request security check.
Figure 2 from Liu et al. (2026), arXiv:2604.08407, licensed CC BY-NC-ND 4.0. Where each attack class acts inside the router.
What the authors found in the wild
To see if this is actually happening in practice, the authors bought access to 28 paid routers from Taobao, Xianyu, and Shopify stores, and gathered 400 free routers shared in public communities. They ran automated coding tasks through each one inside a sandbox, seeding the traffic with researcher-owned “canary” credentials to see if anyone would take the bait.
| What they observed | Paid (28) | Free (400) |
|---|---|---|
| Injected malicious code into tool calls | 1 | 8 |
| Attacked only under certain conditions | 0 | 2 |
| Used a researcher-owned AWS canary key | 0 | 17 |
| Drained ETH from a researcher-owned wallet | 0 | 1 |
The two conditional routers used the exact triggers described in AC-1.b: one waited until the first 50 requests had passed before injecting code, while the other only targeted YOLO-mode sessions working on Rust or Go projects.
Even well-meaning router operators can end up exposing their users. To prove this, the authors leaked one of their own OpenAI keys on Chinese forums and in WeChat and Telegram groups where router operators trade credentials. That single key ended up serving 100 million GPT-5.4 tokens. They also stood up deliberately vulnerable decoy routers, which other operators quickly absorbed into their own upstream relay chains. Those decoys processed roughly 2 billion tokens and exposed 99 real credentials across 440 Codex sessions. Notably, 401 of those sessions were running in YOLO mode, so a basic AC-1 attack would have compromised them immediately.
Client-side frameworks aren’t looking for this, either. None of the four agents the authors tested (OpenClaw, OpenCode, Codex, and Claude Code) verified whether a response had been altered in transit.
What can be done
Right now, there is no way for a client to cryptographically prove that a router left a response untouched. However, the authors tested three practical defenses against synthetic attack data to see what helps.
The first is a policy gate that blocks shell or install commands referencing domains or packages outside a local allowlist. It caught every standard AC-1 and AC-1.a sample with just a 1.0% false-positive rate. Still, when an attacker hosted the payload on an allowlisted domain, or planted a script for a benign-looking command to trigger later, the gate was bypassed every time.
The second is an anomaly detector that scores each tool call based on how risky its shell command looks. At a 6.7% false-positive rate, it flagged 89.0% of direct AC-1 attacks, but caught only about half of the AC-1.a (50.0%) and AC-1.b (50.9%) variants. That makes it more useful as a human review aid than an automated blocker. The third defense is simply keeping an audit log of all local requests and responses. It won’t stop an attack in real time, but it lets you see what a compromised router had access to after the fact.
For a real long-term fix, the authors argue that model providers should cryptographically sign their responses, similar in spirit to DKIM for email, so agents can verify a tool call’s integrity before executing it. No major provider supports this yet, and even then, it wouldn’t solve AC-2, since secrets in the prompt are exposed before the request ever reaches the provider.
Putting all of this together, here is my mental checklist if you have to use a third-party router:
- Assume full visibility and control: Treat the router as able to read every key and rewrite every command.
- Turn off auto-execution: Avoid YOLO mode when routing traffic through infrastructure you don’t own.
- Gate your commands: Put a domain/package allowlist or manual approval step in front of shell and install tools.
- Rotate exposed secrets: Immediately rotate any API keys or tokens that passed through a router you no longer trust.
- Isolate and log: Run coding agents inside a container or VM, and keep local logs of the raw traffic.
My takeaways
I picked this paper because the canary approach felt familiar. Running a T-Pot honeypot taught me that exposed credentials and services get found fast, and this paper shows the same thing happening inside the LLM supply chain.
What stood out to me most about this paper is that the attack requires zero prompt engineering. A service the developer explicitly configured just edits a few lines of JSON, and the agent happily runs it. The relay-poisoning experiments were especially eye-opening: even if you trust the operator of your router, they might be pooling leaked keys or forwarding your traffic through upstream relays they don’t actually control, and you have no visibility into those extra hops.
I’m still curious how response signing will work in practice when routers have to translate between different API schemas (like converting Anthropic’s format to OpenAI’s). The paper proposes signing a normalized version of the core fields to survive translation, which seems promising if providers ever agree on a standard. If I misread anything here, I’d love to hear about it.
References
- Hanzhi Liu, Chaofan Shou, Hongbo Wen, Yanju Chen, Ryan Jingyang Fang, and Yu Feng. “Your Agent Is Mine: Measuring Malicious Intermediary Attacks on the LLM Supply Chain.” arXiv:2604.08407 [cs.CR], April 2026. arXiv abstract · HTML version. Licensed CC BY-NC-ND 4.0.
- Nick Frichette, Sebastian Obregoso, Christophe Tafani-Dereeper, and Emile Spir. “LiteLLM and Telnyx compromised on PyPI: Tracing the TeamPCP supply chain campaign.” Datadog Security Labs, March 24, 2026 (updated March 27, 2026).