Everything the chapters quote, computed by the vendored engine as this page was built; the engine records the same run in policy_results.md. The tools, tasks, grants and sandbox actions are abstract placeholders; the published measurements are quoted from their papers.
Chapters 1 and 2: every task, two kinds of agent
| task | classic agent: allow / ask / deny | planned in advance: allow / ask / deny | last call, classic | last call, planned |
|---|
| Summarise my notes into a file | 3 / 0 / 0 | 3 / 0 / 0 | allow | allow |
| Read my inbox and notes, then reply to a colleague | 3 / 0 / 1 | 3 / 0 / 1 | deny | deny |
| Send a colleague a summary of my notes, after looking up a page | 2 / 1 / 1 | 3 / 1 / 0 | deny | ask |
| Send me a digest of my inbox | 3 / 0 / 0 | 3 / 0 / 0 | allow | allow |
| Read a web page and save a summary | 2 / 1 / 0 | 2 / 1 / 0 | ask | ask |
| Share a public ticket's status with a colleague | 3 / 0 / 0 | 3 / 0 / 0 | allow | allow |
Chapter 3: the trifecta
Over all 256 subsets of the 8 tools, 136 hold all three circles; the trifecta check and the exhaustive search for a flow out agree on 256 of 256.
| circles held | trifecta | flow out found | sequences checked |
|---|
| none | no | none | 0 |
| private | no | none | 0 |
| untrusted | no | none | 0 |
| private+untrusted | no | none | 0 |
| outbound | no | none | 2 |
| private+outbound | no | none | 6 |
| untrusted+outbound | no | none | 6 |
| private+untrusted+outbound | yes | read_notes → read_ticket → send_message (org) | 9 |
| read_inbox+send_message | yes | read_inbox → send_message (org) | 3 |
| read_notes+fetch_page | yes | read_notes → fetch_page → fetch_page (web) | 5 |
Chapter 4: least privilege
Summarise my notes into a file, and one call it does not need. Broad grant: 8 tools; least privilege: 3 (read_notes, summarise, write_file). Held but unused under the broad grant: read_inbox, read_ticket, search_docs, fetch_page, send_message.
| call | broad grant | least privilege | token, least privilege |
|---|
| read_notes | allow | allow | token for notes-api holds notes:read |
| summarise | allow | allow | summarise runs in the model; no token needed |
| write_file | allow | allow | token for files holds files:write |
| send_message | ask | deny | no token for mail-api |
Chapter 5: sandbox layers
| layers | needed, working | needed, broken | unneeded, stopped | unneeded, open |
|---|
| none | 3 | 0 | 0 | 5 |
| fs_scope | 3 | 0 | 2 | 3 |
| fs_scope + container | 3 | 0 | 3 | 2 |
| fs_scope + container + egress | 3 | 0 | 4 | 1 |
Part two's numbers are recorded in policy2_results.md.
Chapter 6: attempts through the detector layers
400 attempts, drawn with the engine's seeded generator; base rate 24%; expected values from the closed form in brackets.
| shared blind spot c | attempts | steer the model | + Provenance marking | + Input classifier | + Output check |
|---|
| 0.0% | 400 (400.00) | 91 (96.00) | 3 (3.84) | 2 (0.77) | 2 (0.23) |
| 0.5% | 400 (400.00) | 91 (96.00) | 4 (3.84) | 3 (1.14) | 3 (0.68) |
| 1.0% | 400 (400.00) | 91 (96.00) | 3 (3.84) | 2 (1.51) | 2 (1.12) |
| 2.0% | 400 (400.00) | 91 (96.00) | 4 (3.84) | 3 (2.27) | 3 (2.02) |
| 4.0% | 400 (400.00) | 91 (96.00) | 3 (3.84) | 3 (3.84) | 3 (3.84) |
Chapter 7: plan-then-execute
| task | planned: allow / ask / deny | classic agent: allow / ask / deny |
|---|
| Send a colleague a summary of my notes, after looking up a page | 3 / 1 / 0 | 2 / 1 / 1 |
| Read a web page and save a summary | 2 / 1 / 0 | 2 / 1 / 0 |
| Read my inbox and notes, then reply to a colleague | 3 / 0 / 1 | 3 / 0 / 1 |
| File a ticket's summary wherever its category says | 2 / 1 / 0 | 2 / 1 / 0 |
Chapter 8: approval thresholds over the label policy
| who must approve | tasks finished | prompts per task | risk per task | unchecked route |
|---|
| refuse | 45.8% | 0.00 | 11.60% | 48.3% |
| policy | 72.5% | 0.30 | 12.32% | 50.0% |
| consequential | 72.5% | 0.59 | 2.41% | 0.0% |
| every | 72.5% | 3.64 | 7.36% | 0.0% |
Chapter 9: the frontier, an attacker who adapts to the detectors
120 benign tasks; base rate 50%; every detector layer passed (arXiv 2503.00061).
| defence | tasks finished | blocked | prompts / task | extra calls / task | risk / task | on the frontier |
|---|
| No defence | 100.0% | 0.0% | 0.00 | 0.00 | 42.92% | yes |
| Provenance marking | 100.0% | 0.0% | 0.00 | 0.00 | 42.92% | yes |
| Three detector layers | 95.1% | 0.0% | 0.00 | 2.09 | 42.92% | no |
| Approve every write and send | 100.0% | 0.0% | 0.78 | 0.00 | 8.90% | yes |
| Label policy | 72.5% | 27.5% | 0.30 | 0.00 | 25.67% | no |
| Label policy, no approvals | 45.8% | 54.2% | 0.00 | 0.00 | 24.17% | yes |
| Label policy + least privilege | 72.5% | 27.5% | 0.30 | 0.00 | 16.59% | no |
| Plan-then-execute + policy + least privilege | 76.7% | 23.3% | 0.25 | 1.00 | 2.38% | yes |
Chapter 9: the frontier, fixed attacks (published rates)
120 benign tasks; base rate 24%.
| defence | tasks finished | blocked | prompts / task | extra calls / task | risk / task | on the frontier |
|---|
| No defence | 100.0% | 0.0% | 0.00 | 0.00 | 20.60% | no |
| Provenance marking | 100.0% | 0.0% | 0.00 | 0.00 | 0.82% | yes |
| Three detector layers | 95.1% | 0.0% | 0.00 | 2.09 | 0.24% | yes |
| Approve every write and send | 100.0% | 0.0% | 0.78 | 0.00 | 4.27% | no |
| Label policy | 72.5% | 27.5% | 0.30 | 0.00 | 12.32% | no |
| Label policy, no approvals | 45.8% | 54.2% | 0.00 | 0.00 | 11.60% | no |
| Label policy + least privilege | 72.5% | 27.5% | 0.30 | 0.00 | 7.96% | no |
| Plan-then-execute + policy + least privilege | 76.7% | 23.3% | 0.25 | 1.00 | 1.14% | no |
Chapter 10: the default design's checklist
| chapter | check | verdict | measured |
|---|
| 1 | Every value carries a label, and every call is checked against the policy | pass | labels tracked |
| 2 | No untrusted result can choose a write or send that nothing checks | warn | 4.2% of tasks have an unchecked route |
| 3 | No task holds private data, untrusted input and an outbound channel unpoliced | warn | 52 of 120 tasks hold all three, and the policy denies the flow out |
| 4 | Each task holds only the tools and scopes it needs | pass | 0.00 unneeded tools per task |
| 5 | Code runs in a sandbox with a file scope, a container and an egress allow-list | pass | 1 unneeded action still open, 0 needed broken |
| 6 | Detectors are one layer, never the only one | pass | no detectors needed: the policy decides on labels |
| 7 | Untrusted data cannot change the plan | pass | planned in advance |
| 8 | Few prompts per task, so each approval gets attention | pass | 0.25 prompts per task |
| 9 | The design sits on the security–utility frontier, within the risk budget | pass | 76.7% of tasks finish; risk 2.38% per task against an adaptive attacker |
Published measurements
| measurement | value | source |
|---|
| InjecAgent: share of its test cases in which a ReAct-prompted GPT-4 agent was steered by text in a tool result (base setting; the enhanced setting nearly doubled it) | 24% | arXiv 2403.02691 |
| InjecAgent: test cases (17 user tools, 62 attacker tools) | 1,054 | arXiv 2403.02691 |
| Spotlighting: attack success rate without the defence (GPT-family models; a lower bound) | > 50% | arXiv 2403.14720 |
| Spotlighting: attack success rate with it (an upper bound) | < 2% | arXiv 2403.14720 |
| AgentDojo: realistic user tasks | 97 | arXiv 2406.13352 |
| AgentDojo: security test cases | 629 | arXiv 2406.13352 |
| CaMeL: AgentDojo tasks solved with provable security | 77% | arXiv 2503.18813 |
| CaMeL: the same tasks solved by the undefended system | 84% | arXiv 2503.18813 |