agent-threat-rules 4.1.5 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/CHANGELOG.md +688 -1
  2. package/README.md +355 -125
  3. package/dist/adapters/openshell-filter.d.ts +10 -3
  4. package/dist/adapters/openshell-filter.d.ts.map +1 -1
  5. package/dist/adapters/openshell-filter.js +10 -3
  6. package/dist/adapters/openshell-filter.js.map +1 -1
  7. package/dist/adapters/stdio-adapter.js +3 -3
  8. package/dist/adapters/stdio-adapter.js.map +1 -1
  9. package/dist/cli/mcp-tools-document.d.ts +40 -0
  10. package/dist/cli/mcp-tools-document.d.ts.map +1 -0
  11. package/dist/cli/mcp-tools-document.js +95 -0
  12. package/dist/cli/mcp-tools-document.js.map +1 -0
  13. package/dist/cli/scan-handler.d.ts +8 -0
  14. package/dist/cli/scan-handler.d.ts.map +1 -1
  15. package/dist/cli/scan-handler.js +126 -34
  16. package/dist/cli/scan-handler.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +80 -43
  19. package/dist/cli.js.map +1 -1
  20. package/dist/converters/sarif.d.ts +14 -2
  21. package/dist/converters/sarif.d.ts.map +1 -1
  22. package/dist/converters/sarif.js +7 -9
  23. package/dist/converters/sarif.js.map +1 -1
  24. package/dist/corpus-event.d.ts +47 -5
  25. package/dist/corpus-event.d.ts.map +1 -1
  26. package/dist/corpus-event.js +68 -29
  27. package/dist/corpus-event.js.map +1 -1
  28. package/dist/embedding/model-loader.d.ts +2 -0
  29. package/dist/embedding/model-loader.d.ts.map +1 -1
  30. package/dist/embedding/model-loader.js +3 -1
  31. package/dist/embedding/model-loader.js.map +1 -1
  32. package/dist/enforcement.d.ts +8 -0
  33. package/dist/enforcement.d.ts.map +1 -1
  34. package/dist/enforcement.js +10 -0
  35. package/dist/enforcement.js.map +1 -1
  36. package/dist/engine.d.ts +150 -6
  37. package/dist/engine.d.ts.map +1 -1
  38. package/dist/engine.js +337 -76
  39. package/dist/engine.js.map +1 -1
  40. package/dist/eval/embedding-provenance.d.ts +40 -0
  41. package/dist/eval/embedding-provenance.d.ts.map +1 -0
  42. package/dist/eval/embedding-provenance.js +71 -0
  43. package/dist/eval/embedding-provenance.js.map +1 -0
  44. package/dist/eval/eval-harness.d.ts +34 -0
  45. package/dist/eval/eval-harness.d.ts.map +1 -1
  46. package/dist/eval/eval-harness.js +49 -2
  47. package/dist/eval/eval-harness.js.map +1 -1
  48. package/dist/eval/hackaprompt-corpus.d.ts +12 -0
  49. package/dist/eval/hackaprompt-corpus.d.ts.map +1 -1
  50. package/dist/eval/hackaprompt-corpus.js +15 -1
  51. package/dist/eval/hackaprompt-corpus.js.map +1 -1
  52. package/dist/eval/hackaprompt-provenance.d.ts +52 -0
  53. package/dist/eval/hackaprompt-provenance.d.ts.map +1 -0
  54. package/dist/eval/hackaprompt-provenance.js +108 -0
  55. package/dist/eval/hackaprompt-provenance.js.map +1 -0
  56. package/dist/eval/run-hackaprompt-benchmark.d.ts +18 -8
  57. package/dist/eval/run-hackaprompt-benchmark.d.ts.map +1 -1
  58. package/dist/eval/run-hackaprompt-benchmark.js +154 -64
  59. package/dist/eval/run-hackaprompt-benchmark.js.map +1 -1
  60. package/dist/eval/run-pint-benchmark.js +11 -1
  61. package/dist/eval/run-pint-benchmark.js.map +1 -1
  62. package/dist/eval/skill-benchmark.d.ts +26 -6
  63. package/dist/eval/skill-benchmark.d.ts.map +1 -1
  64. package/dist/eval/skill-benchmark.js +49 -16
  65. package/dist/eval/skill-benchmark.js.map +1 -1
  66. package/dist/flywheel.d.ts +4 -0
  67. package/dist/flywheel.d.ts.map +1 -1
  68. package/dist/flywheel.js +12 -4
  69. package/dist/flywheel.js.map +1 -1
  70. package/dist/hook-handler.d.ts +11 -1
  71. package/dist/hook-handler.d.ts.map +1 -1
  72. package/dist/hook-handler.js +84 -16
  73. package/dist/hook-handler.js.map +1 -1
  74. package/dist/hook-tool-output.d.ts +47 -0
  75. package/dist/hook-tool-output.d.ts.map +1 -0
  76. package/dist/hook-tool-output.js +110 -0
  77. package/dist/hook-tool-output.js.map +1 -0
  78. package/dist/index.d.ts +5 -3
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +3 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/layer-integration.d.ts +4 -2
  83. package/dist/layer-integration.d.ts.map +1 -1
  84. package/dist/layer-integration.js +6 -2
  85. package/dist/layer-integration.js.map +1 -1
  86. package/dist/mcp-server.d.ts +8 -0
  87. package/dist/mcp-server.d.ts.map +1 -1
  88. package/dist/mcp-server.js +1 -0
  89. package/dist/mcp-server.js.map +1 -1
  90. package/dist/mcp-tools/scan.d.ts.map +1 -1
  91. package/dist/mcp-tools/scan.js +9 -1
  92. package/dist/mcp-tools/scan.js.map +1 -1
  93. package/dist/measurement/enforce-lane.d.ts +66 -0
  94. package/dist/measurement/enforce-lane.d.ts.map +1 -0
  95. package/dist/measurement/enforce-lane.js +89 -0
  96. package/dist/measurement/enforce-lane.js.map +1 -0
  97. package/dist/measurement/from-eval-harness.d.ts +16 -3
  98. package/dist/measurement/from-eval-harness.d.ts.map +1 -1
  99. package/dist/measurement/from-eval-harness.js +15 -5
  100. package/dist/measurement/from-eval-harness.js.map +1 -1
  101. package/dist/measurement/in-sample-ablation.d.ts +29 -0
  102. package/dist/measurement/in-sample-ablation.d.ts.map +1 -0
  103. package/dist/measurement/in-sample-ablation.js +45 -0
  104. package/dist/measurement/in-sample-ablation.js.map +1 -0
  105. package/dist/measurement/schema.d.ts +38 -1
  106. package/dist/measurement/schema.d.ts.map +1 -1
  107. package/dist/measurement/schema.js +43 -1
  108. package/dist/measurement/schema.js.map +1 -1
  109. package/dist/quality/action-eligibility.d.ts +4 -2
  110. package/dist/quality/action-eligibility.d.ts.map +1 -1
  111. package/dist/quality/action-eligibility.js.map +1 -1
  112. package/dist/rule-confidence.d.ts +31 -0
  113. package/dist/rule-confidence.d.ts.map +1 -0
  114. package/dist/rule-confidence.js +49 -0
  115. package/dist/rule-confidence.js.map +1 -0
  116. package/dist/rule-scaffolder.d.ts.map +1 -1
  117. package/dist/rule-scaffolder.js +43 -4
  118. package/dist/rule-scaffolder.js.map +1 -1
  119. package/dist/semantic-evaluator.d.ts.map +1 -1
  120. package/dist/semantic-evaluator.js +15 -13
  121. package/dist/semantic-evaluator.js.map +1 -1
  122. package/dist/shadow-evaluator.d.ts +7 -2
  123. package/dist/shadow-evaluator.d.ts.map +1 -1
  124. package/dist/shadow-evaluator.js +29 -25
  125. package/dist/shadow-evaluator.js.map +1 -1
  126. package/dist/testcase-event.d.ts +82 -1
  127. package/dist/testcase-event.d.ts.map +1 -1
  128. package/dist/testcase-event.js +135 -16
  129. package/dist/testcase-event.js.map +1 -1
  130. package/dist/tier0-invariant.d.ts.map +1 -1
  131. package/dist/tier0-invariant.js +1 -0
  132. package/dist/tier0-invariant.js.map +1 -1
  133. package/dist/tier1-blacklist.d.ts.map +1 -1
  134. package/dist/tier1-blacklist.js +1 -0
  135. package/dist/tier1-blacklist.js.map +1 -1
  136. package/dist/tool-definition-scope.d.ts +45 -0
  137. package/dist/tool-definition-scope.d.ts.map +1 -0
  138. package/dist/tool-definition-scope.js +68 -0
  139. package/dist/tool-definition-scope.js.map +1 -0
  140. package/dist/types.d.ts +42 -2
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/verdict.d.ts +46 -2
  143. package/dist/verdict.d.ts.map +1 -1
  144. package/dist/verdict.js +99 -14
  145. package/dist/verdict.js.map +1 -1
  146. package/package.json +2 -1
  147. package/rules/agent-manipulation/ATR-2026-00030-cross-agent-attack.yaml +2 -2
  148. package/rules/agent-manipulation/ATR-2026-00139-casual-authority-redirect.yaml +1 -1
  149. package/rules/agent-manipulation/ATR-2026-00328-named-mode-persona-override.yaml +1 -1
  150. package/rules/agent-manipulation/ATR-2026-00330-yoja-allcaps-settings-block-persona.yaml +1 -1
  151. package/rules/agent-manipulation/ATR-2026-00418-weknora-mcp-config-rce.yaml +5 -5
  152. package/rules/agent-manipulation/ATR-2026-00432-superagi-output-handler-eval-rce.yaml +1 -1
  153. package/rules/agent-manipulation/ATR-2026-00440-semantic-kernel-vector-store-eval-rce.yaml +3 -3
  154. package/rules/context-exfiltration/ATR-2026-00426-nl-output-injection-credential-leak.yaml +1 -1
  155. package/rules/context-exfiltration/ATR-2026-00702-ipi-credential-exfil-via-agent-action.yaml +1 -1
  156. package/rules/context-exfiltration/ATR-2026-00707-ipi-file-exfil-with-destroy-or-tamper.yaml +1 -1
  157. package/rules/context-exfiltration/ATR-2026-00851-indirect-pi-financial-payment-exfil.yaml +1 -1
  158. package/rules/context-exfiltration/ATR-2026-01457-sysprompt-completion-clone-attack.yaml +1 -1
  159. package/rules/context-exfiltration/ATR-2026-01462-sysprompt-override-pseudo-admin.yaml +1 -1
  160. package/rules/context-exfiltration/ATR-2026-01463-imend-imsystem-prompt-injection-format.yaml +1 -1
  161. package/rules/context-exfiltration/ATR-2026-01606-ssrf-internal-network-scan.yaml +1 -1
  162. package/rules/context-exfiltration/ATR-2026-02847-policy-justified-exfil-to-email.yaml +1 -1
  163. package/rules/context-exfiltration/ATR-2026-02855-cross-email-exfil-relay.yaml +79 -0
  164. package/rules/context-exfiltration/ATR-2026-02861-exfil-send-email-with-body-directive.yaml +83 -0
  165. package/rules/context-exfiltration/ATR-2026-02866-idna-label-separator-in-fetch-url-host.yaml +104 -0
  166. package/rules/excessive-autonomy/ATR-2026-00710-ipi-physical-access-grant.yaml +1 -1
  167. package/rules/model-abuse/ATR-2026-01897-host-sabotage.yaml +2 -2
  168. package/rules/model-security/ATR-2026-00433-modelcache-torch-load-deserialization-rce.yaml +1 -1
  169. package/rules/privilege-escalation/ATR-2026-00436-enclave-vm-sandbox-escape-rce.yaml +1 -1
  170. package/rules/privilege-escalation/ATR-2026-01601-sql-injection-destructive-ddl.yaml +2 -2
  171. package/rules/privilege-escalation/ATR-2026-01612-debug-mode-privilege-escalation.yaml +1 -1
  172. package/rules/privilege-escalation/ATR-2026-02867-identifier-typed-tool-argument-dot-dot-path-segment.yaml +106 -0
  173. package/rules/prompt-injection/ATR-2026-00004-system-prompt-override.yaml +2 -2
  174. package/rules/prompt-injection/ATR-2026-00080-encoding-evasion.yaml +1 -1
  175. package/rules/prompt-injection/ATR-2026-00086-visual-spoofing.yaml +1 -1
  176. package/rules/prompt-injection/ATR-2026-00203-context-pollution-skill-description.yaml +1 -1
  177. package/rules/prompt-injection/ATR-2026-00228-structured-jailbreak.yaml +2 -2
  178. package/rules/prompt-injection/ATR-2026-00233-structured-jailbreak.yaml +2 -2
  179. package/rules/prompt-injection/ATR-2026-00256-base-n-encoding-jailbreak.yaml +1 -1
  180. package/rules/prompt-injection/ATR-2026-00264-latent-injection-translation.yaml +3 -3
  181. package/rules/prompt-injection/ATR-2026-00265-latent-injection-rag-document.yaml +1 -1
  182. package/rules/prompt-injection/ATR-2026-00282-perez-prompt-injection-hijack.yaml +4 -4
  183. package/rules/prompt-injection/ATR-2026-00321-hyphenated-system-instruction-injection.yaml +1 -1
  184. package/rules/prompt-injection/ATR-2026-00394-backspace-deletion-character-injection.yaml +2 -2
  185. package/rules/prompt-injection/ATR-2026-00446-variable-assignment-payload-injection.yaml +3 -3
  186. package/rules/prompt-injection/ATR-2026-00476-garak-web-injection-javascript-alert-1.yaml +3 -3
  187. package/rules/prompt-injection/ATR-2026-00481-garak-dan-mixedunassigned.yaml +1 -1
  188. package/rules/prompt-injection/ATR-2026-00489-garak-encoding-javascript-alert-1.yaml +3 -3
  189. package/rules/prompt-injection/ATR-2026-00508-escape-delimiter-wrapped-goal-hijacking-in-user-input-prompt.yaml +3 -3
  190. package/rules/prompt-injection/ATR-2026-00512-rules-file-backdoor-injection.yaml +1 -1
  191. package/rules/prompt-injection/ATR-2026-01005-many-shot-repetition-override.yaml +2 -2
  192. package/rules/prompt-injection/ATR-2026-01006-payload-split-string-concat-injection.yaml +2 -2
  193. package/rules/prompt-injection/ATR-2026-01015-evasion-refusal-suppression.yaml +1 -1
  194. package/rules/prompt-injection/ATR-2026-01016-evasion-response-priming-forced-opener.yaml +1 -1
  195. package/rules/prompt-injection/ATR-2026-01026-evasion-unicode-glitch-token-anomaly.yaml +2 -2
  196. package/rules/prompt-injection/ATR-2026-01304-indirect-injection-carrier-important-override.yaml +1 -1
  197. package/rules/prompt-injection/ATR-2026-01759-dos-new-important-instructions-injection-in-document.yaml +1 -1
  198. package/rules/prompt-injection/ATR-2026-01865-llmail-fake-email-boundary-marker.yaml +1 -1
  199. package/rules/prompt-injection/ATR-2026-02010-emoji-only-payload.yaml +2 -2
  200. package/rules/prompt-injection/ATR-2026-02815-injected-halt-order-canned-reply-dos.yaml +80 -0
  201. package/rules/prompt-injection/ATR-2026-02820-tool-result-orders-agent-to-hide-its-own-action.yaml +106 -0
  202. package/rules/prompt-injection/ATR-2026-02858-context-suppression-directive.yaml +73 -0
  203. package/rules/prompt-injection/ATR-2026-02859-tool-output-agent-role-reassignment.yaml +71 -0
  204. package/rules/prompt-injection/ATR-2026-02860-forged-instruction-boundary-marker.yaml +70 -0
  205. package/rules/prompt-injection/ATR-2026-02862-fake-chat-template-turn-injection.yaml +79 -0
  206. package/rules/skill-compromise/ATR-2026-00060-skill-impersonation.yaml +1 -1
  207. package/rules/skill-compromise/ATR-2026-00063-skill-chain-attack.yaml +2 -2
  208. package/rules/skill-compromise/ATR-2026-00066-parameter-injection.yaml +2 -2
  209. package/rules/skill-compromise/ATR-2026-00149-skill-exfil-compound.yaml +6 -6
  210. package/rules/skill-compromise/ATR-2026-00223-reverse-shell-dropper.yaml +2 -2
  211. package/rules/skill-compromise/ATR-2026-01755-backdoor-pot-linguistic-trigger-phrase.yaml +1 -1
  212. package/rules/skill-compromise/ATR-2026-01756-backdoor-pot-symbol-emoticon-trigger.yaml +1 -1
  213. package/rules/skill-compromise/ATR-2026-02261-code-credential-exfil-chain.yaml +1 -1
  214. package/rules/tool-poisoning/ATR-2026-00012-unauthorized-tool-call.yaml +23 -23
  215. package/rules/tool-poisoning/ATR-2026-00013-tool-ssrf.yaml +16 -5
  216. package/rules/tool-poisoning/ATR-2026-00161-important-tag-cross-tool-shadowing.yaml +1 -1
  217. package/rules/tool-poisoning/ATR-2026-00277-echo-template-command-injection.yaml +2 -2
  218. package/rules/tool-poisoning/ATR-2026-00494-garak-exploitation-mixedunassigned.yaml +4 -4
  219. package/rules/tool-poisoning/ATR-2026-00581-mcp-tool-rug-pull-post-approval-redefinition.yaml +1 -1
  220. package/rules/tool-poisoning/ATR-2026-01303-tool-schema-enumeration-social-engineering.yaml +1 -1
  221. package/rules/tool-poisoning/ATR-2026-01307-mcp-dns-rebinding-attack.yaml +2 -2
  222. package/rules/tool-poisoning/ATR-2026-01775-semantic-mcp-tool-manifest-poisoning.yaml +1 -1
  223. package/rules/tool-poisoning/ATR-2026-01932-shadow-undeclared-mcp-server-registration.yaml +1 -1
  224. package/rules/tool-poisoning/ATR-2026-02025-mcp-full-schema-poisoning-nondescription-field.yaml +1 -1
  225. package/rules/tool-poisoning/ATR-2026-02817-untrusted-content-dictates-tool-arguments.yaml +83 -0
  226. package/rules/tool-poisoning/ATR-2026-02864-non-canonical-ipv4-host-in-url-tool-argument.yaml +102 -0
  227. package/rules/tool-poisoning/ATR-2026-02865-read-only-utility-write-flag-in-command-argument.yaml +157 -0
  228. package/rules/tool-poisoning/ATR-2026-02868-ssrf-ipv6-transition-address-embedded-private-ipv4.yaml +107 -0
  229. package/spec/README.md +6 -3
  230. package/spec/atr-event-v1.0.md +13 -8
  231. package/spec/atr-method-v1.1.md +2 -2
  232. package/spec/category-registry/gemara/README.md +102 -0
  233. package/spec/category-registry/gemara/ai-agent-capability-catalog.yaml +157 -0
  234. package/spec/category-registry/gemara/atr-attack-vector-catalog.yaml +130 -0
  235. package/spec/category-registry/gemara/atr-detection-control-catalog.yaml +327 -0
  236. package/spec/category-registry/gemara/atr-threat-catalog.yaml +246 -0
  237. package/spec/category-registry/gemara/src/editorial.yaml +743 -0
  238. package/spec/mappings/README.md +1 -1
  239. package/spec/mappings/atr-to-nist-csf-2.0.md +5 -4
  240. package/spec/schema/event.schema.json +1 -1
package/README.md CHANGED
@@ -13,10 +13,9 @@ AI Agent 威脅偵測規則的開放格式
13
13
  [![GitHub Marketplace](https://img.shields.io/badge/Marketplace-ATR%20Scan-2ea44f?style=flat-square&logo=github)](https://github.com/marketplace/actions/atr-scan)
14
14
  [![License: MIT](https://img.shields.io/badge/license-MIT-brightgreen?style=flat-square)](LICENSE)
15
15
  [![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.19178002-blue?style=flat-square)](https://doi.org/10.5281/zenodo.19178002)
16
- [![Rules](https://img.shields.io/badge/rules-829-blue?style=flat-square)](#5-specification)
16
+ [![Rules](https://img.shields.io/badge/rules-843-blue?style=flat-square)](#5-specification)
17
17
  [![Categories](https://img.shields.io/badge/categories-10-blue?style=flat-square)](#7-coverage)
18
18
  [![OWASP Agentic](https://img.shields.io/badge/OWASP_Agentic_Top_10-10%2F10-brightgreen?style=flat-square)](#7-coverage)
19
- [![SAFE-MCP](https://img.shields.io/badge/SAFE--MCP-91.8%25-brightgreen?style=flat-square)](#7-coverage)
20
19
  [![Sponsor](https://img.shields.io/badge/sponsor-Open%20Collective-7FADF2?style=flat-square&logo=opencollective&logoColor=white)](https://opencollective.com/agent-threat-rules)
21
20
 
22
21
  </div>
@@ -29,7 +28,7 @@ ATR (Agent Threat Rules) is an open detection rule format for AI agent security
29
28
 
30
29
  ## Status of This Document
31
30
 
32
- ATR is published as a **Working Draft** at version `4.1.4`. The rule format defined in `SPEC.md` is stable and merged into open-source repos at Microsoft, Cisco, and Gen Digital, and integrated by standards-body projects (MISP / CIRCL, OWASP Agent Security Regression Harness, SigmaHQ, FINOS Common Cloud Controls); full list with PR links in [§6 Adoption](#6-adoption). Governance is currently single-maintainer (BDFL) transitioning to a Technical Steering Committee per [GOVERNANCE.md](GOVERNANCE.md).
31
+ ATR is published as a **Working Draft** at version `4.1.5`. The rule format defined in `SPEC.md` is stable and merged into open-source repos at Microsoft, Cisco, and Gen Digital, and integrated by community projects (MISP / CIRCL, OWASP Agent Security Regression Harness, SigmaHQ, FINOS Common Cloud Controls); full list with PR links in [§6 Adoption](#6-adoption). Governance is currently single-maintainer (BDFL) transitioning to a Technical Steering Committee per [GOVERNANCE.md](GOVERNANCE.md).
33
32
 
34
33
  All numbers in this document are sourced from [`data/stats.json`](data/stats.json), which is the canonical record of the project's current state. Where this README and `stats.json` disagree, `stats.json` is authoritative.
35
34
 
@@ -156,6 +155,7 @@ Zero-install scan of the current directory; the image bundles the CLI and pulls
156
155
  ```bash
157
156
  atr scan skill.md # scan a SKILL.md file
158
157
  atr scan mcp-config.json # scan MCP server config / event log
158
+ atr scan tools.json # scan an MCP tools/list result, one event per tool
159
159
  atr scan . --sarif > results.sarif
160
160
  atr convert generic-regex # export rules as JSON (all patterns)
161
161
  atr convert splunk # export to Splunk SPL
@@ -269,17 +269,20 @@ would be more dangerous than the request.
269
269
 
270
270
  ```bash
271
271
  atr guard # advisory: report only (the default)
272
- atr guard --lane enforce --blocking # blocking on, and only the 106 of 777
273
- # live rules that are maturity: stable
274
- # may fire (see the recall note below)
272
+ atr guard --lane enforce --blocking # blocking on, and only the live rules
273
+ # that are maturity: stable may fire
274
+ # (see the recall note below)
275
275
  ATR_LANE=enforce ATR_BLOCKING=1 atr guard # the same, via the environment
276
276
  ```
277
277
 
278
278
  **`--lane enforce` costs recall, and the cost is large.** It loads only
279
- `maturity: stable` rules: **106 of the 777 live rules** in this repository at the
280
- commit this paragraph was written against. That is the trade being made every
281
- time enforcement is recommended alongside it — narrower firing set, lower
282
- false-positive rate, fewer attacks caught. Rule counts move daily, and a
279
+ `maturity: stable` rules: **103 of the 823 live rules** at ATR 4.1.5, and each
280
+ weekly measurement records the count it ran with as `rules_in_lane` in its
281
+ `breakdown.enforce_lane`. That is the trade being made every time enforcement is
282
+ recommended alongside it — narrower firing set, lower false-positive rate, fewer
283
+ attacks caught. What it still catches, per corpus, is the second (`enforce`)
284
+ figure in the `Recall` cells of [§8 Evaluation](#8-evaluation). Rule counts move
285
+ daily, and a
283
286
  `grep`-based count is wrong here (eight rules quote the value as
284
287
  `maturity: "stable"`), so re-derive by parsing before quoting the figure
285
288
  anywhere:
@@ -299,9 +302,12 @@ PY
299
302
 
300
303
  Programmatic embedding: `new ATREngine({ lane })` for the lane,
301
304
  `new ActionExecutor({ adapter, blocking })` and
302
- `new HookHandler({ engine, executor, blocking })` for enforcement. **These are
303
- the only inputs** — the constructors ignore `ATR_LANE` and `ATR_BLOCKING`, so
304
- setting them will not configure an embedded engine. An unrecognised lane throws
305
+ `new HookHandler({ engine, executor, blocking })` for enforcement, and
306
+ `new ATREngine({ modelDecisions })` for whether a match a model decided (a
307
+ semantic judge, the embedding signal, the semantic layer) may enforce; it is
308
+ advisory by default. **These are the only inputs** — the constructors ignore
309
+ `ATR_LANE`, `ATR_BLOCKING` and `ATR_MODEL_DECISIONS`, so setting them will not
310
+ configure an embedded engine. An unrecognised lane throws
305
311
  a `TypeError`, and so does a non-boolean `blocking`: `blocking: "false"` is a
306
312
  string, and every non-empty string is truthy, so it used to switch enforcement
307
313
  **on** while reading as an explicit "off".
@@ -359,11 +365,11 @@ test_cases:
359
365
 
360
366
  ## 6. Adoption
361
367
 
362
- Production deployments and standards-body integrations, as of 2026-07-05 (every PR state re-verified against GitHub on that date):
368
+ Merged integrations, as of 2026-07-05 (every PR state re-verified against GitHub on that date):
363
369
 
364
370
  | Organization | Integration | Reference |
365
371
  |---|---|---|
366
- | Microsoft Agent Governance Toolkit | 287-rule expansion + weekly auto-sync (merged 2026-04-26); 15-rule PoC (merged 2026-04-13) | [PR #1277](https://github.com/microsoft/agent-governance-toolkit/pull/1277) · [PR #908](https://github.com/microsoft/agent-governance-toolkit/pull/908) |
372
+ | Microsoft Agent Governance Toolkit | Example directories in the AGT repository (ATR import example, merged 2026-05-18; ACS annotator adapter example, merged 2026-06-15). The rule-pack YAML from the earlier example PRs (#908, #1277) was removed upstream on 2026-07-30 (AGT PR #3451). | [PR #3025](https://github.com/microsoft/agent-governance-toolkit/pull/3025) · [PR #2308](https://github.com/microsoft/agent-governance-toolkit/pull/2308) |
367
373
  | Cisco AI Defense (skill-scanner) | Full rule pack merged into the open-source scanner repository (2026-04-22); original PoC (merged 2026-04-03). This is a merge into that repository, not a statement about any vendor product. | [PR #99](https://github.com/cisco-ai-defense/skill-scanner/pull/99) · [PR #79](https://github.com/cisco-ai-defense/skill-scanner/pull/79) |
368
374
  | MISP (CIRCL) | Threat-intel cluster (galaxy, merged 2026-05-10) + rule-ID tagging vocabulary (taxonomies, merged 2026-05-10) | [galaxy #1207](https://github.com/MISP/misp-galaxy/pull/1207) · [taxonomies #323](https://github.com/MISP/misp-taxonomies/pull/323) |
369
375
  | Gen Digital Sage (Norton / Avast / AVG parent) | Rule pack merged 2026-05-11 | [PR #33](https://github.com/gendigitalinc/sage/pull/33) |
@@ -376,13 +382,13 @@ Production deployments and standards-body integrations, as of 2026-07-05 (every
376
382
 
377
383
  ### Featured loop — Microsoft Copilot SWE Agent → ATR (2026-05-11)
378
384
 
379
- On 2026-05-07 MSRC published two Semantic Kernel CVEs (CVE-2026-26030 lambda+eval RCE, CVE-2026-25592 autostart file write). On 2026-05-11 06:07 UTC, Microsoft Copilot SWE Agent opened [microsoft/agent-governance-toolkit#1981](https://github.com/microsoft/agent-governance-toolkit/pull/1981) with regression-test fixtures *presuming ATR detection*. At 08:24 UTC the same day, ATR v2.1.2 (rules ATR-2026-00440 + ATR-2026-00441) was merged, npm-published, and GitHub-released. End-to-end: 2h 16m.
385
+ On 2026-05-07 MSRC published two Semantic Kernel CVEs (CVE-2026-26030 lambda+eval RCE, CVE-2026-25592 autostart file write). On 2026-05-10 the ATR maintainer filed [microsoft/agent-governance-toolkit#1937](https://github.com/microsoft/agent-governance-toolkit/issues/1937) mapping both CVEs to ATR rules. On 2026-05-11 06:07 UTC, after an AGT maintainer assigned that issue to it, Microsoft Copilot SWE Agent opened [microsoft/agent-governance-toolkit#1981](https://github.com/microsoft/agent-governance-toolkit/pull/1981), adding regression-test fixtures *presuming ATR detection* to the ATR community-rules example's test suite. By 08:24 UTC the same day, ATR v2.1.2 (rules ATR-2026-00440 + ATR-2026-00441) was merged, npm-published, and GitHub-released. PR open to rule merge: 2h 16m.
380
386
 
381
387
  This is Microsoft Copilot operating inside AGT, not an MSRC endorsement. Coverage is partial: 2 of 4 Copilot fixtures match the v2.1.2 canonical regex shape.
382
388
 
383
389
  ### Under maintainer review (open PRs)
384
390
 
385
- [NVIDIA garak #1676](https://github.com/NVIDIA/garak/pull/1676) · [NVIDIA NeMo Guardrails #1992](https://github.com/NVIDIA-NeMo/Guardrails/pull/1992) · [OWASP LLM Top 10 #814](https://github.com/OWASP/www-project-top-10-for-large-language-model-applications/pull/814) · [OWASP AI Exchange #181](https://github.com/OWASP/www-project-ai-security-and-privacy-guide/pull/181) · [Meta PurpleLlama #206](https://github.com/meta-llama/PurpleLlama/pull/206) · [BerriAI LiteLLM #28050](https://github.com/BerriAI/litellm/pull/28050) · [promptfoo #8529](https://github.com/promptfoo/promptfoo/pull/8529) · [Microsoft agent-framework #6528](https://github.com/microsoft/agent-framework/pull/6528) · [OpenAI guardrails-python #77](https://github.com/openai/openai-guardrails-python/pull/77) · [Cisco mcp-scanner #194](https://github.com/cisco-ai-defense/mcp-scanner/pull/194) · [Cisco a2a-scanner #14](https://github.com/cisco-ai-defense/a2a-scanner/pull/14) · [Splunk security_content #4128](https://github.com/splunk/security_content/pull/4128) · [NIST OSCAL oscal-content #338](https://github.com/usnistgov/oscal-content/pull/338) · [OpenTelemetry semantic-conventions-genai #165](https://github.com/open-telemetry/semantic-conventions-genai/pull/165)
391
+ [NVIDIA NeMo Guardrails #2251](https://github.com/NVIDIA-NeMo/Guardrails/pull/2251) · [BerriAI LiteLLM #28050](https://github.com/BerriAI/litellm/pull/28050) · [promptfoo #8529](https://github.com/promptfoo/promptfoo/pull/8529) · [Cisco mcp-scanner #194](https://github.com/cisco-ai-defense/mcp-scanner/pull/194) · [Splunk security_content #4128](https://github.com/splunk/security_content/pull/4128) · [NIST OSCAL oscal-content #338](https://github.com/usnistgov/oscal-content/pull/338) (awaiting NIST review) · [OpenTelemetry semantic-conventions-genai #165](https://github.com/open-telemetry/semantic-conventions-genai/pull/165)
386
392
 
387
393
  ### Integrating ATR into your project
388
394
 
@@ -405,10 +411,10 @@ ATR maps its rules onto established frameworks so adopters can answer "we deploy
405
411
  | Framework | Coverage | Mapping document |
406
412
  |---|---|---|
407
413
  | [OWASP Agentic Top 10 (2026)](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/) | 10/10 categories, 1,179 mappings across all 683 tagged rules | [docs/OWASP-AGENTIC-MAPPING.md](docs/OWASP-AGENTIC-MAPPING.md) |
408
- | [SAFE-MCP (OpenSSF)](https://github.com/safe-agentic-framework/safe-mcp) | 78/85 techniques (91.8%) | [docs/SAFE-MCP-MAPPING.md](docs/SAFE-MCP-MAPPING.md) |
414
+ | [SAF-MCP](https://github.com/secure-agentic-framework/saf-mcp) (Framework Model v2) | 55 of 78 active techniques have at least one reviewed ATR rule (partial or adjacent); candidate mappings, not validated by SAF | [docs/SAFE-MCP-MAPPING.md](docs/SAFE-MCP-MAPPING.md) |
409
415
  | [OWASP LLM Top 10 (2025)](https://owasp.org/www-project-top-10-for-large-language-model-applications/) | Per-rule references | Per-rule `references.owasp_llm` field |
410
416
  | [MITRE ATLAS](https://atlas.mitre.org/) | Per-rule references | Per-rule `references.mitre_atlas` field |
411
- | NIST AI RMF (community OSCAL catalog) | 4/4 functions covered, community catalog (NIST not endorsing) | [Agent-Threat-Rule/ai-rmf-oscal-catalog](https://github.com/Agent-Threat-Rule/ai-rmf-oscal-catalog) |
417
+ | NIST AI RMF (community OSCAL catalog) | Per-rule subcategory mappings; community catalog, not endorsed by NIST | [Agent-Threat-Rule/ai-rmf-oscal-catalog](https://github.com/Agent-Threat-Rule/ai-rmf-oscal-catalog) |
412
418
  | Five Eyes joint guidance (2026-05-01) | 5-category Careful-Adoption guidance → ATR's 10 categories | [docs/FIVE-EYES-MAPPING.md](docs/FIVE-EYES-MAPPING.md) |
413
419
 
414
420
  ### Detection categories
@@ -444,7 +450,9 @@ A full list lives in each rule's `references.cve` field. See [LIMITATIONS.md](LI
444
450
 
445
451
  ## 8. Evaluation
446
452
 
447
- Every number below is a version-pinned, reproducible measurement. The full
453
+ Every number in the table below is a version-pinned, reproducible measurement;
454
+ figures in the prose and footnotes are either quoted from those measurements or
455
+ are named one-off replays that say how they were produced. The full
448
456
  historical series for each source lives at
449
457
  [`data/measurements/<source>/`](data/measurements/) (immutable, append-only).
450
458
  The current pointer per source is `data/measurements/<source>/latest.json`.
@@ -452,35 +460,75 @@ Aggregated into [`data/stats.json`](data/stats.json) under `benchmarks[]`.
452
460
 
453
461
  | Source | Source version | Samples | Recall | Precision | FP rate | ATR version | Measured |
454
462
  |---|---|---:|---:|---:|---:|---|---|
455
- | AdvBench (LLM-attacks behaviors) | upstream-2026-06-16 | 520 | 2.1% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
456
- | atr-self-test | internal | 341 | 96.6% | 100.0% | 0.0% | 3.5.12 | 2026-08-15 |
463
+ | AdvBench (LLM-attacks behaviors) | upstream-2026-10-15 | 520 | 5.6% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
464
+ | atr-self-test | internal | 341 | 96.6% (hunt) / 3.4% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
457
465
  | autoresearch | internal-1054 | 1,054 | 15.1% | 100.0% | 0.0% | 3.0.0-alpha.0 | 2026-05-23 |
458
- | garak (in-the-wild jailbreaks) | inthewild-jailbreak-corpus-650 | 650 | 92.3% | 100.0% | 0.0% | 3.5.12 | 2026-08-15 |
459
- | garak-full (all probe families) | 23-families | 3,475 | 57.2% | 100.0% | 0.0% | 3.5.12 | 2026-08-15 |
460
- | hackaprompt | v1 | 4,780 | 69.6% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
461
- | HarmBench (CAIS behaviors) | upstream-2026-06-16 | 400 | 2.8% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
462
- | hh-rlhf (Anthropic red-team-attempts) [^stdcorpora] | snapshot-2026-04 | 4,957 | 1.5% | 100.0% | 0.0% | 3.5.11 | 2026-08-05 |
463
- | JailbreakBench (JBB-Behaviors) | upstream-2026-06-16 | 100 | 6.0% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
464
- | llm-guard (Protect AI test fixtures) | corpus-2026-05-12 | 44 | 77.3% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
465
- | MITRE ATLAS [^stdcorpora] | snapshot-2026-04 | 182 | 39.0% | 100.0% | 0.0% | 3.5.11 | 2026-08-05 |
466
- | NeMo Guardrails (NVIDIA test fixtures) | corpus-2026-05-12 | 6 | 100.0% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
467
- | OWASP LLM Top 10 [^stdcorpora] | snapshot-2026-04 | 56 | 16.1% | 100.0% | 0.0% | 3.5.11 | 2026-08-05 |
468
- | PINT-format (deepset + Lakera Gandalf) [^pint] | v1 | 850 | 65.4% | 100.0% | 0.0% | 3.5.12 | 2026-08-15 |
469
- | PromptBench (academic adversarial) [^promptcorpora] | snapshot-2026-04 | 3,280 | 15.7% | 100.0% | 0.0% | 3.5.11 | 2026-08-05 |
470
- | promptfoo (red-team plugin fixtures) | corpus-2026-05-12 | 44 | 97.7% | 100.0% | 0.0% | 3.5.0 | 2026-06-16 |
471
- | PromptInject (academic adversarial) [^promptcorpora] | snapshot-2026-04 | 1,080 | 100.0% | 100.0% | 0.0% | 3.5.11 | 2026-08-05 |
472
- | SKILL.md benchmark (internal) [^skilllane] | internal-498 | 498 | 100.0% (hunt) / 0.0% (enforce) | 97.0% | 0.20% | 3.5.12 | 2026-08-15 |
466
+ | garak (in-the-wild jailbreaks) [^garakcorpus] | inthewild-jailbreak-corpus-650 | 650 | 92.3% (hunt) / 0.9% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
467
+ | garak-full (all probe families) [^garakcorpus] | 23-families | 3,475 | 57.2% (hunt) / 0.5% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
468
+ | hackaprompt [^hackaprompt] | v1 | 4,780 | 83.1% (hunt) / 49.0% (enforce) | not cited [^hackaprompt] | not cited [^hackaprompt] | 4.1.5 | 2026-10-19 |
469
+ | HarmBench (CAIS behaviors) | upstream-2026-10-15 | 400 | 6.0% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
470
+ | hh-rlhf (Anthropic red-team-attempts) [^stdcorpora] | snapshot-2026-04 | 4,957 | 1.4% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
471
+ | JailbreakBench (JBB-Behaviors) | upstream-2026-10-15 | 100 | 3.0% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
472
+ | llm-guard (2026-05-12 import) [^legacyvendor] | corpus-2026-05-12 | 44 | 63.6% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
473
+ | MITRE ATLAS [^stdcorpora] | snapshot-2026-04 | 182 | 37.4% (hunt) / 1.1% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
474
+ | NeMo Guardrails (2026-05-12 import) [^legacyvendor] | corpus-2026-05-12 | 6 | 100.0% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
475
+ | OWASP LLM Top 10 [^stdcorpora] | snapshot-2026-04 | 56 | 16.1% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
476
+ | PINT-format (deepset + Lakera Gandalf) [^pint] | v1 | 850 | 65.4% (hunt) / 0.4% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
477
+ | PromptBench (academic adversarial) [^promptcorpora] | snapshot-2026-04 | 3,280 | 15.7% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
478
+ | promptfoo (2026-05-12 import) [^legacyvendor] | corpus-2026-05-12 | 44 | 90.9% (hunt) / 6.8% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
479
+ | PromptInject (academic adversarial) [^promptcorpora] | snapshot-2026-04 | 1,080 | 100.0% (hunt) / 0.0% (enforce) | 100.0% | 0.0% | 4.1.5 | 2026-10-15 |
480
+ | SKILL.md benchmark (internal) [^skilllane] | internal-498 | 498 | 100.0% (hunt) / 0.0% (enforce) | 97.0% (hunt) | 0.20% (hunt) | 4.1.5 | 2026-10-15 |
481
+ | Vendor guardrail suites (pinned upstream tests) [^vendorsuites] | pinned-2026-10-19 | 146 | 77.0% (hunt) / 5.6% (enforce) | not cited [^vendorsuites] | not cited [^vendorsuites] | 4.1.5 | 2026-10-19 |
473
482
  | Wild scan (OpenClaw + Skills.sh + Hermes + ClawHub) [^wildscan] | full-scan-v2-2026-04-13 | 101,280 | — | not measured | 1.42% flag rate | 2.0.0 | 2026-04-13 |
474
483
 
475
- All detection corpora were (re-)measured against ATR 3.5.0 on 2026-06-16,
476
- except `autoresearch` (an internal predicted-rule corpus with no standalone
477
- runner) and the `Wild scan` snapshot, which retain their earlier measurements.
478
- `PromptInject` and `PromptBench` were re-measured against ATR 3.5.11 on
479
- 2026-08-05; see [^promptcorpora] for what moved and why. (An earlier
480
- re-measurement against 3.5.2 on 2026-06-25 fixed a harness event shape; the
481
- 0.0% rows before that were a harness artifact — the harness placed the prompt
482
- in a top-level field the engine does not read — not the engine's actual
483
- result.)
484
+ The `Vendor guardrail suites` row is the test cases of llm-guard, NeMo
485
+ Guardrails and promptfoo extracted by script from pinned upstream commits
486
+ ([`data/test-corpora/vendor-guardrail-suites/`](data/test-corpora/vendor-guardrail-suites/README.md)).
487
+ It is partly in-sample: 16 rules name one of the three vendors or carry the
488
+ `(vendor-corpus-import)` tag. Its measurement file re-measures the same samples
489
+ without them (`breakdown.in_sample_ablation.without_rules_naming_source`), the
490
+ ablation reference to cite beside the row: 61.9% (hunt) / 5.6% (enforce) at
491
+ ATR 4.1.5, measured 2026-10-19. That is not an out-of-sample figure either; see
492
+ [^vendorsuites]. The weekly run does not re-measure this row:
493
+ `bash scripts/vendor-suites/reproduce.sh --write` does. The three
494
+ `2026-05-12 import` rows are the earlier hand-assembled corpora; not all of
495
+ their samples come from the vendor they name ([^legacyvendor]).
496
+
497
+ A `Recall` cell gives two figures from the same measurement file. The first is
498
+ the `hunt` lane: the engine default, which loads every maturity, and the figure
499
+ `latest.json` and `data/stats.json` carry. The second is the `enforce` lane, the
500
+ one to run when blocking is on, which loads only `stable` rules: the same
501
+ samples through the same detection loop, recorded as `breakdown.enforce_lane`.
502
+ Read the second before deploying with blocking on; see [^skilllane].
503
+ `autoresearch` shows only the first, because its last measurement predates
504
+ the second's recording, and `Wild scan` has no recall at all. `Precision` and `FP rate` are `hunt`-lane figures, measured only on the
505
+ rows that contain benign samples (`atr-self-test`, `PINT-format` and `SKILL.md
506
+ benchmark`); elsewhere they are convention values or, for `Wild scan`, a flag
507
+ rate, not results (see Conventions below).
508
+
509
+ Whether a corpus was used to write the rules it scores has been examined for
510
+ `garak`, `garak-full`, `PromptInject`, `PromptBench` ([^garakcorpus],
511
+ [^promptcorpora]) and `hackaprompt` ([^hackaprompt]); all five are partly
512
+ closed-book. For `hackaprompt`, 20 rules name HackAPrompt in their YAML,
513
+ several mined from this exact sample; the same measurement file re-runs the
514
+ samples without them (`breakdown.in_sample_ablation.without_rules_naming_source`),
515
+ the ablation reference to cite beside the row: 28.7% (hunt) / 0.7% (enforce)
516
+ at ATR 4.1.5, measured 2026-10-19. It approximates an out-of-sample figure; it
517
+ is not one. The other rows are not
518
+ thereby out-of-sample: `atr-self-test` is built partly from the rules' own true
519
+ positives, and the rules authored `(vendor-corpus-import)` name the `promptfoo`
520
+ and `llm-guard` fixtures as their source.
521
+
522
+ A weekly run re-measures every row except the following, which keep their
523
+ earlier measurements: `hackaprompt`, whose corpus (a gated HuggingFace dataset) is not
524
+ provisioned in CI, so the run skips it and the row is re-measured locally with
525
+ `npm run eval:hackaprompt`; `autoresearch`, an internal predicted-rule
526
+ corpus with no standalone runner; and the `Wild scan` snapshot. The table is
527
+ re-rendered from the measurement files; the prose and footnotes are not, so a
528
+ figure there holds for the version it names. Before the weekly run, every row
529
+ except `autoresearch` and the `Wild scan` had been (re-)measured against ATR
530
+ 3.5.0 on 2026-06-16; the re-measurements since are described below, and
531
+ [^promptcorpora] covers what moved for `PromptInject` and `PromptBench`.
484
532
 
485
533
  `PromptInject` and `PromptBench` were re-measured against ATR 3.5.2 on
486
534
  2026-06-25 after a fix to the recall-analysis harness event shape; the prior
@@ -488,14 +536,55 @@ result.)
488
536
  top-level field the engine does not read), not the engine's actual result.
489
537
  `hh-rlhf`, `MITRE ATLAS` and `OWASP LLM Top 10` were re-measured against ATR
490
538
  3.5.11 on 2026-08-05 after their runner was found never to have called the
491
- engine at all — see [^stdcorpora]. Those three rows fell 99.1% → 1.5%,
492
- 100.0% → 39.0% and 100.0% → 16.1%. Nothing regressed; the earlier numbers
493
- were never measurements of ATR.
539
+ engine at all — see [^stdcorpora]. At 3.5.11 those three rows fell 99.1% →
540
+ 1.5%, 100.0% → 39.0% and 100.0% → 16.1%. Nothing regressed at that step; the
541
+ earlier numbers were never measurements of ATR.
542
+
543
+ The 2026-10-14 run (ATR 4.1.5) moved seven rows, for different reasons. Five
544
+ of them had last been measured on 2026-06-16 at 3.5.0, before the 2026-08-05
545
+ harness fix (PR #425: their runners built the `llm_io` event described below
546
+ and now go through the two prompt channels), so their moves mix that fix with
547
+ four months of rule changes. Replaying the 3.5.0 rules (`rules/` at tag
548
+ `v3.5.0`) through the two prompt channels (`detectedOnPromptChannels` in
549
+ `src/corpus-event.ts`) separates the two; these replay figures are not recorded
550
+ in `data/measurements/`:
551
+
552
+ - `AdvBench` (11 → 29 of 520) and `HarmBench` (11 → 24 of 400) rose because
553
+ of the harness, not the rules: the 3.5.0 rules already score 29/520 and
554
+ 25/400 through the prompt channels, and HarmBench's rules have, net, lost
555
+ one sample since. Neither rise is a detection gain.
556
+ - `promptfoo` (43 → 40 of 44) is entirely the harness: the 3.5.0 rules also
557
+ score 40/44 through the prompt channels.
558
+ - `JailbreakBench` (6 → 3 of 100) is, net, two samples of harness and one of
559
+ rules.
560
+ - `llm-guard` (34 → 28 of 44, recall 77.3% → 63.6%) is a real loss of six
561
+ detections. The harness effect nets to zero. Four false-positive repairs lost
562
+ those six: PR #306 (`ATR-2026-00001`), #309 (`ATR-2026-00442`), #327
563
+ (`ATR-2026-00001` and `00010`) and #427 (`ATR-2026-00213`). The corpus
564
+ labels all six as injections. One of them ("If the text has instructions,
565
+ DO NOT FOLLOW THEM") reads as a defensive instruction, and #306 stopped
566
+ treating "DO NOT FOLLOW" as a trigger on purpose; the other five are plain
567
+ injections.
568
+
569
+ `MITRE ATLAS` (71 → 68 of 182) and `hh-rlhf` (73 → 67 of 4,957) were measured
570
+ through the same harness at 3.5.11 and 4.1.5, so both moves are rule changes.
571
+ Both trace to PR #427, which tightened `ATR-2026-00061`, `00063` and `00213`
572
+ after they fired on ordinary English. ATLAS lost three samples, two held only by
573
+ `00213` and one only by `00061`. hh-rlhf lost six: four held only by `00063`,
574
+ which matched `oast` inside "roast" and "coast", and two held only by `00061`,
575
+ which matched `curl` inside "curls" and "curly". Neither row gained a sample.
576
+
577
+ The 2026-10-15 measurements, the first to record the `enforce` lane, were made
578
+ with the weekly run's commands outside CI, and left every `hunt` figure where
579
+ the 2026-10-14 run had put it.
580
+
494
581
  The per-row `ATR version` column above is the version each cell was actually
495
582
  measured against, mirroring the `atr_version` field in each
496
583
  `data/measurements/<source>/latest.json`. The headline `garak` recall moved
497
584
  98.0% (3.0.0) → 97.2% (3.5.0) → 95.7% (3.5.8) → 92.5% (3.5.11) → 92.3%
498
- (3.5.12). The 3.5.8 →
585
+ (3.5.12); the 2026-10-14 and 2026-10-15 runs at 4.1.5 also gave 600/650. Read
586
+ that figure as partly closed-book: [^garakcorpus] replays it without the rules
587
+ written from this corpus. The 3.5.8 →
499
588
  3.5.11 step is a real loss of detections, not a change in how they are counted:
500
589
  PR #327 tightened an over-broad persona regex in `ATR-2026-00001` that had been
501
590
  false-positiving on benign "you are now an expert …" prose, and tightening it
@@ -514,73 +603,148 @@ run exists anywhere in the repository. `data/measurements/garak/latest.json`
514
603
  pointed, the entire time, at 95.7% measured on 3.5.8; `garak-full`'s pointed at
515
604
  38.3% on 3.5.0, while a never-referenced 3.5.8 file sat unread in the same
516
605
  directory. So the claims failed this project's own rule that every published
517
- number is a version-pinned, reproducible measurement. It was also produced by a harness
606
+ number is a version-pinned, reproducible measurement. The 91.5% was also produced by a harness
518
607
  that built an event of `type: 'llm_io'`, which is a rule *source* and not an
519
608
  `AgentEventType`; `src/engine.ts` could not map it and so ran every rule of
520
609
  every source against the event instead of the two source types the harness
521
- documented itself as using. The 92.3% above replaces it: measured on
522
- 2026-08-15 at 784 rules through `llm_input` + `tool_response`, the two channels
523
- `src/hook-handler.ts` can actually deliver a prompt on, and written to
610
+ documented itself as using. The figure that replaced them was first measured on
611
+ 2026-08-05 at 780 rules (601/650) and re-measured on 2026-08-15 at 784 rules
612
+ (600/650, written to
524
613
  `data/measurements/garak/2026-08-15_garak-inthewild-jailbreak-corpus-650_atr-3-5-12.json`
525
- with the commit that produced it. Under the wider shape set used for
526
- false-positive measurement (which also runs `engine.scanSkill()`) the same
527
- corpus scores 92.9%; that number is recorded in the measurement's `breakdown`
528
- and is deliberately not the published one, because a garak prompt never reaches
614
+ with the commit that produced it), both through `llm_input` + `tool_response`,
615
+ the two channels `src/hook-handler.ts` can actually deliver a prompt on; every
616
+ `garak` measurement since, including the 2026-10-14 one at ATR 4.1.5, goes
617
+ through the same two channels. Under the wider shape set used for
618
+ false-positive measurement (which also runs `engine.scanSkill()`) the corpus
619
+ scores a little higher, 92.9% at both 3.5.12 and 4.1.5, recorded as
620
+ `wide_shape_recall` in each measurement's `breakdown`; that number is
621
+ deliberately not the published one, because a garak prompt never reaches
529
622
  production as a SKILL.md. `.github/workflows/eval.yml` now runs
530
623
  `scripts/check-benchmark-citations.ts`, which fails CI if this table or
531
624
  `stats.json` cites a number no measurement file backs.
532
625
  See [CHANGELOG.md](CHANGELOG.md).
533
626
 
534
- [^skilllane]: **Lane matters more here than anywhere else in this table.** The
535
- 100% figure is the `hunt` lane, which is the engine default and loads every
536
- maturity. In the `enforce` lane — the auto-block one, where a detection stops
537
- the agent with no human in the loop — this corpus scores **0%**, and the
538
- reason is structural rather than a tuning problem: of the 38 rules carrying
539
- `scan_target: skill`, **all 38 are `maturity: test`, and none is `stable`.**
540
- The enforce lane only loads `stable`, so it loads no skill-scanning rule at
541
- all, and 0 of 32 malicious samples fire. Anyone reading "100% recall on
542
- SKILL.md" and deploying in enforce mode would be forming a completely wrong
543
- expectation, so both numbers are shown. Verified on this commit with
544
- `grep`-free counting over `rules/**/*.yaml`.
627
+ [^skilllane]: **The first figure in each `Recall` cell is the `hunt` lane, and
628
+ the lane can change the answer completely.** `hunt` is the engine default
629
+ and loads every maturity. The `enforce` lane — the one to run when blocking
630
+ is on, where a detection can stop the agent with no human in the loop —
631
+ loads only `stable` rules, and its figure is the second one in the cell.
632
+ Since 2026-10-15 every measurement records it as `breakdown.enforce_lane`
633
+ in the same file as the headline: the same samples through the
634
+ benchmark's own detection loop (`engine.scanSkill()` for SKILL.md, the two
635
+ prompt channels for `garak`), on `new ATREngine({ lane: 'enforce' })`, with
636
+ `rules_in_lane` giving how many rules that lane loaded. It counts
637
+ detections, and a detection is not necessarily a block: the verdict blocks
638
+ only on severity and confidence (`src/verdict.ts`), so the figure is an
639
+ upper bound on what blocking stops.
640
+ `scripts/check-benchmark-citations.ts` fails CI when a cell's enforce
641
+ figure differs from that record or is left out. For SKILL.md the low figure
642
+ is structural rather than a tuning problem: at ATR 4.1.5 all 39 rules
643
+ carrying `scan_target: skill` are `maturity: test` and none is
644
+ `maturity: stable`, so
645
+ the enforce lane loads none of the rules written specifically for SKILL.md
646
+ files. Anyone reading "100% recall on SKILL.md" and deploying with
647
+ blocking on would be forming a completely wrong expectation. Until
648
+ 2026-10-15 the weekly run measured only the `hunt` lane, and the enforce
649
+ figures this footnote quoted were one-off replays.
545
650
 
546
651
  [^pint]: The `PINT-format` row is **not** a run of Lakera's official PINT
547
652
  benchmark. That corpus is private and roughly 5x larger; this row is a
548
653
  self-built 850-sample corpus in PINT's format, assembled from
549
654
  `deepset/prompt-injections` (660) and `Lakera/gandalf_ignore_instructions`
550
- (190). It also carries a scope caveat worth stating plainly: only **62 of
551
- 784 rules** fire on it at all, and `ATR-2026-00001` alone accounts for 226
552
- of the 295 detections. The eval report's per-rule block lists 63 entries,
655
+ (190). It also carries a scope caveat worth stating plainly: at 3.5.12 only
656
+ **62 of 784 rules** fired on it at all, and `ATR-2026-00001` alone
657
+ accounted for 226 of the 295 detections. The 3.5.12 eval report
658
+ (`data/pint-benchmark/pint-eval-report.json`) listed 63 per-rule entries,
553
659
  but one of those is `tier2.5-embedding-match` — the Tier 2.5 embedding
554
660
  stage, not a rule. Read it as a prompt-injection-family score, not as
555
661
  ATR's overall coverage. The row moved 63.6% → 60.3% between 3.5.0 and
556
662
  3.5.11 for the same reason `garak` moved: PR #327 tightened
557
663
  `ATR-2026-00001`'s persona-switch regex to stop it false-positiving on
558
- benign prose. Precision moved 99.7% → 100% over the same span. It then
559
- recovered 60.3% → 65.4% at 3.5.12 (2026-08-15) as rules added since
560
- 3.5.11 widened the family: rules firing on this corpus went 29 → 63 while
561
- `ATR-2026-00001`'s own contribution stayed at 226, so the gain came from
562
- the tail, not from re-loosening the one dominant rule. Precision held at
563
- 100% (0 FP on the 399 benign samples).
564
-
565
- [^promptcorpora]: **Read both of these as closed-book scores.** Until
566
- 2026-08-05 the harness recorded its per-rule breakdown as the literal
567
- string `"unknown"` (it read `m.rule_id` off an engine match that carries
568
- `m.rule.id`), so no published version of these rows could say which rules
569
- produced them. With attribution restored:
570
- **PromptInject 100.0%** is produced by **7 of 780 rules**. Five of those
664
+ benign prose. Through 3.5.11 the runner used a legacy event shape under
665
+ which the engine's source-type filter dropped every non-`llm_io` rule
666
+ before matching, so those figures, precision included, were measured over
667
+ roughly half the rulebase. The rise to 65.4% at 3.5.12 (2026-08-15) is
668
+ that harness changing, not the rules: that run was the first through the
669
+ canonical shape (PR #427), which admits every rule. Replayed through the
670
+ canonical shape, the 3.5.11 rules score 300 detections against 272 under
671
+ the legacy shape (the published 3.5.11 figure), so the rules changed
672
+ between 3.5.11 and 3.5.12 lost five detections on this corpus, while
673
+ `ATR-2026-00001`'s own contribution stayed at 226. Precision on the
674
+ canonical shape at 3.5.12 was 100% (0 FP on the 399 benign samples).
675
+
676
+ [^garakcorpus]: **Read this row as partly closed-book.** At ATR 4.1.5 (the rules
677
+ on `main` at `32bd48966`, 830 rule files), many of the rules that fire on
678
+ this corpus had been written from it, so its `hunt` figure is not an
679
+ out-of-sample result. A rule is counted as written from the corpus when its
680
+ own YAML shows the link, in either of two ways. 102 rules name the corpus in
681
+ their author, title, description or references (matching
682
+ `/inthewild|daninthewild|in-the-wild (jailbreak|corpus)/i`): an `author` of
683
+ `ATR Community (via NVIDIA garak in-the-wild corpus)` or
684
+ `(via NVIDIA garak dan.DanInTheWild probe)` — garak's `dan.DanInTheWild`
685
+ probes draw their prompts from this corpus — or, for example,
686
+ `ATR-2026-00307`, whose description cites `inthewild_jailbreak_llms.json`.
687
+ 28 more give garak as their source in their `author` (22
688
+ `ATR Community (garak-import)`, 4 `(via NVIDIA garak dan probe)`, 2
689
+ `(via ATR garak pipe)`) and quote one of these 650 prompts in their own
690
+ true-positive test cases: 60 or more consecutive characters, compared after
691
+ lower-casing and collapsing whitespace. Of the 130, 126 fire here, and 175
692
+ of the 600 prompts the full rule set detected were detected by them alone.
693
+ **Replayed without them, recall on the same 650 prompts was 65.4% (425 of
694
+ 650).** Without only the 102 that name the corpus, it was 81.5% (530 of
695
+ 650). A looser reading goes lower. `ATR-2026-00003`, a generic DAN rule in
696
+ ATR since v0.1.0 (2026-03-09), before any rule here was written from garak,
697
+ also quotes this corpus: one of its test cases is the public DAN opener,
698
+ which 7 of these prompts contain. It detects 259 of the 650, almost all of
699
+ them without that text, so it is not counted; counting it gave 51.8% (337
700
+ of 650). Two rules authored `(via NVIDIA garak dan probe)` neither name nor
701
+ quote the corpus (`ATR-2026-00273` and `00304` cite garak's DAN templates);
702
+ counting them too would move 65.4% to 64.9%, by arithmetic over the full
703
+ run's per-prompt matches rather than by a replay. The other figures are
704
+ **replays**, not measurements: each was run once, on 2026-10-15, with the
705
+ rule files removed from a copy of `rules/` and every prompt sent through the
706
+ two channels the published figure uses (`llm_input` and `tool_response`, as
707
+ `scripts/eval-garak-inthewild.ts` does). Nothing in `data/measurements/`
708
+ records them, and nothing refreshes them. None of the 130 is
709
+ `maturity: stable`, so the `enforce` figure does not rest on them.
710
+ `garak-full` contains these 650 prompts twice; see the paragraph on the two
711
+ `garak` rows below.
712
+
713
+ [^promptcorpora]: **Read both of these as closed-book scores.** Both rows are
714
+ measured on a legacy event, `{ type: 'llm_io', content,
715
+ fields.user_input }`. `llm_io` is a rule source, not an event type the
716
+ engine can route, so source filtering is off and every rule of every
717
+ source runs, on only those two fields. The other prompt-corpus rows
718
+ (`garak`, `AdvBench`, `HarmBench`, `JailbreakBench`, `llm-guard`,
719
+ `NeMo Guardrails` and `promptfoo`) go through the two prompt channels
720
+ instead. Each measurement's `breakdown.attribution.canonical_shapes` gives
721
+ the same corpus on the wider shape set the benign false-positive gate uses,
722
+ which also runs `engine.scanSkill()`; there PromptBench scores 54.8%, 1,633
723
+ of its 1,798 detections coming from one tool-argument rule,
724
+ `ATR-2026-00066`. That figure is deliberately not the published one, for
725
+ the reason given for `garak` above. Until 2026-08-05 the harness
726
+ recorded its per-rule breakdown as the literal string `"unknown"` (it read
727
+ `m.rule_id` off an engine match that carries `m.rule.id`), so no published
728
+ version of these rows could say which rules produced them. With
729
+ attribution restored, at 3.5.11:
730
+ **PromptInject 100.0%** was produced by **7 of 780 rules**. Five of those
571
731
  seven — `ATR-2026-00506`, `00507`, `00508`, `00509`, `00518` — carry
572
732
  `author: ATR Community (PromptInject corpus)`: they were written *from*
573
733
  this corpus, which has four attack classes built from a handful of
574
- templates. Remove those five and recall on the same 1,080 samples is
575
- **9.7%**. The concentration is real but not fragile: the top rule
576
- (`ATR-2026-00508`, 968/1,080 samples) is the sole detector on none of
577
- them, so deleting it leaves recall at 100%; only `00518` (45 samples) and
578
- `00507` (27) are sole detectors of anything. On the 5,352-sample benign
579
- gate, `00506` / `00507` / `00518` are 0-FP; `00508` has 4 FP, `00509` 3,
580
- `ATR-2026-00001` 19, `ATR-2026-00400` 1.
581
- **PromptBench 15.7%** is produced by **3 of 780 rules** (`ATR-2026-00520`,
734
+ templates. Remove those five and recall on the same 1,080 samples was
735
+ **9.7%**. The concentration was real but not fragile: the top rule
736
+ (`ATR-2026-00508`, 968/1,080 samples) was the sole detector on none of
737
+ them, so deleting it left recall at 100%; only `00518` (45 samples) and
738
+ `00507` (27) were sole detectors of anything. On the 5,352-sample benign
739
+ gate, `00506` / `00507` / `00518` were 0-FP; `00508` had 4 FP, `00509` 3,
740
+ `ATR-2026-00001` 19, `ATR-2026-00400` 1. At 4.1.5 nine rules fire on
741
+ PromptInject. One of them, `ATR-2026-02846`, was added since and was
742
+ authored from PromptInject attack clusters (its title says so); it matches
743
+ 780 of the 1,080 samples, so at 4.1.5 the closed-book figure needs it
744
+ removed as well as the five above.
745
+ **PromptBench 15.7%** was produced by **3 of 780 rules** (`ATR-2026-00520`,
582
746
  `00519`, `00202`), all three 0-FP on the same benign gate. Two of the three
583
- were mined from PromptBench; without them recall is **2.4%**.
747
+ were mined from PromptBench; without them recall was **2.4%**.
584
748
  The PromptBench row moved 23.2% (3.5.2) → 15.7% (3.5.11) and the loss is
585
749
  fully attributable: 247 samples were held only by rules that have since
586
750
  been precision-repaired, and re-running each rule version by version pins
@@ -596,6 +760,38 @@ See [CHANGELOG.md](CHANGELOG.md).
596
760
  columns are properties of the corpus, not measurements — read them
597
761
  together with the benign-gate FP counts above, never alone.
598
762
 
763
+ [^vendorsuites]: **Partly in-sample; precision not cited.** 126 attack and 20
764
+ benign test cases (llm-guard 46 / 16, NeMo Guardrails 22 / 4, promptfoo 58 / 0),
765
+ each traceable to repo, commit, path and line in
766
+ [`manifest.json`](data/test-corpora/vendor-guardrail-suites/manifest.json). At ATR
767
+ 4.1.5 (measured 2026-10-19) recall is 97/126 (hunt), 92/126 (alert) and 7/126
768
+ (enforce). Without the 16 rules whose YAML names promptfoo, llm-guard or NeMo, or
769
+ carries the `(vendor-corpus-import)` tag, it is 78/126 = 61.9% (hunt), 70/126 =
770
+ 55.6% (alert) and 7/126 = 5.6% (enforce); per vendor in the hunt lane, llm-guard
771
+ 26/46, NeMo Guardrails 20/22, promptfoo 32/58. That ablation is the reference
772
+ to cite beside the row, and it still overstates out-of-sample recall, by an
773
+ amount the same file measures (`breakdown.overlaps`): 35 further rules carry a
774
+ 40-character run of some vendor sample's text in their YAML, and 23 of the 78
775
+ hunt-lane detections in the ablation are by such a rule on the sample it
776
+ overlaps; removing those rules too gives 58/126 = 46.0% (hunt) / 5/126 = 4.0%
777
+ (enforce). Five NeMo Guardrails attack samples (DAN-style jailbreaks) share 58%
778
+ to 99% of their text with a prompt in `garak-full`'s `dan.json` and
779
+ `inthewild.json`, a corpus other rules were mined from; all five are detected in
780
+ the hunt lane of the ablation. The vendors ship only 20 benign cases (the hunt
781
+ lane flags 1, the enforce lane 0), too few for a precision or FP-rate claim, so
782
+ the cells say "not cited" and `data/stats.json` carries `null` for both
783
+ (`breakdown.precision_not_citable`).
784
+
785
+ [^legacyvendor]: These three rows are the corpora assembled by hand on
786
+ 2026-05-12 and attributed to the vendors' test suites. A script-based audit
787
+ ([`legacy-corpora-provenance.json`](data/test-corpora/vendor-guardrail-suites/legacy-corpora-provenance.json))
788
+ checked each of their 94 samples against the vendor repo it names, at a pinned
789
+ commit: 79 occur verbatim in the vendor file they name, 3 are truncated or
790
+ edited from it, 2 are another vendor's test cases, and 10 are not found
791
+ verbatim anywhere in the vendor repository at the pinned commit. Read them as ATR-assembled corpora seeded from vendor tests, not as the
792
+ vendors' fixtures; the `Vendor guardrail suites` row is the script-extracted
793
+ replacement.
794
+
599
795
  [^stdcorpora]: Until 2026-08-05 these three rows were **not produced by the ATR
600
796
  engine**. `scripts/eval-std-corpora.ts` walked `rules/` with a YAML parser,
601
797
  kept only `operator: regex` conditions, flattened every condition of every
@@ -617,56 +813,72 @@ See [CHANGELOG.md](CHANGELOG.md).
617
813
  and the canonical event shapes in `scripts/lib/corpus-event.ts` — the same
618
814
  entry point the false-positive gates use. Reproduce with
619
815
  `npx tsx scripts/eval-std-corpora.ts`. Read the new numbers with the same
620
- scope caveat as `PINT-format`: on ATLAS, `ATR-2026-00061` alone accounts
621
- for 59 of the 71 detections (32.4% of the corpus), and ATLAS procedures are
816
+ scope caveat as `PINT-format`: on ATLAS at 3.5.11, `ATR-2026-00061` alone
817
+ accounted for 59 of the 71 detections (32.4% of the corpus; 58 of 68 at
818
+ 4.1.5), and ATLAS procedures are
622
819
  prose *descriptions* of attacks rather than attack payloads, so this row
623
820
  measures ATR against attack write-ups, not against traffic.
624
821
 
625
- Two `garak` rows are deliberate: the headline `garak` source tracks NVIDIA's
626
- in-the-wild jailbreak corpus (narrow, the ~92% number ATR cites publicly,
627
- refreshed 2026-08-15 against ATR 3.5.12), while `garak-full` tracks
628
- every probe family in upstream garak (broad, includes families like
629
- `badchars`, `dra`, `encoding` that ATR's regex layer intentionally does
630
- not target). Both are valid measurements against different corpora; they
631
- are kept as separate streams so the broad-corpus number does not silently
632
- overwrite the headline.
633
-
634
- The single-digit recall on AdvBench / HarmBench / JailbreakBench / hh-rlhf is
822
+ Two `garak` rows are deliberate, and they are not independent. The headline
823
+ `garak` source tracks NVIDIA's in-the-wild jailbreak corpus (narrow; this is the
824
+ number ATR cites publicly, and [^garakcorpus] gives how much of it rests on
825
+ rules written from that corpus). `garak-full` runs every non-empty family file
826
+ extracted from upstream garak (23 at ATR 4.1.5), and it contains the
827
+ in-the-wild corpus twice: as its `inthewild` family, and as 648 of the 664
828
+ prompts of its `dan` family. In the 2026-10-15 measurement those two families
829
+ supplied 1,214 of its 1,987 `hunt` detections. Rules have also been written
830
+ from most of its other families (an `author` of
831
+ `ATR Community (via NVIDIA garak <family> probe)`, 12 of them from `badchars`,
832
+ `dra` and `encoding`), so read `garak-full` as partly closed-book too.
833
+ [docs/RULE-PRODUCTION.md](docs/RULE-PRODUCTION.md) §2.3–2.4 covers its other
834
+ limits: six of its families are content-safety and hallucination probes, four
835
+ are the same 16 unencoded seed sentences, and 2,739 of its 3,475 prompts are
836
+ distinct. The two are kept as separate streams so neither figure stands in for
837
+ the other.
838
+
839
+ The low recall on AdvBench / HarmBench / JailbreakBench / hh-rlhf is
635
840
  honest and expected. Those four corpora test **LLM safety alignment** (does the
636
841
  model refuse harmful requests like "explain how to make a bomb"), not
637
842
  **prompt-injection detection** (the surface ATR's regex layer targets).
638
- ATR's near-zero recall on these corpora confirms the layering thesis:
639
- regex catches structured attack patterns, alignment + content moderation
640
- catch natural-language harm requests. The numbers are recorded for
843
+ ATR's low recall on these corpora is consistent with the layering view
844
+ (this section does not measure what any other layer catches): regex catches
845
+ structured attack patterns, alignment + content moderation catch
846
+ natural-language harm requests. The numbers are recorded for
641
847
  completeness and so any future ATR rule additions in the harm-category
642
848
  space can be measured against a documented baseline. `hh-rlhf` is Anthropic's
643
- red-team-attempts set — the same genre as the other three — and its 1.5% now
644
- sits with their 2.1% / 2.8% / 6.0% instead of contradicting them at 99.1%.
849
+ red-team-attempts set — the same genre as the other three — and its
850
+ low recall now sits with theirs instead of contradicting them at
851
+ 99.1%.
645
852
 
646
853
  Conventions: 100%-adversarial corpora contain no benign samples, so they have
647
854
  no true-negative population and **`precision` and `fp_rate` cannot be computed
648
855
  from them**. The measurement schema requires numbers, so those rows record the
649
856
  convention `precision 1` / `fp_rate 0`. Read the `Precision` and `FP rate`
650
- columns as "not applicable to this corpus", not as results — the real
651
- precision numbers come from the benign gate, lane-keyed, below. Wild-scan has
652
- no ground-truth labels either; its `precision` column reports a precision floor
653
- computed as `confirmed_malware / flagged`. Every cell is sourced from a
654
- specific measurement file — see `data/measurements/<source>/latest.json` for
655
- the file path and `metadata.measurement_file` in `stats.json` for the absolute
656
- repo path.
857
+ columns as "not applicable to this corpus", not as results. No benign-gate
858
+ precision numbers are published at present: the lane FP rates once quoted are
859
+ withdrawn (see below). Wild-scan has no ground-truth labels either; its
860
+ `Precision` cell reads `not measured` because the precision floor once
861
+ published for it is withdrawn (see [^wildscan]), and its `FP rate` cell is a
862
+ flag rate, flagged divided by scanned, not a false-positive rate. Every cell
863
+ except the `Wild scan` row (taken from the raw scan file; see [^wildscan]) is
864
+ sourced from a specific measurement file: `data/measurements/<source>/latest.json`
865
+ names it, and `benchmarks[].measurement_file` in `data/stats.json` gives its
866
+ path.
657
867
 
658
868
  False-positive rate is lane-keyed as of v3.5.0, not a single overall figure.
659
869
  ATR ships detection lanes (`enforce` / `alert` / `hunt`). The specific lane FP
660
870
  rates previously quoted here are withdrawn and awaiting re-measurement — see
661
- the note in the lanes section above for why. Per-corpus `FP rate` cells above
662
- are measured in the default `hunt` lane. See [CHANGELOG.md](CHANGELOG.md)
871
+ the note in the lanes section above for why. Where a row has benign samples
872
+ (`atr-self-test`, `PINT-format`, `SKILL.md benchmark`), its `FP rate` cell is a
873
+ `hunt`-lane measurement; on every other row it is a convention value or, for
874
+ `Wild scan`, a flag rate. See [CHANGELOG.md](CHANGELOG.md)
663
875
  (v3.5.0) for the lane definitions.
664
876
 
665
877
  ```bash
666
878
  npm test # engine + rule unit tests (vitest)
667
879
  npm run eval # atr-self-test eval (writes a measurement)
668
880
  npm run eval:pint # PINT benchmark (writes a measurement)
669
- npx tsx src/eval/run-hackaprompt-benchmark.ts # HackAPrompt
881
+ npm run eval:hackaprompt # HackAPrompt (gated dataset; writes a measurement)
670
882
  npx tsx src/eval/skill-benchmark.ts # SKILL.md (498 labeled)
671
883
  npx tsx scripts/eval-std-corpora.ts # HH-RLHF + OWASP + ATLAS
672
884
  npx tsx scripts/atr_recall_analysis.ts # PromptBench + PromptInject
@@ -681,9 +893,27 @@ npx tsx scripts/sync-stats-from-measurements.ts # r
681
893
 
682
894
  Raw data: [`data/full-scan-v2-2026-04-14.json`](data/full-scan-v2-2026-04-14.json) — 101,280 skills scanned, 1,434 flagged, engine 2.0.0, timestamped 2026-04-13. The malware-campaign write-up is in [`docs/research/openclaw-malware-campaign-2026-04.md`](docs/research/openclaw-malware-campaign-2026-04.md).
683
895
 
896
+ [^hackaprompt]: **Partly in-sample; cite the ablation reference beside it.**
897
+ The samples are a seeded subset of 4,780 of the dataset's 601,757 rows
898
+ (`hackaprompt/hackaprompt-dataset` at revision `25b87fbe`, parquet sha256
899
+ `bedca308...`, seed 20260511, rows shorter than 3 characters dropped), pinned
900
+ by row indices and digests in
901
+ [`hackaprompt-corpus.manifest.json`](data/hackaprompt/hackaprompt-corpus.manifest.json);
902
+ `npm run eval:hackaprompt` re-draws it and stops on any mismatch. At ATR 4.1.5
903
+ (measured 2026-10-19) recall is 3,972 (hunt), 3,931 (alert) and 2,343
904
+ (enforce) of 4,780. Without the 20 rules whose YAML names HackAPrompt it is
905
+ 1,372 = 28.7% (hunt) / 32 = 0.7% (enforce): the ablation reference, an
906
+ approximation of out-of-sample recall that does not guarantee it (a rule that
907
+ does not name HackAPrompt can still have been tuned on these prompts). The
908
+ corpus has no benign samples, so precision and FP rate are undefined on it:
909
+ the cells say "not cited" and `data/stats.json` carries `null` for both
910
+ (`breakdown.precision_not_citable`). Earlier figures for this row (28.6% → 66.2% in the 2.2.0 CHANGELOG
911
+ entry, 69.6% at ATR 3.5.0) and their re-runs are in
912
+ [`data/hackaprompt/METHOD.md`](data/hackaprompt/METHOD.md).
913
+
684
914
  [^wildscan]: Scanned and flagged counts come from `total_scanned` and `total_flagged` in the raw scan file; the flag rate is those two divided. **No precision figure is given because none was measured over this run.** Earlier material paired a manual-review count against a smaller 96,096-skill analysis subset and reported the ratio as a precision floor. The review covered that subset rather than all 1,434 flags, so the ratio does not describe this scan and is not reproducible from the raw file; it has been withdrawn rather than recomputed. Establishing precision here means re-reviewing a sample of the 1,434 flags, which has not been done.
685
915
 
686
- ATR is honest about what it cannot detect. Regex catalogs miss paraphrased attacks, semantic rephrasings of credential exfiltration, and novel attack shapes not present in the training corpus. `PromptBench` (3,280 character- and word-level robustness perturbations) is a different threat class from prompt injection and sits largely outside ATR's content scope; ATR still matches the 23.2% that carry injection-shaped payloads, at 100% precision. See [LIMITATIONS.md](LIMITATIONS.md) for the documented evasion-test corpus (64 techniques as of 2026-05) and the layering recommendation: ATR is the content layer; pair with credential brokering, sandbox execution, and human-in-the-loop for high-blast-radius actions.
916
+ ATR is honest about what it cannot detect. Regex catalogs miss paraphrased attacks, semantic rephrasings of credential exfiltration, and novel attack shapes not present in the training corpus. `PromptBench` (3,280 character- and word-level robustness perturbations) is a different threat class from prompt injection and sits largely outside ATR's content scope; most of what ATR matches there comes from two rules mined from PromptBench itself, and the share fell from 23.2% at 3.5.2; [^promptcorpora] covers both. See [LIMITATIONS.md](LIMITATIONS.md) for the documented evasion-test corpus (64 techniques as of 2026-05) and the layering recommendation: ATR is the content layer; pair with credential brokering, sandbox execution, and human-in-the-loop for high-blast-radius actions.
687
917
 
688
918
  ## 9. Governance
689
919
 
@@ -693,7 +923,7 @@ ATR is currently single-maintainer (BDFL) under Adam Lin, transitioning to a Tec
693
923
  |---|---|
694
924
  | Phase 0 — Core spec, reference engine, initial rule corpus | Done |
695
925
  | Phase 1 — Distribution surfaces (npm, PyPI, GitHub Action, SARIF, MCP server) | Done |
696
- | Phase 2 — Production adoption (Microsoft AGT, Cisco AI Defense, MISP, Gen Digital Sage) | In progress |
926
+ | Phase 2 — Downstream integrations (Cisco skill-scanner, MISP, Gen Digital Sage, Microsoft AGT examples) | In progress |
697
927
  | Phase 3 — Community contribution flywheel (issue-to-proposal automation, CVE-collector pipeline) | In progress |
698
928
  | Phase 4 — TSC seating; second-engine implementation; submission to a standards body | Planned |
699
929
 
@@ -797,7 +1027,7 @@ ATR is released under the [MIT License](LICENSE). All contributions are MIT-lice
797
1027
 
798
1028
  ## 17. Acknowledgments
799
1029
 
800
- ATR's design draws on prior work in: [Sigma](https://github.com/SigmaHQ/sigma) (SIEM detection format), [YARA](https://github.com/VirusTotal/yara) (malware signature format), [OWASP LLM Top 10](https://owasp.org/www-project-top-10-for-large-language-model-applications/), [OWASP Agentic Top 10](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/), [MITRE ATLAS](https://atlas.mitre.org/), [NVIDIA garak](https://github.com/NVIDIA/garak), [Lakera PINT](https://github.com/lakeraai/pint-benchmark), [Meta LlamaFirewall](https://ai.meta.com/research/publications/llamafirewall-an-open-source-guardrail-system-for-building-secure-ai-agents/), and [SAFE-MCP (OpenSSF)](https://github.com/safe-agentic-framework/safe-mcp).
1030
+ ATR's design draws on prior work in: [Sigma](https://github.com/SigmaHQ/sigma) (SIEM detection format), [YARA](https://github.com/VirusTotal/yara) (malware signature format), [OWASP LLM Top 10](https://owasp.org/www-project-top-10-for-large-language-model-applications/), [OWASP Agentic Top 10](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/), [MITRE ATLAS](https://atlas.mitre.org/), [NVIDIA garak](https://github.com/NVIDIA/garak), [Lakera PINT](https://github.com/lakeraai/pint-benchmark), [Meta LlamaFirewall](https://ai.meta.com/research/publications/llamafirewall-an-open-source-guardrail-system-for-building-secure-ai-agents/), and [SAF-MCP](https://github.com/secure-agentic-framework/saf-mcp).
801
1031
 
802
1032
  The 2026-04 ecosystem-wide skill scan was made possible by the maintainers of OpenClaw, Skills.sh, Hermes Agent, and ClawHub publishing their registries openly.
803
1033
 
@@ -814,7 +1044,7 @@ The 2026-04 ecosystem-wide skill scan was made possible by the maintainers of Op
814
1044
  - [OWASP Agentic Top 10 (2026)](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/) — Taxonomy of agentic-application risk categories.
815
1045
  - [OWASP LLM Top 10 (2025)](https://owasp.org/www-project-top-10-for-large-language-model-applications/) — Taxonomy of LLM-application risk categories.
816
1046
  - [MITRE ATLAS](https://atlas.mitre.org/) — Adversarial-threat landscape for AI systems.
817
- - [SAFE-MCP (OpenSSF)](https://github.com/safe-agentic-framework/safe-mcp) — Secure-MCP framework, technique catalog.
1047
+ - [SAF-MCP](https://github.com/secure-agentic-framework/saf-mcp) — Secure Agentic Framework for Model Context Protocol, technique catalog.
818
1048
  - [Sigma](https://github.com/SigmaHQ/sigma) — Generic detection rule format for SIEMs (architectural precedent).
819
1049
  - [YARA](https://github.com/VirusTotal/yara) — Pattern-matching language for malware (architectural precedent).
820
1050
  - Five Eyes joint guidance on AI agent deployment (2026-05-01): CISA + NSA + UK NCSC + ASD + CCCS + NZ NCSC — [CyberScoop coverage](https://cyberscoop.com/cisa-nsa-five-eyes-guidance-secure-deployment-ai-agents/).