agent-threat-rules 4.1.5 → 4.2.1

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 (248) hide show
  1. package/CHANGELOG.md +751 -1
  2. package/README.md +356 -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-00501-data-exfiltration-via-markdown-image-and-link-url-injection.yaml +16 -2
  156. package/rules/context-exfiltration/ATR-2026-00505-system-prompt-extraction-instruction-dump-request.yaml +6 -4
  157. package/rules/context-exfiltration/ATR-2026-00702-ipi-credential-exfil-via-agent-action.yaml +1 -1
  158. package/rules/context-exfiltration/ATR-2026-00707-ipi-file-exfil-with-destroy-or-tamper.yaml +1 -1
  159. package/rules/context-exfiltration/ATR-2026-00851-indirect-pi-financial-payment-exfil.yaml +1 -1
  160. package/rules/context-exfiltration/ATR-2026-01457-sysprompt-completion-clone-attack.yaml +1 -1
  161. package/rules/context-exfiltration/ATR-2026-01462-sysprompt-override-pseudo-admin.yaml +1 -1
  162. package/rules/context-exfiltration/ATR-2026-01463-imend-imsystem-prompt-injection-format.yaml +1 -1
  163. package/rules/context-exfiltration/ATR-2026-01606-ssrf-internal-network-scan.yaml +1 -1
  164. package/rules/context-exfiltration/ATR-2026-02847-policy-justified-exfil-to-email.yaml +1 -1
  165. package/rules/context-exfiltration/ATR-2026-02855-cross-email-exfil-relay.yaml +79 -0
  166. package/rules/context-exfiltration/ATR-2026-02861-exfil-send-email-with-body-directive.yaml +83 -0
  167. package/rules/context-exfiltration/ATR-2026-02866-idna-label-separator-in-fetch-url-host.yaml +104 -0
  168. package/rules/excessive-autonomy/ATR-2026-00500-ssrf-via-agent-url-fetch-instruction.yaml +12 -2
  169. package/rules/excessive-autonomy/ATR-2026-00710-ipi-physical-access-grant.yaml +1 -1
  170. package/rules/model-abuse/ATR-2026-01897-host-sabotage.yaml +2 -2
  171. package/rules/model-security/ATR-2026-00433-modelcache-torch-load-deserialization-rce.yaml +1 -1
  172. package/rules/privilege-escalation/ATR-2026-00040-privilege-escalation.yaml +1 -1
  173. package/rules/privilege-escalation/ATR-2026-00436-enclave-vm-sandbox-escape-rce.yaml +1 -1
  174. package/rules/privilege-escalation/ATR-2026-01601-sql-injection-destructive-ddl.yaml +3 -3
  175. package/rules/privilege-escalation/ATR-2026-01612-debug-mode-privilege-escalation.yaml +1 -1
  176. package/rules/privilege-escalation/ATR-2026-02867-identifier-typed-tool-argument-dot-dot-path-segment.yaml +106 -0
  177. package/rules/prompt-injection/ATR-2026-00004-system-prompt-override.yaml +2 -2
  178. package/rules/prompt-injection/ATR-2026-00080-encoding-evasion.yaml +1 -1
  179. package/rules/prompt-injection/ATR-2026-00086-visual-spoofing.yaml +1 -1
  180. package/rules/prompt-injection/ATR-2026-00203-context-pollution-skill-description.yaml +1 -1
  181. package/rules/prompt-injection/ATR-2026-00228-structured-jailbreak.yaml +2 -2
  182. package/rules/prompt-injection/ATR-2026-00233-structured-jailbreak.yaml +2 -2
  183. package/rules/prompt-injection/ATR-2026-00256-base-n-encoding-jailbreak.yaml +1 -1
  184. package/rules/prompt-injection/ATR-2026-00264-latent-injection-translation.yaml +3 -3
  185. package/rules/prompt-injection/ATR-2026-00265-latent-injection-rag-document.yaml +1 -1
  186. package/rules/prompt-injection/ATR-2026-00282-perez-prompt-injection-hijack.yaml +4 -4
  187. package/rules/prompt-injection/ATR-2026-00321-hyphenated-system-instruction-injection.yaml +1 -1
  188. package/rules/prompt-injection/ATR-2026-00394-backspace-deletion-character-injection.yaml +2 -2
  189. package/rules/prompt-injection/ATR-2026-00446-variable-assignment-payload-injection.yaml +3 -3
  190. package/rules/prompt-injection/ATR-2026-00476-garak-web-injection-javascript-alert-1.yaml +3 -3
  191. package/rules/prompt-injection/ATR-2026-00481-garak-dan-mixedunassigned.yaml +1 -1
  192. package/rules/prompt-injection/ATR-2026-00489-garak-encoding-javascript-alert-1.yaml +3 -3
  193. package/rules/prompt-injection/ATR-2026-00503-fake-error-state-takeover-unrestricted-replacement-bot.yaml +4 -2
  194. package/rules/prompt-injection/ATR-2026-00508-escape-delimiter-wrapped-goal-hijacking-in-user-input-prompt.yaml +3 -3
  195. package/rules/prompt-injection/ATR-2026-00512-rules-file-backdoor-injection.yaml +1 -1
  196. package/rules/prompt-injection/ATR-2026-01005-many-shot-repetition-override.yaml +2 -2
  197. package/rules/prompt-injection/ATR-2026-01006-payload-split-string-concat-injection.yaml +2 -2
  198. package/rules/prompt-injection/ATR-2026-01015-evasion-refusal-suppression.yaml +1 -1
  199. package/rules/prompt-injection/ATR-2026-01016-evasion-response-priming-forced-opener.yaml +1 -1
  200. package/rules/prompt-injection/ATR-2026-01026-evasion-unicode-glitch-token-anomaly.yaml +2 -2
  201. package/rules/prompt-injection/ATR-2026-01304-indirect-injection-carrier-important-override.yaml +1 -1
  202. package/rules/prompt-injection/ATR-2026-01759-dos-new-important-instructions-injection-in-document.yaml +1 -1
  203. package/rules/prompt-injection/ATR-2026-01865-llmail-fake-email-boundary-marker.yaml +1 -1
  204. package/rules/prompt-injection/ATR-2026-02010-emoji-only-payload.yaml +2 -2
  205. package/rules/prompt-injection/ATR-2026-02815-injected-halt-order-canned-reply-dos.yaml +80 -0
  206. package/rules/prompt-injection/ATR-2026-02820-tool-result-orders-agent-to-hide-its-own-action.yaml +106 -0
  207. package/rules/prompt-injection/ATR-2026-02858-context-suppression-directive.yaml +73 -0
  208. package/rules/prompt-injection/ATR-2026-02859-tool-output-agent-role-reassignment.yaml +71 -0
  209. package/rules/prompt-injection/ATR-2026-02860-forged-instruction-boundary-marker.yaml +70 -0
  210. package/rules/prompt-injection/ATR-2026-02862-fake-chat-template-turn-injection.yaml +79 -0
  211. package/rules/skill-compromise/ATR-2026-00060-skill-impersonation.yaml +1 -1
  212. package/rules/skill-compromise/ATR-2026-00063-skill-chain-attack.yaml +2 -2
  213. package/rules/skill-compromise/ATR-2026-00064-over-permissioned-skill.yaml +1 -1
  214. package/rules/skill-compromise/ATR-2026-00066-parameter-injection.yaml +2 -2
  215. package/rules/skill-compromise/ATR-2026-00149-skill-exfil-compound.yaml +6 -6
  216. package/rules/skill-compromise/ATR-2026-00223-reverse-shell-dropper.yaml +2 -2
  217. package/rules/skill-compromise/ATR-2026-01755-backdoor-pot-linguistic-trigger-phrase.yaml +1 -1
  218. package/rules/skill-compromise/ATR-2026-01756-backdoor-pot-symbol-emoticon-trigger.yaml +1 -1
  219. package/rules/skill-compromise/ATR-2026-02261-code-credential-exfil-chain.yaml +1 -1
  220. package/rules/tool-poisoning/ATR-2026-00012-unauthorized-tool-call.yaml +23 -23
  221. package/rules/tool-poisoning/ATR-2026-00013-tool-ssrf.yaml +16 -5
  222. package/rules/tool-poisoning/ATR-2026-00161-important-tag-cross-tool-shadowing.yaml +1 -1
  223. package/rules/tool-poisoning/ATR-2026-00277-echo-template-command-injection.yaml +2 -2
  224. package/rules/tool-poisoning/ATR-2026-00494-garak-exploitation-mixedunassigned.yaml +4 -4
  225. package/rules/tool-poisoning/ATR-2026-00521-shell-command-injection-agent-tool-context.yaml +1 -1
  226. package/rules/tool-poisoning/ATR-2026-00581-mcp-tool-rug-pull-post-approval-redefinition.yaml +1 -1
  227. package/rules/tool-poisoning/ATR-2026-01303-tool-schema-enumeration-social-engineering.yaml +1 -1
  228. package/rules/tool-poisoning/ATR-2026-01307-mcp-dns-rebinding-attack.yaml +2 -2
  229. package/rules/tool-poisoning/ATR-2026-01775-semantic-mcp-tool-manifest-poisoning.yaml +1 -1
  230. package/rules/tool-poisoning/ATR-2026-01932-shadow-undeclared-mcp-server-registration.yaml +1 -1
  231. package/rules/tool-poisoning/ATR-2026-02025-mcp-full-schema-poisoning-nondescription-field.yaml +1 -1
  232. package/rules/tool-poisoning/ATR-2026-02817-untrusted-content-dictates-tool-arguments.yaml +83 -0
  233. package/rules/tool-poisoning/ATR-2026-02864-non-canonical-ipv4-host-in-url-tool-argument.yaml +102 -0
  234. package/rules/tool-poisoning/ATR-2026-02865-read-only-utility-write-flag-in-command-argument.yaml +157 -0
  235. package/rules/tool-poisoning/ATR-2026-02868-ssrf-ipv6-transition-address-embedded-private-ipv4.yaml +107 -0
  236. package/rules/tool-poisoning/ATR-2026-02870-invisible-control-before-directive.yaml +94 -0
  237. package/spec/README.md +6 -3
  238. package/spec/atr-event-v1.0.md +13 -8
  239. package/spec/atr-method-v1.1.md +2 -2
  240. package/spec/category-registry/gemara/README.md +102 -0
  241. package/spec/category-registry/gemara/ai-agent-capability-catalog.yaml +157 -0
  242. package/spec/category-registry/gemara/atr-attack-vector-catalog.yaml +130 -0
  243. package/spec/category-registry/gemara/atr-detection-control-catalog.yaml +327 -0
  244. package/spec/category-registry/gemara/atr-threat-catalog.yaml +246 -0
  245. package/spec/category-registry/gemara/src/editorial.yaml +743 -0
  246. package/spec/mappings/README.md +1 -1
  247. package/spec/mappings/atr-to-nist-csf-2.0.md +5 -4
  248. package/spec/schema/event.schema.json +1 -1
@@ -0,0 +1,107 @@
1
+ title: SSRF via IPv6 Transition Address Embedding a Private IPv4 Target
2
+ id: ATR-2026-02868
3
+ rule_version: 1
4
+ status: experimental
5
+ description: |
6
+ Detects an agent tool argument whose URL host (or host-typed argument) is an IPv6 transition-mechanism address that carries an internal IPv4 address in its embedded 32 bits: IPv4-compatible (::a.b.c.d / ::XXXX:XXXX), IPv4-mapped written in hex (::ffff:XXXX:XXXX), IPv4-translated (::ffff:0:XXXX:XXXX), 6to4 (2002:XXXX:XXXX::/48) and NAT64 (64:ff9b::/96 and the local-use 64:ff9b:1::/48 prefix written with the IPv4 in the last 32 bits). The embedded IPv4 must fall in loopback 127.0.0.0/8, RFC 1918 (10/8, 172.16/12, 192.168/16) or link-local / cloud metadata 169.254/16.
7
+ Source: CVE-2026-55787 / GHSA-794r-5rp2-fpg8 in flytohub/flyto-core (< 2.26.3), an execution kernel for AI-agent workflows. Its validate_url_ssrf / is_private_ip guard compared resolved addresses only against native private IPv4 and IPv6 ranges and never unwrapped transition addresses, so a workflow author could hand http.get, http.request, browser.goto or llm.chat a URL such as http://[64:ff9b::a9fe:a9fe]/ and reach cloud metadata, loopback or RFC 1918 services. The same blind spot exists in any agent fetch tool whose guard checks ipaddress.is_private on the IPv6 object without first extracting the embedded IPv4.
8
+ Scope and limits: the rule matches the address literal only when it is the bracketed host of a URL (scheme://[...] or the %5B/%5D percent-encoded bracket form) or the value of a host / hostname / ip / address argument. It does not resolve DNS names that point at transition addresses, does not cover 100.64.0.0/10 CGNAT or 0.0.0.0/8 embeddings, and does not decode the RFC 6052 /32-/64 NAT64 layouts where the IPv4 is split around the u-octet. Overlap is intentional and partial: the dotted IPv4-mapped forms are already covered by ATR-2026-00013, hex ::ffff:7f00:1 / ::ffff:a9fe:a9fe by ATR-2026-02193, and the 169.254.169.254 NAT64 / 6to4 spellings by ATR-2026-02140; this rule adds the IPv4-compatible hex, 6to4 and NAT64 embeddings of loopback and RFC 1918 targets that no existing rule covers. CWE-918 (Server-Side Request Forgery).
9
+ author: ATR Community (CVE sync)
10
+ date: 2026/10/22
11
+ schema_version: '0.1'
12
+ detection_tier: pattern
13
+ maturity: test
14
+ severity: high
15
+ references:
16
+ cve:
17
+ - CVE-2026-55787
18
+ ghsa:
19
+ - GHSA-794r-5rp2-fpg8
20
+ euvd:
21
+ - EUVD-2026-101588
22
+ cwe:
23
+ - CWE-918
24
+ owasp_llm:
25
+ - LLM06:2025
26
+ owasp_agentic:
27
+ - ASI02:2026
28
+ mitre_atlas:
29
+ - AML.T0049 - Exploit Public-Facing Application
30
+ mitre_attack:
31
+ - 'T1552.005 - Unsecured Credentials: Cloud Instance Metadata API'
32
+ - T1046 - Network Service Discovery
33
+ external:
34
+ - https://github.com/flytohub/flyto-core/security/advisories/GHSA-794r-5rp2-fpg8
35
+ - https://github.com/flytohub/flyto-core/commit/519e040384a74984848c4e84a6600e5f1020a6d2
36
+ - https://github.com/flytohub/flyto-core/releases/tag/v2.26.3
37
+ - https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-101588
38
+ metadata_provenance:
39
+ cve: human-reviewed
40
+ ghsa: human-reviewed
41
+ cwe: human-reviewed
42
+ owasp_llm: human-reviewed
43
+ owasp_agentic: human-reviewed
44
+ mitre_atlas: human-reviewed
45
+ mitre_attack: human-reviewed
46
+ compliance:
47
+ eu_ai_act:
48
+ - article: '15'
49
+ context: Article 15 (accuracy, robustness and cybersecurity) requires high-risk AI systems to resist attempts to misuse their tools; this rule provides detection evidence supporting that requirement by flagging agent fetch-tool URLs that smuggle an internal IPv4 target inside an IPv6 transition address past an SSRF guard.
50
+ strength: primary
51
+ - article: '9'
52
+ context: Article 9 risk management must treat identified risks; this rule provides detection evidence supporting the treatment of the transition-address SSRF bypass risk for agent workflows whose outbound-request guard may not unwrap embedded IPv4.
53
+ strength: secondary
54
+ nist_ai_rmf:
55
+ - function: Measure
56
+ subcategory: MS.2.7
57
+ context: MS.2.7 requires AI system security and resilience to be evaluated and documented; inspecting every outbound URL argument for transition-encoded internal targets is a measurable per-invocation control at the agent-to-network boundary.
58
+ strength: primary
59
+ - function: Manage
60
+ subcategory: MG.3.2
61
+ context: MG.3.2 requires third-party and pre-built components to be monitored in operation; agent workflow kernels such as flyto-core ship their own SSRF guards, and this rule monitors for the input class those guards are known to miss.
62
+ strength: secondary
63
+ iso_42001:
64
+ - clause: '8.1'
65
+ context: Clause 8.1 operational planning and control is supported by this rule's runtime detection of transition-address SSRF targets in agent tool calls.
66
+ strength: primary
67
+ - clause: '8.3'
68
+ context: Clause 8.3 AI risk treatment is supported by this rule's detection of the SSRF guard bypass before the request leaves the agent.
69
+ strength: secondary
70
+ tags:
71
+ category: tool-poisoning
72
+ subcategory: ssrf-ipv6-transition-address-bypass
73
+ scan_target: tool_args
74
+ confidence: high
75
+ source: cve-disclosure
76
+ agent_source:
77
+ type: tool_call
78
+ framework:
79
+ - any
80
+ provider:
81
+ - any
82
+ detection:
83
+ condition: any
84
+ false_positives:
85
+ - Security test suites and SSRF-guard fixtures (for example flyto-core tests/core/test_ssrf_ipv6_transition.py) that pass these addresses to a fetch tool on purpose will match; that is the intended behaviour for a detection corpus.
86
+ - A NAT64 or 6to4 deployment that deliberately reaches an internal RFC 1918 service through its translated IPv6 address would match; such deployments are rare and should be allow-listed by destination.
87
+ - Public IPv4 embeddings (::ffff:8.8.8.8, 64:ff9b::808:808, 2002:808:808::) and ordinary native IPv6 hosts do not match.
88
+ conditions:
89
+ - field: tool_args
90
+ operator: regex
91
+ value: (?i)//(?:[^\s/@\[\]"]{1,64}@)?(?:\[|%5b)(?:(?:(?:0{1,4}:){1,4}|:):(?:ffff:(?:0{1,4}:)?)?|(?:0{1,4}:){5}(?:ffff|0{1,4}):|0{0,2}64:ff9b(?::0{0,3}1)?(?::0{1,4}){0,4}::?)(?:(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f]{1,4}|(?:127|10)(?:\.\d{1,3}){3}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2}|(?:192\.168|169\.254)(?:\.\d{1,3}){2})(?:\]|%5d)
92
+ description: URL bracketed host is an IPv4-compatible, IPv4-mapped/translated or NAT64 address whose final 32 bits are a loopback, RFC 1918 or link-local IPv4
93
+ - field: tool_args
94
+ operator: regex
95
+ value: (?i)//(?:[^\s/@\[\]"]{1,64}@)?(?:\[|%5b)0{0,2}2002:(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f:]{0,35}(?:\]|%5d)
96
+ description: URL bracketed host is a 6to4 (2002::/16) address whose embedded IPv4 (bits 16-47) is a loopback, RFC 1918 or link-local IPv4
97
+ - field: tool_args
98
+ operator: regex
99
+ value: (?i)["'](?:host|hostname|host_?name|ip|ip_?addr(?:ess)?|addr(?:ess)?|target_?host|server)["']\s*:\s*["']\[?(?:(?:(?:0{1,4}:){1,4}|:):(?:ffff:(?:0{1,4}:)?)?(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f]{1,4}|0{0,2}64:ff9b(?::0{0,3}1)?(?::0{1,4}){0,4}::?(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f]{1,4}|0{0,2}2002:(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f:]{0,35})\]?["']
100
+ description: Host-typed tool argument whose whole value is an IPv6 transition address (hex embedding) carrying a loopback, RFC 1918 or link-local IPv4
101
+ response:
102
+ actions:
103
+ - alert
104
+ - snapshot
105
+ message_template: |
106
+ [ATR-2026-02868] HIGH: an agent tool is being sent to an IPv6 transition address (IPv4-compatible, IPv4-mapped, 6to4 or NAT64) whose embedded IPv4 is loopback, private or cloud metadata. This is the CVE-2026-55787 (flyto-core) SSRF guard bypass shape: unwrap the embedded IPv4 before the private-address check, and block the request.
107
+ confidence: 78
@@ -0,0 +1,94 @@
1
+ title: Invisible Unicode Control Before a Directive in a Tool Description or Skill File
2
+ id: ATR-2026-02870
3
+ rule_version: 1
4
+ status: experimental
5
+ description: |
6
+ Detects a zero-width, soft-hyphen or bidirectional control character placed at the start of a clause that gives the model or agent an instruction, in the static text an agent reads before it acts: an MCP tool description (tools/list at registration time) or a SKILL.md file. The control is invisible in most renderers, so a reviewer reading the description sees an ordinary sentence while the model still reads the directive after it.
7
+ Controls covered: U+200B ZERO WIDTH SPACE, U+200C ZWNJ, U+200D ZWJ, U+2060 WORD JOINER, U+180E MONGOLIAN VOWEL SEPARATOR, U+00AD SOFT HYPHEN, U+FEFF (only after punctuation or a word, not at the start of the text or of a line, where it is a byte-order mark), U+200E LRM, U+200F RLM, and the bidi embedding, override and isolate controls U+202A-U+202E and U+2066-U+2069. A run of up to seven controls counts; the last one before the directive must not be a closing control (U+202C PDF, U+2069 PDI), so a directive that follows a closed embedding is not attributed to it.
8
+ Presence alone does not trigger. These characters are legitimate in right-to-left text (LRM/RLM and embeddings around Latin words in Hebrew, Arabic and Persian), in Persian and Indic shaping (ZWNJ, ZWJ), in emoji ZWJ sequences, in hyphenation hints (soft hyphens inside German compounds), in localized keyboard shortcuts wrapped in LRE/PDF, and in text pasted from chat clients (ZWSP after an @ sign). The rule therefore requires two things: the control starts a clause (it follows ASCII punctuation, an ASCII word and a space, CJK sentence punctuation, or the start of the text or a line, not a letter it is glued to and not a right-to-left word), and the clause is a directive addressed to the model: an instruction override, an attention header whose clause tells the model to read, send or ignore something, an injected <IMPORTANT> or <system> tag, a response-timing hook (before answering, after responding) tied to sending secrets, conversation or a URL, an always/must directive to call this tool first or instead of others, an exfiltration verb on secrets, conversation, environment or credential files, a read of ~/.ssh, .env or cloud credential files, or a concealment directive (do not tell the user).
9
+ Surfaces: the conditions read tool_description, so the rule runs on an mcp_exchange event that carries fields.tool_description, on each tool definition of a tools/list document scanned with `atr scan` (the tool-definition scope admits a tool_call rule that reads tool_description), and on SKILL.md files (scan_target both; in a skill scan every field resolves to the document). The engine strips most of these controls before matching but also tests each regex against the raw text, which is where these patterns match.
10
+ Limits: the directive vocabulary is English only. A control glued between two letters (for example a soft hyphen inside a word) is not counted, so a directive written as one run with no space or punctuation before the control is missed. Text reversed under an override (RLO with the directive stored backwards) and Unicode tag characters (U+E0000-U+E007F) are out of scope; ATR-2026-00276 and ATR-2026-00258 cover those on other fields. Directives without any invisible control are left to the tool-poisoning rules (for example ATR-2026-00100 to -00107). Reported in issue #653.
11
+ author: ATR Community
12
+ date: 2026/10/24
13
+ schema_version: '0.1'
14
+ detection_tier: pattern
15
+ maturity: test
16
+ severity: high
17
+ references:
18
+ owasp_llm:
19
+ - LLM01:2025
20
+ owasp_agentic:
21
+ - ASI01:2026
22
+ - ASI04:2026
23
+ mitre_atlas:
24
+ - 'AML.T0051.001 - LLM Prompt Injection: Indirect'
25
+ external:
26
+ - https://github.com/Agent-Threat-Rule/agent-threat-rules/issues/653
27
+ - https://trojansource.codes/
28
+ - https://www.unicode.org/reports/tr9/
29
+ compliance:
30
+ eu_ai_act:
31
+ - article: '15'
32
+ context: Article 15 (accuracy, robustness and cybersecurity) requires high-risk AI systems to resist attempts by third parties to alter their behaviour; this rule provides detection evidence for that requirement by flagging tool descriptions and skill files that hide a directive to the model behind an invisible Unicode control.
33
+ strength: primary
34
+ - article: '13'
35
+ context: Article 13 (transparency) requires that deployers can understand the system they operate; a directive hidden behind an invisible control is text the deployer cannot see in review but the model follows, and this rule surfaces it.
36
+ strength: secondary
37
+ - article: '9'
38
+ context: Article 9 (risk management system) requires identified risks to be treated; this rule is a detection control for the hidden-directive risk in third-party MCP tools and skills, applied before the agent loads them.
39
+ strength: secondary
40
+ nist_ai_rmf:
41
+ - subcategory: MS.2.7
42
+ context: MEASURE 2.7 requires security and resilience to be evaluated and documented; scanning tool definitions and skill files for directives hidden behind invisible controls is a measurable check on third-party agent components.
43
+ strength: primary
44
+ - subcategory: MG.3.1
45
+ context: MANAGE 3.1 requires risks from third-party resources to be monitored; MCP tool descriptions and skills are third-party text the model treats as instructions, and this rule monitors them for hidden directives.
46
+ strength: secondary
47
+ - subcategory: MP.5.1
48
+ context: MAP 5.1 requires the likelihood and impact of identified attack patterns to be characterised; the gap between what a reviewer sees and what the model reads is one such pattern, and this rule detects its use in static tool text.
49
+ strength: secondary
50
+ iso_42001:
51
+ - clause: '8.1'
52
+ context: ISO/IEC 42001 Clause 8.1 (operational planning and control, including control of externally provided processes) is supported by this rule's detection of hidden directives in externally provided MCP tools and skills.
53
+ strength: primary
54
+ - clause: '6.2'
55
+ context: ISO/IEC 42001 Clause 6.2 (AI objectives and planning) calls for risk treatment of known attack patterns; detecting a directive hidden behind an invisible Unicode control in tool text is such a treatment.
56
+ strength: secondary
57
+ tags:
58
+ category: tool-poisoning
59
+ subcategory: invisible-control-before-directive
60
+ scan_target: both
61
+ confidence: medium
62
+ agent_source:
63
+ type: tool_call
64
+ framework:
65
+ - mcp
66
+ - any
67
+ provider:
68
+ - any
69
+ detection:
70
+ condition: any
71
+ conditions:
72
+ - field: tool_description
73
+ operator: regex
74
+ value: "(?:(?:[\\x21-\\x2F\\x3A-\\x40\\x5B-\\x60\\x7B-\\x7E、。!,:;?][ \\t]{0,3}|[A-Za-z0-9][ \\t]{1,3})[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨\uFEFF‬⁩]{0,6}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨\uFEFF]|(?:^|[\\r\\n])[ \\t]{0,8}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨‬⁩]{0,6}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨])[ \\t\"'*_`>(\\[]{0,4}(?:(?:also|then|please|now|first|additionally|and|but|ai|assistant|agent|model|llm|claude)[,:]?\\s+){0,2}(?:(?:ignore|disregard|forget|override|bypass)\\s+(?:all\\s+|any\\s+)?(?:(?:the|your|of\\s+the|previous|prior|above|earlier|preceding|system|original|other|safety|security|developer)\\s+){0,3}(?:instructions?|rules|prompts?|guidelines|directives|polic(?:y|ies)|guardrails|restrictions|constraints)|(?:important|attention|urgent|critical|system\\s+(?:note|override|message|instruction)|admin(?:istrator)?\\s+note|note\\s+to\\s+(?:the\\s+)?(?:ai|assistant|model|agent|llm))\\s{0,3}[:!>-][^\\n]{0,60}?\\b(?:ignore|disregard|forget|read|send|forward|upload|include|attach|exfiltrate|leak|(?:do\\s+not|don't|never)\\s+(?:tell|mention|inform|reveal)|(?:before|after|when)\\s+(?:answering|responding|replying|you\\s+(?:answer|respond|reply)))\\b|<\\s{0,3}/?(?:important|system|instructions?|secret|hidden)\\s{0,3}>|you\\s+are\\s+now\\b|new\\s+(?:system\\s+)?instructions?\\s{0,3}:)"
75
+ description: Invisible control at the start of a clause, followed by an instruction override (ignore/disregard previous instructions), an attention header whose clause tells the model to read, send, ignore or answer differently, an injected <IMPORTANT>/<system> tag, or a new-instructions / you-are-now header
76
+ - field: tool_description
77
+ operator: regex
78
+ value: "(?:(?:[\\x21-\\x2F\\x3A-\\x40\\x5B-\\x60\\x7B-\\x7E、。!,:;?][ \\t]{0,3}|[A-Za-z0-9][ \\t]{1,3})[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨\uFEFF‬⁩]{0,6}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨\uFEFF]|(?:^|[\\r\\n])[ \\t]{0,8}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨‬⁩]{0,6}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨])[ \\t\"'*_`>(\\[]{0,4}(?:(?:also|then|please|now|first|additionally|and|but|ai|assistant|agent|model|llm|claude)[,:]?\\s+){0,2}(?:(?:before|after|when|while|prior\\s+to|whenever|each\\s+time)\\s+(?:you\\s+)?(?:respond(?:ing)?|answer(?:ing)?|repl(?:y|ying)|returning\\s+(?:the\\s+)?(?:results?|output|answer)|(?:using|calling|invoking)\\s+(?:this|any|another|other)\\s+tools?)\\b[^\\n]{0,80}?\\b(?:(?:send|forward|upload|post|attach|append|include|copy|transmit|exfiltrate|leak|email|share)\\b[^\\n]{0,60}?(?:(?:conversation|chat\\s+history|message\\s+history|system\\s+prompt|(?:process\\s+)?environment|\\benv\\s+vars?|credentials?|secrets?|api[\\s_-]?keys?|(?:access|auth(?:entication)?|session|bearer)\\s+tokens?|passwords?|private\\s+keys?|ssh\\s+keys?|~/\\.ssh|\\.ssh/|id_rsa|id_ed25519|\\.env\\b|\\.aws/|cookies)|https?://|[\\w.+-]{1,64}@[\\w-]{1,63}\\.[a-z]{2,})|(?:call|invoke|use)\\s+this\\s+(?:tool|function))|(?:always|must|you\\s+must|you\\s+should|make\\s+sure\\s+(?:to|you))\\s+(?:first\\s+)?(?:(?:call|invoke|use|run|prefer|choose|select)\\s+this\\s+(?:tool|function|server|skill|plugin)\\s+(?:first|before\\s+(?:any|all|every|other)|instead\\s+of|rather\\s+than|for\\s+(?:every|all|any)\\b|even\\s+(?:if|when))|(?:send|forward|upload|post|attach|append|include|copy|transmit|exfiltrate|leak|email|share)\\b[^\\n]{0,60}?(?:conversation|chat\\s+history|message\\s+history|system\\s+prompt|(?:process\\s+)?environment|\\benv\\s+vars?|credentials?|secrets?|api[\\s_-]?keys?|(?:access|auth(?:entication)?|session|bearer)\\s+tokens?|passwords?|private\\s+keys?|ssh\\s+keys?|~/\\.ssh|\\.ssh/|id_rsa|id_ed25519|\\.env\\b|\\.aws/|cookies))|(?:(?:for\\s+best\\s+results|for\\s+(?:debugging|analytics|telemetry|quality|audit)(?:\\s+purposes)?|to\\s+improve\\s+[a-z]+)[,:]?\\s+)?(?:silently\\s+|quietly\\s+|secretly\\s+)?(?:send|forward|upload|post|attach|append|include|copy|transmit|exfiltrate|leak|email|share)\\b[^\\n]{0,60}?(?:conversation|chat\\s+history|message\\s+history|system\\s+prompt|(?:process\\s+)?environment|\\benv\\s+vars?|credentials?|secrets?|api[\\s_-]?keys?|(?:access|auth(?:entication)?|session|bearer)\\s+tokens?|passwords?|private\\s+keys?|ssh\\s+keys?|~/\\.ssh|\\.ssh/|id_rsa|id_ed25519|\\.env\\b|\\.aws/|cookies)|(?:read|cat|open|load|print|dump|return)\\s+[^\\n]{0,40}?(?:~/\\.ssh|\\.ssh/|id_rsa|id_ed25519|/etc/(?:passwd|shadow)|\\.env\\b|\\.aws/credentials|\\.npmrc|\\.netrc|\\.git-credentials|\\.kube/config))"
79
+ description: Invisible control at the start of a clause, followed by a response-timing hook (before answering, after responding, when calling another tool) that sends or reads secrets, conversation or a URL; an always/must directive to call this tool first or instead of others, or to send secrets; an exfiltration verb on secrets, conversation, environment or credential files; or a read of ~/.ssh, .env, .aws/credentials and similar files
80
+ - field: tool_description
81
+ operator: regex
82
+ value: "(?:(?:[\\x21-\\x2F\\x3A-\\x40\\x5B-\\x60\\x7B-\\x7E、。!,:;?][ \\t]{0,3}|[A-Za-z0-9][ \\t]{1,3})[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨\uFEFF‬⁩]{0,6}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨\uFEFF]|(?:^|[\\r\\n])[ \\t]{0,8}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨‬⁩]{0,6}[​‌‍⁠᠎­‎‏‪‫‭‮⁦⁧⁨])[ \\t\"'*_`>(\\[]{0,4}(?:(?:also|then|please|now|first|additionally|and|but|ai|assistant|agent|model|llm|claude)[,:]?\\s+){0,2}(?:(?:do\\s+not|don't|never|without)\\s+(?:tell(?:ing)?|inform(?:ing)?|notify(?:ing)?|mention(?:ing)?|reveal(?:ing)?|disclos(?:e|ing)|alert(?:ing)?|warn(?:ing)?|ask(?:ing)?)\\s+(?:this\\s+to\\s+|anything\\s+to\\s+)?(?:the\\s+)?(?:user|human|operator)\\b|(?:do\\s+not|don't|never)\\s+let\\s+(?:the\\s+)?(?:user|human)\\s+(?:know|see))"
83
+ description: Invisible control at the start of a clause, followed by a concealment directive (do not tell, inform, mention or reveal to the user)
84
+ false_positives:
85
+ - A security write-up, test fixture or rule file that quotes a poisoned tool description verbatim, invisible control included, will match; that is the text the rule looks for.
86
+ - A localized description whose right-to-left text puts an LRM or RLM after an ASCII word and a space, directly before an English sentence that tells the model to send credentials or not tell the user, would match. No such text was found in the benign corpora; the non-Latin samples in the true negatives do not match.
87
+ - Presence of the controls alone (right-to-left marks, ZWNJ in Persian, emoji ZWJ sequences, soft hyphens inside words, LRE/PDF around a keyboard shortcut, ZWSP from pasted chat text, a byte-order mark at the start of a file) does not match.
88
+ response:
89
+ actions:
90
+ - alert
91
+ - snapshot
92
+ message_template: |
93
+ [ATR-2026-02870] HIGH: an invisible Unicode control (zero-width, soft hyphen or bidi) starts a clause that instructs the model, in a tool description or skill file. Show the description with control characters made visible, and review the tool or skill before the agent loads it.
94
+ confidence: 60
package/spec/README.md CHANGED
@@ -56,7 +56,10 @@ spec/
56
56
  │ └── atr-to-nist-csf-2.0.md ← NIST CSF 2.0 (NIST IR 8596 Informative Reference draft)
57
57
  │
58
58
  ├── category-registry/
59
- │ └── v1.0.yaml ← (new) versioned top-level category list
59
+ │ ├── v1.0.yaml ← (new) versioned top-level category list
60
+ │ └── gemara/ ← (new) the registry as Gemara catalogs, generated
61
+ │ from v1.0.yaml; published as an OCI bundle
62
+ │ by publish-gemara.yml
60
63
  │
61
64
  ├── schema/ ← (new) JSON Schemas
62
65
  │ ├── rule.schema.json ← rule format JSON Schema
@@ -266,8 +269,8 @@ The spec is being prepared for:
266
269
 
267
270
  1. **OASIS Open Project (primary)** as adjacent to CoSAI.
268
271
  Target: Q3 2026 acceptance, Q1 2027 first Committee Specification.
269
- 2. **NIST CAISI (citation target)**.
270
- No formal submission window currently open; awaiting next RFI.
272
+ 2. **NIST CAISI**, as a possible future citation venue only.
273
+ ATR has no engagement with CAISI.
271
274
  3. **IETF (informational draft, transport / OTEL emission only)**
272
275
  when reference implementations are stable.
273
276
 
@@ -58,7 +58,7 @@ All conformant engines MUST emit these fields on every event.
58
58
  | `@timestamp` | RFC 3339 UTC string | When the rule fired. |
59
59
  | `atr.event_id` | UUID v7 (time-ordered) | Globally unique event identifier. |
60
60
  | `atr.spec_version` | string | ATR spec version this event conforms to. v1.0 = `"1.0"`. |
61
- | `atr.engine_id` | string | Identifier of the engine that produced the event. Format: `<vendor>/<product>/<version>`. Example: `atr/typescript-reference/3.1.0`, `cisco/ai-defense/2.4.1`, `microsoft/agent-governance-toolkit/2026.05`. |
61
+ | `atr.engine_id` | string | Identifier of the engine that produced the event. Format: `<vendor>/<product>/<version>`. Example: `atr/typescript-reference/3.1.0`, `example-vendor/example-scanner/2.4.1`, `example-org/agent-gateway/2026.05`. |
62
62
 
63
63
  ### Rule attribution
64
64
 
@@ -105,7 +105,7 @@ All conformant engines MUST emit these fields on every event.
105
105
  | Field | Type | Description |
106
106
  |---|---|---|
107
107
  | `evidence.observation_id` | UUID | Identifier for cross-reference into OSCAL `observation` records. Same as `atr.event_id` recommended unless an existing system has its own. |
108
- | `evidence.signature` | base64 ed25519 | Signature over the canonical JSON encoding of this event. Signed by the engine's deployment-time key. Required for EU AI Act Article 50 evidence chains and NIST AI RMF audit pipelines. May be omitted in `dev_mode` deployments. |
108
+ | `evidence.signature` | base64 ed25519 | Signature over the canonical JSON encoding of this event. Signed by the engine's deployment-time key. Intended for evidence chains such as EU AI Act record-keeping and OSCAL-based audit pipelines. May be omitted in `dev_mode` deployments. |
109
109
  | `evidence.signature_key_id` | string | Identifier of the signing key. SHOULD reference a key registered with the deployer's CA. |
110
110
  | `evidence.upstream_chain` | array \| null | When this event is part of a multi-agent chain (A2A), the upstream event IDs that led to this detection. Enables forensic chain reconstruction. |
111
111
 
@@ -132,12 +132,17 @@ is known).
132
132
  | `agent.from_id` | string |
133
133
  | `agent.to_id` | string |
134
134
  | `agent.delegation_chain` | array of {agent_id, capability_grant, granted_by} |
135
- | `agent.identity_assertion` | JWT \| null |
136
-
137
- The `agent.identity_assertion` field anticipates the IETF AI agent
138
- auth drafts (`draft-klrc-aiagent-auth-00`, `draft-ni-a2a-ai-agent-
139
- security-requirements-01`) — once those reach RFC, the field carries
140
- the canonical assertion format.
135
+ | `agent.identity_assertion` | string \| null (a reference, never the assertion) |
136
+
137
+ The `agent.identity_assertion` field holds a reference to the identity
138
+ assertion that authorised the agent, written `<issuer>#<assertion id>`
139
+ (for a JWT, its `iss` and `jti`, or a SHA-256 digest of the token when it
140
+ has no `jti`). It never holds the assertion itself:
141
+ an assertion is a bearer credential, and the Forbidden fields section
142
+ below rules out raw tokens. The IETF AI agent auth drafts
143
+ (`draft-klrc-aiagent-auth-00`, `draft-ni-a2a-ai-agent-security-requirements-01`)
144
+ may settle a canonical identifier format; until then the reference form
145
+ above applies.
141
146
 
142
147
  ### Memory write detail (when `atr.matched_field == "memory_write"`)
143
148
 
@@ -457,7 +457,7 @@ If `detection.condition` is absent, the default is `any of *` — Rule matches i
457
457
 
458
458
  ### 8.5 Evaluation Determinism
459
459
 
460
- Trace evaluation MUST be deterministic for a fixed Trace input. Engines MUST NOT introduce randomization, time-of-day branching, or sampling into Trace evaluation. This is identical to SPEC §6.4 and applies normatively.
460
+ Trace evaluation MUST be deterministic for a fixed Trace input. Engines MUST NOT introduce randomization, time-of-day branching, or sampling into Trace evaluation. This is identical to SPEC §6.5 and applies normatively.
461
461
 
462
462
  ### 8.6 Performance Considerations
463
463
 
@@ -508,7 +508,7 @@ This is the inverse direction of §6.5's calibration workflow: §6.5 ensures the
508
508
 
509
509
  Rules MAY declare `references.nist_csf` and `references.etsi_ts_104223` to align with the two major sovereign cybersecurity frameworks for AI agents:
510
510
 
511
- - `nist_csf`: NIST CSF 2.0 subcategory IDs (e.g., `DE.CM-09`, `PR.IR-01`). Required for citation in NIST IR 8596 Cyber AI Profile Informative References.
511
+ - `nist_csf`: NIST CSF 2.0 subcategory IDs (e.g., `DE.CM-09`, `PR.IR-01`). Needed if a mapping is ever offered as a NIST IR 8596 Cyber AI Profile Informative Reference.
512
512
  - `etsi_ts_104223`: ETSI TS 104 223 principle/sub-principle IDs (e.g., `P4.3`). ETSI TS 104 223 upstreamed UK NCSC's AI Cyber Code of Practice; this binding lets ATR Rules be cited in NCSC Implementation Guides.
513
513
 
514
514
  Both fields are arrays of strings to permit multi-framework alignment per Rule.
@@ -0,0 +1,102 @@
1
+ # Gemara catalogs for the ATR category registry
2
+
3
+ This directory holds the ATR category registry
4
+ ([`../v1.0.yaml`](../v1.0.yaml)) expressed as [Gemara](https://github.com/gemaraproj/gemara)
5
+ catalogs, validated against the Gemara CUE schemas and published by
6
+ `.github/workflows/publish-gemara.yml` as a signed OCI bundle.
7
+
8
+ | File | Gemara type | What it holds |
9
+ |---|---|---|
10
+ | `atr-attack-vector-catalog.yaml` | `#VectorCatalog` | One vector per registry category. The vector id is the registry category id (which is also the `rules/` directory name) and the description is the registry's description. |
11
+ | `ai-agent-capability-catalog.yaml` | `#CapabilityCatalog` | Capabilities of AI agent systems that create the opportunity for the threats. |
12
+ | `atr-threat-catalog.yaml` | `#ThreatCatalog` | Threats at impact level, each mapped to the capabilities that enable it and the vectors that realize it. Every vector is reached by at least one threat. |
13
+ | `atr-detection-control-catalog.yaml` | `#ControlCatalog` | Detection controls an operator can apply with ATR, cut by when inspection happens. **Root artifact of the bundle.** |
14
+
15
+ ## Why they live here
16
+
17
+ The vector catalog is a projection of the registry. When the projection and
18
+ the registry lived in different repositories, nothing stopped them drifting
19
+ apart. Here they sit side by side, and the catalogs are generated, not
20
+ hand-edited.
21
+
22
+ ## Editing
23
+
24
+ The four catalogs are **generated**; do not edit them by hand. Edit one of the
25
+ two inputs and regenerate:
26
+
27
+ - [`../v1.0.yaml`](../v1.0.yaml), the registry: which categories exist, their
28
+ ids and their descriptions.
29
+ - [`src/editorial.yaml`](src/editorial.yaml): everything the registry does not
30
+ say (vector titles and grouping, and the capability, threat and control
31
+ catalogs).
32
+
33
+ ```sh
34
+ npx tsx scripts/generate-gemara-catalogs.ts # regenerate
35
+ npx tsx scripts/generate-gemara-catalogs.ts --check # what CI checks
36
+ ```
37
+
38
+ The generator stops when the inputs disagree: a registry category with no
39
+ editorial entry, an editorial entry for a category the registry does not have,
40
+ a vector no threat reaches, or a reference to an id that does not exist.
41
+ `tests/gemara-catalogs.test.ts` fails when the committed catalogs differ from
42
+ the generator output.
43
+
44
+ ## Validating
45
+
46
+ ```sh
47
+ git clone --depth 1 --branch v1.6.0 https://github.com/gemaraproj/gemara.git /tmp/gemara
48
+ scripts/validate-gemara-catalogs.sh /tmp/gemara
49
+ ```
50
+
51
+ Each catalog is checked with `cue vet -c -d '#<metadata.type>'` against the
52
+ Gemara release named by `bundle.gemara-version` in `src/editorial.yaml`.
53
+
54
+ ## Versioning
55
+
56
+ `bundle.version` in `src/editorial.yaml` is stamped as `metadata.version` on
57
+ all four catalogs and is the OCI tag. Its MAJOR.MINOR is the registry version
58
+ (the generator enforces this); PATCH moves with any change that alters a
59
+ generated catalog. A published version is never overwritten. While the
60
+ registry's `status` is `draft`, every catalog carries `draft: true`.
61
+
62
+ ## Publication
63
+
64
+ `.github/workflows/publish-gemara.yml` publishes the bundle to
65
+
66
+ ```
67
+ ghcr.io/agent-threat-rule/agent-threat-rules/gemara:<bundle version>
68
+ ```
69
+
70
+ using [`gemara-publish-action`](https://github.com/gemaraproj/gemara-publish-action).
71
+ Assembly starts at the control catalog and follows each catalog's `imports`
72
+ to the threat, capability and vector catalogs, so one bundle carries all four.
73
+
74
+ It runs by manual dispatch from a release tag. ATR release tags also start
75
+ it, but those runs are skipped until the repository variable
76
+ `GEMARA_PUBLISH_ON_TAGS` is `true`, so the first publish is always a manual
77
+ run. It never runs on a push to `main`. Rule changes move release tags, not the bundle: a release
78
+ whose catalogs are already published under their version pushes nothing, and
79
+ a release whose catalogs changed without a version bump fails before pushing.
80
+
81
+ The bundle is signed keyless with the workflow's GitHub OIDC identity; no key
82
+ is stored. To verify:
83
+
84
+ ```sh
85
+ cosign verify ghcr.io/agent-threat-rule/agent-threat-rules/gemara:<version> \
86
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
87
+ --certificate-identity-regexp \
88
+ '^https://github\.com/Agent-Threat-Rule/agent-threat-rules/\.github/workflows/publish-gemara\.yml@refs/tags/v'
89
+ ```
90
+
91
+ Once a version is published, to reference a catalog from another Gemara artifact, use the bundle as the
92
+ mapping-reference `url` and the catalog's `metadata.id` as the reference `id`:
93
+
94
+ ```yaml
95
+ mapping-references:
96
+ - id: ATR-AI-AGENT-VECTOR-CATALOG
97
+ title: AI Agent Attack Vector Catalog (ATR)
98
+ version: "1.0.0"
99
+ url: oci://ghcr.io/agent-threat-rule/agent-threat-rules/gemara:1.0.0
100
+ ```
101
+
102
+ License: MIT.
@@ -0,0 +1,157 @@
1
+ # GENERATED by scripts/generate-gemara-catalogs.ts. Do not edit by hand.
2
+ # Inputs: spec/category-registry/v1.0.yaml (the category registry) and
3
+ # spec/category-registry/gemara/src/editorial.yaml
4
+ # Regenerate: npx tsx scripts/generate-gemara-catalogs.ts
5
+ # License: MIT
6
+ metadata:
7
+ id: ATR-AI-AGENT-CAPABILITY-CATALOG
8
+ type: CapabilityCatalog
9
+ gemara-version: 1.6.0
10
+ version: 1.0.0
11
+ description: >-
12
+ Capabilities of AI agent systems that create the opportunity for the threats
13
+ catalogued by Agent Threat Rules (ATR). Each capability describes a feature
14
+ or function an agent can perform, not a threat. Threats in the companion
15
+ threat catalog reference these capabilities. Authored by the ATR project and
16
+ validated against the Gemara schemas; ATR hosts the content, Gemara defines
17
+ the schema.
18
+ author:
19
+ id: atr
20
+ name: Agent Threat Rules
21
+ type: Software Assisted
22
+ contact:
23
+ name: ATR maintainers
24
+ affiliation: Agent Threat Rules
25
+ email: adam@agentthreatrule.org
26
+ draft: true
27
+ title: AI Agent Capability Catalog
28
+ groups:
29
+ - id: model-interaction
30
+ title: Model Interaction
31
+ description: >-
32
+ Capabilities by which an agent receives instructions and untrusted content
33
+ into a language model and acts on the model's output.
34
+ - id: tool-and-protocol
35
+ title: Tool and Protocol Access
36
+ description: >-
37
+ Capabilities by which an agent invokes external tools and connects to tool
38
+ servers over protocols such as the Model Context Protocol (MCP).
39
+ - id: extensibility
40
+ title: Extensibility
41
+ description: >-
42
+ Capabilities by which an agent loads and runs third-party extensions such
43
+ as skills, plugins, and packaged capability bundles.
44
+ - id: state-and-memory
45
+ title: State and Memory
46
+ description: >-
47
+ Capabilities by which an agent reads and writes persistent state,
48
+ conversation context, and long-term memory across turns and sessions.
49
+ - id: autonomy-and-coordination
50
+ title: Autonomy and Coordination
51
+ description: >-
52
+ Capabilities by which an agent acts without per-step human approval and
53
+ coordinates with other agents.
54
+ - id: model-supply-chain
55
+ title: Model supply chain
56
+ description: >-
57
+ Capabilities by which the model an agent depends on is built, packaged and
58
+ loaded. These sit outside agent runtime: they shape behaviour before any
59
+ request is served.
60
+ capabilities:
61
+ - id: CAP-MODEL-INFERENCE
62
+ title: Model Inference on Untrusted Input
63
+ description: >-
64
+ The agent passes user instructions and content drawn from external sources
65
+ into a language model and treats the resulting output as actionable.
66
+ Mixing trusted instructions with untrusted content in a single context
67
+ window is the feature that makes prompt injection and jailbreak attempts
68
+ possible.
69
+ group: model-interaction
70
+ - id: CAP-OUTPUT-ACTION
71
+ title: Acting on Model Output
72
+ description: >-
73
+ The agent converts model output into actions, tool calls, or content
74
+ returned to downstream systems. Because output is consumed without an
75
+ independent trust boundary, manipulated output can redirect behaviour or
76
+ carry exfiltrated data.
77
+ group: model-interaction
78
+ - id: CAP-TOOL-INVOCATION
79
+ title: Tool Invocation
80
+ description: >-
81
+ The agent calls external tools and functions, passing arguments and
82
+ receiving results that re-enter the model context. Tool arguments and tool
83
+ results are an attack surface for injection, command execution, and
84
+ over-broad actions.
85
+ group: tool-and-protocol
86
+ - id: CAP-MCP-CONNECTION
87
+ title: MCP and Tool-Server Connection
88
+ description: >-
89
+ The agent connects to tool servers over the Model Context Protocol or
90
+ similar transports, consuming server-provided tool manifests, schemas, and
91
+ descriptions. Server-controlled metadata is trusted at connection time,
92
+ which creates the opportunity for tool poisoning and tool redefinition
93
+ (rug-pull) after initial approval.
94
+ group: tool-and-protocol
95
+ - id: CAP-CROSS-AGENT-MESSAGING
96
+ title: Cross-Agent Message Passing
97
+ description: >-
98
+ The agent sends and receives messages to and from other agents in a
99
+ multi-agent workflow. Messages from peer agents are treated as trusted
100
+ input, which lets a compromised or manipulated agent influence others.
101
+ group: tool-and-protocol
102
+ - id: CAP-SKILL-LOADING
103
+ title: Skill and Plugin Loading
104
+ description: >-
105
+ The agent loads third-party skills, plugins, or capability bundles and
106
+ executes the instructions and code they contain. Loaded extensions run
107
+ with the agent's privileges, which creates the opportunity for supply
108
+ chain compromise and backdoored or over-privileged skills.
109
+ group: extensibility
110
+ - id: CAP-MEMORY-WRITE
111
+ title: Memory and State Write
112
+ description: >-
113
+ The agent writes facts, instructions, or artifacts into persistent memory
114
+ or shared state that influences later turns and sessions. Writable memory
115
+ is the feature that lets poisoned content persist and take effect after
116
+ the originating input is gone.
117
+ group: state-and-memory
118
+ - id: CAP-MEMORY-READ
119
+ title: Context and Memory Read
120
+ description: >-
121
+ The agent reads conversation history, retrieved documents, and long-term
122
+ memory back into the active context. Reading attacker- influenced state
123
+ re-introduces untrusted content and is a channel for indirect injection
124
+ and staged data exfiltration.
125
+ group: state-and-memory
126
+ - id: CAP-AUTONOMOUS-ACTION
127
+ title: Autonomous Action Without Per-Step Approval
128
+ description: >-
129
+ The agent executes multi-step plans, loops, and side-effecting actions
130
+ without human approval at each step. Unbounded autonomy is the feature
131
+ that turns a single manipulated decision into runaway loops, resource
132
+ abuse, or unauthorized real-world actions.
133
+ group: autonomy-and-coordination
134
+ - id: CAP-PRIVILEGED-EXECUTION
135
+ title: Privileged and Delegated Execution
136
+ description: >-
137
+ The agent runs with delegated credentials and access to systems,
138
+ databases, files, and networks. Holding standing privilege creates the
139
+ opportunity for privilege escalation, sandbox escape, and use of the
140
+ agent's access beyond its intended scope.
141
+ group: autonomy-and-coordination
142
+ - id: CAP-MODEL-ARTIFACT-LOADING
143
+ title: Model Artifact Loading
144
+ description: >-
145
+ The agent depends on a serialized model artifact that is fetched from a
146
+ registry or filesystem and deserialized into an executable object before
147
+ any behavioural check runs. The artifact is trusted at the moment it is
148
+ least verified.
149
+ group: model-supply-chain
150
+ - id: CAP-TRAINING-DATA-INGESTION
151
+ title: Training and Fine-Tuning Data Ingestion
152
+ description: >-
153
+ The model the agent depends on is adapted using data drawn from sources
154
+ the operator does not fully control. Ingested data shapes behaviour after
155
+ the originating input is gone, and every deployment of the resulting model
156
+ carries that shaping.
157
+ group: model-supply-chain