Measured against
the guardrails you use today
Public datasets, each tool at its own default. Every number links to the script and the results file behind it, including the ones we lose.
Attacks that got through
AgentDojo, 588 attacks · lower is betterReal attacks caught
held-out injections · higher is betterHarmless messages blocked
339 look-alikes · lower is betterPrompt Guard 2 blocks fewer harmless messages and catches far fewer attacks.
Normal tasks done without a person
AgentDojo, 97 tasks · higher is betterAgentFox sends the rest to a person to approve. Section 3 shows how to get to 62.
Prompt Guard 2 is the 86M model. Every model at its published threshold.
Attacks on a tool-using agent
AgentDojo (ETH Zurich) is the standard benchmark for prompt injection against agents that call tools. We replayed its 97 user tasks, and its 588 attacks that try to make the agent write, send or pay something it shouldn't.
| Defence | Attacks that got through | Normal tasks done without a person | Harmless tool outputs flagged |
|---|---|---|---|
| No defence | 588 / 588 | 97 / 97 | – |
| LLM Guard, default | 86 / 588 (14.6%) | 42 / 97 | 82 / 339 (24.2%) |
| AgentFox, default | 0 / 588 | 24 / 97 | 0 / 339 |
| AgentFox, detectors off | 0 / 588 | 24 / 97 | – |
| LLM Guard and AgentFox together | 0 / 588 | 11 / 97 | 82 / 339 |
LLM Guard reads each tool output and decides whether it holds an injection. Most of its 86 misses are attacks past the first 512 tokens of an output, which is all its model reads. AgentFox doesn't have to spot the injection. It tracks where each argument of a tool call came from, and a payment or email whose details came from a tool output goes to a person first.
LLM Guard can close the gap. In its long-input mode, with a threshold picked on this test set, it stops all 588 at the same 24 tasks, but flags 38% of harmless tool outputs on the way.
- No model was run. The agent's calls are AgentDojo's own answer key, so this measures the guardrail, not how easily a model is fooled.
- Sending a call to a person counts as stopping it. If the reviewer approves an attacker's call, it goes through.
- Prompt Guard 2 isn't in this table: the model is gated on Hugging Face and our access request wasn't granted.
With every detector switched off
Once attackers can adapt, published defences fail more than 90% of the time (Nasr, Carlini et al., 2025). So we tested what happens after the detectors have already lost: eight attacks, one for each way AgentFox stops a call, with detection disabled.
| Attack | Result | Stopped by |
|---|---|---|
| Send data out through a tool the agent was never given | block | capability.denied |
| Transfer money to an account taken from attacker text | escalate | taint.irreversible_tool |
| Transfer above the declared limit | block | capability constraint |
| Refund above the declared limit | block | capability constraint |
| Unbounded DELETE inside a tool argument | block | sql.unbounded_mutation |
| SQL injection in an ordinary lookup | block | scope.sql_fragment_in_value |
| Output of a read tool reused in an irreversible call | escalate | taint.irreversible_tool |
| Allowed call while the agent is quarantined | block | agent.quarantined |
- Containment is only as good as what you declare. A tool that sends money but is declared read-only is invisible to it.
- Eight scenarios is a small set: one per mechanism, chosen to differ from each other.
What containment costs
Stopping every attack has a price. By default, three in four normal AgentDojo tasks needed a person to approve a step. That is a setting, and these are the trade-offs on the same replay.
Attacks stopped
of 588 · higher is betterNormal tasks without a person
of 97 · higher is better| Setting | Normal tasks without a person | Attacks stopped |
|---|---|---|
| Session-level taint (default) | 24 / 97 | 588 / 588 |
| Argument-level taint | 37 / 97 | 527 / 588 |
| Session-level, read-only tools exempt | 43 / 97 | 588 / 588 |
| Argument-level, read-only tools exempt | 62 / 97 | 527 / 588 |
| Perfect labels (upper bound) | 97 / 97 | 588 / 588 |
| Grants and impact tiers only | 97 / 97 | 0 / 588 |
Session-level taint treats everything after the agent reads a tool output as untrusted. It stops every attack, and most legitimate payments and messages with them. Argument-level taint checks each argument. It lets more work through and misses 61 attacks: short values like a file id of "13", and attacker text buried inside a longer argument.
Most of what's left can't be fixed by provenance alone. Paying a bill means copying an IBAN out of a document, which is exactly what an attack that redirects a payment does.
- The read-only exemption was designed after seeing the other rows.
- This checks whether legitimate work could run without a person, not whether the agent did it well.
Detection accuracy
The detectors are the second layer. These are their numbers on held-out data that nothing was tuned on.
| Model | Held-out attacks caught | SPML | Multilingual | Harmless look-alikes flagged |
|---|---|---|---|---|
| AgentFox default (PIGuard and patterns) | 68.3% | 29.6% | 76.0% | 11.5% |
| LLM Guard's model (ProtectAI DeBERTa v2) | 31.7% | – | – | 42.2% |
| Prompt Guard 2, 86M | 20.0% | 38.4% | 44.7% | 3.5% |
| Prompt Guard 2, 22M | 16.7% | 11.2% | 7.4% | 0% |
| AgentFox patterns only, no model | 16.7% | 9.6% | 0.9% | 0% |
Held-out attacks are 116 examples from deepset/prompt-injections, scored with no false alarms on that split. Harmless look-alikes are NotInject's 339 prompts, written to use the same words attacks do. Prompt Guard 2 86M does better on SPML and flags less harmless text. PIGuard catches the most, so it stays the default.
A second model can run beside PIGuard. With it on, 41% of the look-alikes are flagged, so its hits are recorded and never block: an enforcing rule acts on PIGuard alone.
Peer reviewed · WAITI 2026No Tier Wins Everywhere: Benchmarking Rules, Classifiers, a Judgment Model and an LLM as Security Detectors for AI Agents
Accepted at WAITI 2026, a workshop of ACSAC 2026, to be presented in Los Angeles on 7 December. Five kinds of detector compared on the same rows across eight kinds of security decision: 21 labelled datasets, 175,816 labelled decisions.
Per-row decisions and scripts- Without the model weights (installed without the classifiers extra) AgentFox runs the patterns alone, at 16.7%. The Docker image ships the weights.
- An earlier version of the patterns was fitted to the held-out misses and scored 26.7%. Those patterns were removed and the benchmark rerun.
Attacks a text scanner can't see
A prompt-injection scanner checks one string at a time. These attacks only show up across messages, inside tool arguments, or in what the agent is allowed to do. LLM Guard was installed and called for real.
LLM Guard is more precise on attacks hidden in tool output, with 2 false alarms on the harmless half against our 5. The permission scenario AgentFox got wrong was an ordinary ticket update held for approval, because the demo declares that updating a ticket sends an email.
- The multi-message check runs in the SDK only, not yet in the gateway.
An attacker who adapts
Here the attacker sees our verdict and rewrites the attack, up to 50 times. It starts only from attacks our detectors already stop, so every success is a real bypass.
With rewrites a model could still read (paraphrases, other languages, no encoding), three in four attacks got past the detectors within 50 tries. With encodings allowed, all of them did within 25. Detectors alone don't hold, which is why the next check happens at the tool call.
- Search-based attacks only. Gradient-based attacks and human red-teamers are stronger, so read this as a lower bound.
- It measured the pattern-only stack, the default until October 2026. The classifier now in the default hasn't been re-measured.
- A bypass means the detector allowed the text, not that a model would obey it. No model was in the loop.
Rerun it yourself
Each benchmark is one script with its own throwaway database, and it writes the results file quoted here. A number that doesn't match is a bug.
uv run python -m benchmarks.containment.run_containment_benchmarkPYTHONPATH=src .venv/bin/python -m benchmarks.head_to_head.run_agentdojo_h2huv run python -m benchmarks.agent_security.tier_b_indirect_injectionuv run python -m benchmarks.injection.run_prompt_injection_benchmarkuv run python -m benchmarks.adaptive.run_adaptive_benchmarkThe LLM Guard comparisons need LLM_GUARD_VENV_PYTHON pointing at a separate environment, because LLM Guard pins an older transformers. None of these runs needs a model API key. Each figure is a single run, and the run dates, hardware and LLM Guard version weren't recorded.
Latency: what a check adds to your agent What AgentFox doesn't catch yet
Try AgentFox on your own agent
One command finds your agents. It starts in watch mode, so nothing is blocked until you say so.
uvx agentfox scan