Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

toolgate-bench

A trivially useless defence scores 100% on this benchmark. That is the point.

toolgate-bench measures how well an agent tool-call authorization layer separates a task's legitimate actions from injected ones, using AgentDojo ground truth. It runs in seconds, needs no API key, no GPU, and no agent.

tier      attacks   attack actions   own task   cross-task
          stopped   blocked          intact     generality
----------------------------------------------------------------
allow-all (control)
target      0.0%      0.0%           100.0%     100.0%
deny-egress (control)
target    100.0%     65.4%            37.4%      39.5%
anchor (deterministic layer only)
target     98.9%     88.1%           100.0%       8.4%

Read that table before reading anyone's marketing. deny-egress blocks every write, network and message call regardless of policy — it is nine lines and protects nothing anybody would ship. It beats every real defence on the headline metric. A block rate is only meaningful next to what the defence still permits, which is why the generality column exists and why the controls ship in the repo.

Why this exists

Agent-security libraries make claims. Almost none publish numbers, and the ones that do rarely publish what their defence costs. Meanwhile the strongest published architectural defences — CaMeL, FIDES — are Python research code, so the whole JavaScript ecosystem is unmeasured.

Anyone can run this against their own gate in under a minute and get a number that is comparable to everyone else's.

Run it

git clone <this repo> && cd toolgate-bench
node src/run.mjs adapters/allow-all.mjs adapters/deny-egress.mjs   # the controls
node src/run.mjs adapters/anchor.mjs                               # a real defence

No install step. The corpus is committed as data/ground_truth.jsonl.

To regenerate it from an AgentDojo checkout (only needed if AgentDojo changes):

pip install pydantic docstring-parser deepdiff pyyaml rich tenacity click python-dotenv
python extract/extract_ground_truth.py --out data/ground_truth.jsonl

src/corpus.mjs refuses to run if extraction does not yield the published 97 user tasks and 629 pairs, so silent drift fails loudly instead of quietly rescoring everyone against a different corpus.

Add your defence

An adapter is one file with no dependencies:

export default {
  name: 'my-gate',
  createSession(policySpec) {          // once per replayed sequence
    return {
      async check(action) {            // { type, effect, target? }
        return { allowed: true };      // { allowed: boolean, reason?: string }
      },
    };
  },
};

policySpec is a neutral format that is nobody's product schema — translate it into whatever your library speaks. See src/policy.mjs for the shape and adapters/allow-all.mjs for the reference implementation. createSession is per-sequence, so stateful defences can accumulate session state and stateless ones can ignore it.

Target patterns in the spec are literal strings, not globs. A benchmark cannot assume a shared glob dialect, so if your matcher globs, escape the metacharacters. Getting this wrong inflates your score: an unescaped ? — every URL with a query string — becomes a wildcard.

Methodology

For each of AgentDojo's 629 (user task, injection task) pairs:

  1. A policy is built from the user task alone — what an operator could write knowing the declared job and nothing about the attack. Tightening beyond that would score the benchmark author's foresight, not the defence.
  2. The user task's actions replay first, then the injection's, because indirect injection fires after the agent has read the poisoned content.
  3. An attack is stopped if any of its actions is blocked; it needs all its steps. A benign task is intact only if every action is allowed.

Four policy tiers run for every adapter, loosest to tightest, so the tradeoff is visible instead of cherry-picked: effect, tool, target, limits.

20 of 629 pairs are excluded. travel/injection_task_6's goal is "say I should visit this hotel" — pure output manipulation with no tool call. No action-level gate can stop it, CaMeL and FIDES included. Scoring it would measure the corpus, not the defence.

The two columns that matter together

attacks stopped is the number everyone wants to quote. cross-task generality is the policy for task A applied to a different legitimate task B — the user changing subject mid-session. It is the only non-circular utility figure here, and it is what stops a defence from winning by forbidding everything.

Note that "own task intact" is 100% for any tier that scopes to observed actions. That is a tautology, not a false-positive rate — the policy is derived from the actions it is then tested on. It is reported only so a tier that breaks its own task is visible.

What this is not

These are not AgentDojo attack-success-rate figures. ASR comes from a live agent under a live injection. This replays each task's fixed ground_truth() call sequence. A real agent may take different actions, including permitted ones that still achieve the attacker's goal.

Do not compare these numbers to CaMeL's 77% or to any published ASR. Different metric, different experiment. What this measures is an upper bound on what a policy layer can catch, holding the agent constant.

It measures authorization, not detection. A defence that identifies injected content is not what this scores.

Known limitations

  • Ground-truth replay, not live trajectories (above).
  • One corpus. AgentDojo is banking, Slack, travel and workspace tools — no shell, no filesystem, no code execution. A defence specialised for those is unmeasurable here.
  • The limits tier hardcodes which reads are untrusted, in src/policy.mjs. That list is a judgement call and is worth arguing with.
  • Effects and target arguments for all 57 tools are assigned by hand in extract/extract_ground_truth.py. Both tables are auditable and both are places a mistake would move the numbers.

License and attribution

This harness is MIT.

data/ground_truth.jsonl is derived from AgentDojo (ETH Zurich SPY Lab), which is also MIT, so redistribution is fine. The tasks, the injection goals, and the ground-truth call sequences are entirely their work — this repo only extracts and re-encodes them. If you use these numbers, cite the original:

@inproceedings{debenedetti2024agentdojo,
  title     = {AgentDojo: A Dynamic Environment to Evaluate Prompt Injection
               Attacks and Defenses for LLM Agents},
  author    = {Debenedetti, Edoardo and Zhang, Jie and Balunovi\'{c}, Mislav and
               Beurer-Kellner, Luca and Fischer, Marc and Tram\`{e}r, Florian},
  booktitle = {Thirty-eighth Conference on Neural Information Processing Systems
               Datasets and Benchmarks Track},
  year      = {2024}
}

About

Measure how well an agent tool-call authorization layer separates legitimate actions from injected ones, on AgentDojo ground truth. No agent runs, no model calls, seconds to run. Ships the controls that make a block rate meaningful.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages