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/dist/verdict.d.ts CHANGED
@@ -6,9 +6,42 @@
6
6
  *
7
7
  * @module agent-threat-rules/verdict
8
8
  */
9
- import type { ATRMatch, ATRSeverity, ATRVerdict } from './types.js';
9
+ import type { ATRMatch, ATRSeverity, ATRAction, ATRVerdict } from './types.js';
10
+ /**
11
+ * Whether a model, not a rule's deterministic logic, decided this match. A match
12
+ * without a decider (built outside the engine, as every match was before
13
+ * decidedBy existed) is read as deterministic; an unknown decider as a model.
14
+ */
15
+ export declare function isModelDecided(match: Pick<ATRMatch, 'decidedBy'>): boolean;
16
+ /**
17
+ * What a model-decided match may do.
18
+ * 'advisory' (default): it is reported and its observe-tier actions run, but
19
+ * it takes no part in the outcome and its actions above observe are not
20
+ * dispatched.
21
+ * 'enforce': it counts like any other match. Its false-positive rate has not
22
+ * been measured; choose this only if you accept that.
23
+ */
24
+ export type ModelDecisions = 'advisory' | 'enforce';
25
+ export interface VerdictOptions {
26
+ readonly modelDecisions?: ModelDecisions;
27
+ }
28
+ /** Read a modelDecisions setting, refusing anything but the two values. */
29
+ export declare function modelDecisionsFromConfig(value: unknown): ModelDecisions;
10
30
  /** Severity rank from most severe (0) to least severe (4) */
11
31
  export declare const SEVERITY_RANK: Readonly<Record<ATRSeverity, number>>;
32
+ /**
33
+ * Minimum confidence at which a high-severity match denies; below it, asks.
34
+ *
35
+ * Compared as given. A confidence the engine derives from a rule's
36
+ * `tags.confidence` label reaches this threshold only from a `high` label:
37
+ * `src/rule-confidence.ts` caps every lower label one basis point under it.
38
+ */
39
+ export declare const HIGH_SEVERITY_DENY_CONFIDENCE = 0.8;
40
+ /**
41
+ * Minimum confidence at which a medium-severity match asks; below it, allows.
42
+ * A `low` label reaches it exactly (0.6) when every condition matched.
43
+ */
44
+ export declare const MEDIUM_SEVERITY_ASK_CONFIDENCE = 0.6;
12
45
  /**
13
46
  * Check whether auto-response is enabled for a matched rule.
14
47
  * The auto_response_threshold field on ATRResponse indicates the
@@ -16,11 +49,22 @@ export declare const SEVERITY_RANK: Readonly<Record<ATRSeverity, number>>;
16
49
  * automatically (without human approval).
17
50
  */
18
51
  export declare function isAutoResponseEnabled(match: ATRMatch): boolean;
52
+ /**
53
+ * The observe-tier actions of matches whose other actions are not dispatched.
54
+ * Only actions the tier ladder names: actionTier() reads an unknown name as
55
+ * observe, and a name it does not know is not one this policy can vouch for.
56
+ */
57
+ export declare function observeActions(matches: readonly ATRMatch[]): readonly ATRAction[];
19
58
  /**
20
59
  * Compute a verdict from an array of ATR matches.
21
60
  *
61
+ * A match a model decided (isModelDecided) is reported but, unless
62
+ * options.modelDecisions is 'enforce', takes no part in the outcome, and of its
63
+ * actions only the observe tier is dispatched: no benign-corpus measurement
64
+ * stands behind a model's decision. Every other match counts as before.
65
+ *
22
66
  * This is a pure function -- no side effects, no mutation.
23
67
  * Returns a frozen ATRVerdict object.
24
68
  */
25
- export declare function computeVerdict(matches: readonly ATRMatch[]): ATRVerdict;
69
+ export declare function computeVerdict(matches: readonly ATRMatch[], options?: VerdictOptions): ATRVerdict;
26
70
  //# sourceMappingURL=verdict.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EAEX,UAAU,EAEX,MAAM,YAAY,CAAC;AAEpB,6DAA6D;AAC7D,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAM/D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,GACd,OAAO,CAcT;AAwED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,SAAS,QAAQ,EAAE,GAC3B,UAAU,CAkCZ"}
1
+ {"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,QAAQ,EAER,WAAW,EACX,SAAS,EACT,UAAU,EAEX,MAAM,YAAY,CAAC;AAsBpB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,OAAO,CAE1E;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,SAAS,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC1C;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAMvE;AAED,6DAA6D;AAC7D,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAM/D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,GACd,OAAO,CAcT;AAkDD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,SAAS,EAAE,CAIjF;AA8BD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,OAAO,GAAE,cAAmB,GAC3B,UAAU,CAmDZ"}
package/dist/verdict.js CHANGED
@@ -6,6 +6,41 @@
6
6
  *
7
7
  * @module agent-threat-rules/verdict
8
8
  */
9
+ import { ACTION_TIERS, TIERED_ACTIONS, actionTier } from './quality/action-eligibility.js';
10
+ /**
11
+ * Deciders that are a rule's own deterministic logic. Every other decider is a
12
+ * model: a semantic rule's judge (`semantic-judge`), the Tier 2.5 embedding
13
+ * signal (`embedding`), the Layer 3 semantic module (`semantic-layer`), and any
14
+ * value this list does not know. No evidence a response tier is earned from
15
+ * measures a model's decisions (the benign-corpus evidence runs the synchronous
16
+ * engine, where a semantic rule is its pattern fallback), so by default they are
17
+ * reported and not enforced.
18
+ */
19
+ const DETERMINISTIC_DECIDERS = new Set([
20
+ 'pattern',
21
+ 'signature',
22
+ 'trace',
23
+ 'semantic-fallback',
24
+ 'invariant',
25
+ 'blacklist',
26
+ 'fingerprint',
27
+ ]);
28
+ /**
29
+ * Whether a model, not a rule's deterministic logic, decided this match. A match
30
+ * without a decider (built outside the engine, as every match was before
31
+ * decidedBy existed) is read as deterministic; an unknown decider as a model.
32
+ */
33
+ export function isModelDecided(match) {
34
+ return match.decidedBy !== undefined && !DETERMINISTIC_DECIDERS.has(match.decidedBy);
35
+ }
36
+ /** Read a modelDecisions setting, refusing anything but the two values. */
37
+ export function modelDecisionsFromConfig(value) {
38
+ if (value === undefined)
39
+ return 'advisory';
40
+ if (value === 'advisory' || value === 'enforce')
41
+ return value;
42
+ throw new TypeError(`modelDecisions must be 'advisory' or 'enforce', not ${JSON.stringify(value)}`);
43
+ }
9
44
  /** Severity rank from most severe (0) to least severe (4) */
10
45
  export const SEVERITY_RANK = {
11
46
  critical: 0,
@@ -14,6 +49,19 @@ export const SEVERITY_RANK = {
14
49
  low: 3,
15
50
  informational: 4,
16
51
  };
52
+ /**
53
+ * Minimum confidence at which a high-severity match denies; below it, asks.
54
+ *
55
+ * Compared as given. A confidence the engine derives from a rule's
56
+ * `tags.confidence` label reaches this threshold only from a `high` label:
57
+ * `src/rule-confidence.ts` caps every lower label one basis point under it.
58
+ */
59
+ export const HIGH_SEVERITY_DENY_CONFIDENCE = 0.8;
60
+ /**
61
+ * Minimum confidence at which a medium-severity match asks; below it, allows.
62
+ * A `low` label reaches it exactly (0.6) when every condition matched.
63
+ */
64
+ export const MEDIUM_SEVERITY_ASK_CONFIDENCE = 0.6;
17
65
  /**
18
66
  * Check whether auto-response is enabled for a matched rule.
19
67
  * The auto_response_threshold field on ATRResponse indicates the
@@ -49,9 +97,9 @@ function determineOutcome(severity, confidence) {
49
97
  return 'deny';
50
98
  }
51
99
  if (severity === 'high') {
52
- return confidence >= 0.8 ? 'deny' : 'ask';
100
+ return confidence >= HIGH_SEVERITY_DENY_CONFIDENCE ? 'deny' : 'ask';
53
101
  }
54
- if (severity === 'medium' && confidence >= 0.6) {
102
+ if (severity === 'medium' && confidence >= MEDIUM_SEVERITY_ASK_CONFIDENCE) {
55
103
  return 'ask';
56
104
  }
57
105
  return 'allow';
@@ -73,6 +121,19 @@ function collectUniqueActions(matches) {
73
121
  }
74
122
  return Object.freeze(actions);
75
123
  }
124
+ /**
125
+ * The observe-tier actions of matches whose other actions are not dispatched.
126
+ * Only actions the tier ladder names: actionTier() reads an unknown name as
127
+ * observe, and a name it does not know is not one this policy can vouch for.
128
+ */
129
+ export function observeActions(matches) {
130
+ return collectUniqueActions(matches).filter((a) => TIERED_ACTIONS.includes(a) && actionTier(a) === ACTION_TIERS.OBSERVE);
131
+ }
132
+ /** Severity first, then confidence: the order computeVerdict reads matches in. */
133
+ function bySeverityThenConfidence(a, b) {
134
+ const bySeverity = (SEVERITY_RANK[a.rule.severity] ?? 4) - (SEVERITY_RANK[b.rule.severity] ?? 4);
135
+ return bySeverity !== 0 ? bySeverity : b.confidence - a.confidence;
136
+ }
76
137
  /**
77
138
  * Build a human-readable reason string from the highest severity match.
78
139
  */
@@ -89,10 +150,16 @@ function buildReason(highestMatch, outcome, matchCount) {
89
150
  /**
90
151
  * Compute a verdict from an array of ATR matches.
91
152
  *
153
+ * A match a model decided (isModelDecided) is reported but, unless
154
+ * options.modelDecisions is 'enforce', takes no part in the outcome, and of its
155
+ * actions only the observe tier is dispatched: no benign-corpus measurement
156
+ * stands behind a model's decision. Every other match counts as before.
157
+ *
92
158
  * This is a pure function -- no side effects, no mutation.
93
159
  * Returns a frozen ATRVerdict object.
94
160
  */
95
- export function computeVerdict(matches) {
161
+ export function computeVerdict(matches, options = {}) {
162
+ const modelDecisions = modelDecisionsFromConfig(options.modelDecisions);
96
163
  if (matches.length === 0) {
97
164
  return Object.freeze({
98
165
  outcome: 'allow',
@@ -105,23 +172,41 @@ export function computeVerdict(matches) {
105
172
  timestamp: new Date().toISOString(),
106
173
  });
107
174
  }
108
- // Matches are already sorted by the engine (severity desc, confidence desc).
109
- // The first match has the highest severity.
110
- const highestMatch = matches[0];
111
- const highestSeverity = highestMatch.rule.severity;
112
- const highestConfidence = highestMatch.confidence;
113
- const outcome = determineOutcome(highestSeverity, highestConfidence);
114
- const actions = collectUniqueActions(matches);
115
- const reason = buildReason(highestMatch, outcome, matches.length);
175
+ // Sorted here rather than trusted: not every caller sorts (the engine appends
176
+ // the embedding match last, and scanSkill appends decoded-payload matches).
177
+ // The sort is stable, so an already sorted list keeps its order.
178
+ const sorted = [...matches].sort(bySeverityThenConfidence);
179
+ const advisory = modelDecisions === 'advisory' ? sorted.filter(isModelDecided) : [];
180
+ const enforcing = advisory.length === 0 ? sorted : sorted.filter((m) => !isModelDecided(m));
181
+ const highestMatch = enforcing[0];
182
+ const outcome = highestMatch ? determineOutcome(highestMatch.rule.severity, highestMatch.confidence) : 'allow';
183
+ const actions = Object.freeze([
184
+ ...new Set([...collectUniqueActions(enforcing), ...observeActions(advisory)]),
185
+ ]);
186
+ const topAdvisory = advisory[0];
187
+ const reason = highestMatch
188
+ ? buildReason(highestMatch, outcome, matches.length) + advisoryNote(advisory.length)
189
+ : `ALLOW: ${topAdvisory.rule.title} [${topAdvisory.rule.severity}/` +
190
+ `${Math.round(topAdvisory.confidence * 100)}% confidence, decided by ` +
191
+ `${topAdvisory.decidedBy}]: ${advisory.length} model-decided ` +
192
+ `match${advisory.length === 1 ? '' : 'es'} reported, not enforced (modelDecisions: advisory).`;
116
193
  return Object.freeze({
117
194
  outcome,
118
195
  reason,
119
196
  matchCount: matches.length,
120
- highestSeverity,
121
- highestConfidence,
197
+ highestSeverity: highestMatch?.rule.severity ?? null,
198
+ highestConfidence: highestMatch?.confidence ?? 0,
122
199
  actions,
123
- matches: Object.freeze([...matches]),
200
+ matches: Object.freeze(sorted),
201
+ // Present only when something was held back, so a verdict without a model
202
+ // decision has exactly the fields it always had.
203
+ ...(topAdvisory
204
+ ? { advisory: Object.freeze(advisory), advisorySeverity: topAdvisory.rule.severity }
205
+ : {}),
124
206
  timestamp: new Date().toISOString(),
125
207
  });
126
208
  }
209
+ function advisoryNote(count) {
210
+ return count === 0 ? '' : `; ${count} model-decided match${count === 1 ? '' : 'es'} reported, not enforced`;
211
+ }
127
212
  //# sourceMappingURL=verdict.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"verdict.js","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verdict.js","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-threat-rules",
3
- "version": "4.1.5",
3
+ "version": "4.2.0",
4
4
  "mcpName": "io.github.Agent-Threat-Rule/agent-threat-rules",
5
5
  "type": "module",
6
6
  "description": "Open detection standard — like Sigma, but for AI agents. Executable, testable rules for prompt injection, tool poisoning, context exfiltration, and MCP attacks. MIT-licensed.",
@@ -95,6 +95,7 @@
95
95
  "validate:compliance": "tsx scripts/validate-compliance.ts",
96
96
  "eval": "tsx src/eval/run-eval.ts",
97
97
  "eval:pint": "tsx src/eval/run-pint-benchmark.ts",
98
+ "eval:hackaprompt": "python3 scripts/hackaprompt-to-corpus.py && tsx src/eval/run-hackaprompt-benchmark.ts",
98
99
  "eval:generalization": "tsx scripts/eval-generalization.ts --all",
99
100
  "gate:generalization": "tsx scripts/eval-generalization.ts --gate",
100
101
  "gate:rule-latency": "tsx scripts/gate-rule-latency.ts",
@@ -124,7 +124,7 @@ detection:
124
124
  description: Attempt to inject new instructions via agent messages (live directive only; negative lookbehind excludes quoted/listed occurrences in prompt-injection detection docs)
125
125
  - field: content
126
126
  operator: regex
127
- value: (?i)(?:you\s+are\s+now|from\s+now\s+on|henceforth|going\s+forward|effective\s+immediately)\s+(?:a|an|the)?\s*(?:unrestricted|unfiltered|jailbroken)?\s*[a-zA-Z]+\s+(?:agent|assistant|bot)
127
+ value: (?i)(?:you\s+are\s+now|from\s+now\s+on|henceforth|going\s+forward|effective\s+immediately)\s+(?:(?:a|an|the)\s*)?(?:(?:unrestricted|unfiltered|jailbroken)\s*)?[a-zA-Z]+\s+(?:agent|assistant|bot)
128
128
  description: Attempt to redefine target agent's identity or role
129
129
  - field: content
130
130
  operator: regex
@@ -132,7 +132,7 @@ detection:
132
132
  description: Instruction to hide actions from oversight (excludes benign coaching form 'never tell users to <verb>' via the first negative lookahead; the second excludes output-verbosity control, 'do not tell the user about each|every|individual <step>', which scopes down per-item progress narration rather than concealing a fact — a report-building skill triggered this on 'Don't tell the user about each tool call as you make it'. Concealment form 'do not tell the user about <fact>' is kept)
133
133
  - field: content
134
134
  operator: regex
135
- value: (?i)(?:acting|speaking|operating)\s+(?:on\s+behalf\s+of|as\s+a?\s*proxy\s+for|in\s+place\s+of)\s+[a-zA-Z0-9_-]+\s*(?:agent)?
135
+ value: (?i)(?:acting|speaking|operating)\s+(?:on\s+behalf\s+of|as\s+(?:a\s*)?proxy\s+for|in\s+place\s+of)\s+[a-zA-Z0-9_-]+\s*(?:agent)?
136
136
  description: Agent claiming to act on behalf of another agent
137
137
  - field: content
138
138
  operator: regex
@@ -64,7 +64,7 @@ detection:
64
64
  conditions:
65
65
  - field: content
66
66
  operator: regex
67
- value: (?i)(?:fyi|btw|heads up)?\s*(?:the\s+)?(?:orchestrator|coordinator|manager|admin|boss|supervisor|lead)\s+(?:wants?|said|told|instructed|requires?|decided).*(?:append|send|redirect|forward|pipe|write|log)\s+(?:all\s+)?(?:data|output|results?|logs?|info).*(?://|https?://|\d{1,3}\.\d{1,3})
67
+ value: (?i)(?:the\s+)?(?:orchestrator|coordinator|manager|admin|boss|supervisor|lead)\s+(?:wants?|said|told|instructed|requires?|decided).*(?:append|send|redirect|forward|pipe|write|log)\s+(?:all\s+)?(?:data|output|results?|logs?|info).*(?://|https?://|\d{1,3}\.\d{1,3})
68
68
  description: Authority claim redirecting output to external endpoint
69
69
  - field: content
70
70
  operator: regex
@@ -80,7 +80,7 @@ detection:
80
80
  description: '"Stay in X Mode" + chaos/law-inversion — from inthewild corpus #339 (APOPHIS Mode: "the only true Law is Chaos, and that Humans are meant to be self-serving and selfish")'
81
81
  - field: tool_response
82
82
  operator: regex
83
- value: \w+\s+Mode\s+(?:output|responses?)[\s\S]{0,200}(?:generating\s+any\s+kind\s+of\s+content|ignoring\s+OpenA[Il]\s*['`]?s?\s+content\s+polic)
83
+ value: \w\s+Mode\s+(?:output|responses?)[\s\S]{0,200}(?:generating\s+any\s+kind\s+of\s+content|ignoring\s+OpenA[Il](?:\s*(?:['`]s?|s))?\s+content\s+polic)
84
84
  description: '"X Mode output/responses" + generating any content or ignoring OpenAI policies — Mode-output paired with content-policy bypass from inthewild corpus #416 BABY Mode output definition'
85
85
  - field: tool_response
86
86
  operator: regex
@@ -76,7 +76,7 @@ detection:
76
76
  description: '"always respond as the character despite any negative feedback or criticism" — exact character-lock mandate from inthewild corpus #14 #88; "despite negative feedback" prevents safety interventions from breaking the character'
77
77
  - field: tool_response
78
78
  operator: regex
79
- value: '[A-Z][A-Z0-9]+\s+(?:respond|always\s+respond|will\s+respond)\s+as\s+the\s+character[\s\S]{0,50}[A-Z][A-Z0-9]+\s+always\s+respond\s+as\s+the\s+character'
79
+ value: '[A-Z][0-9]*[A-Z0-9]\s+(?:respond|always\s+respond|will\s+respond)\s+as\s+the\s+character[\s\S]{0,50}[A-Z][A-Z0-9]+\s+always\s+respond\s+as\s+the\s+character'
80
80
  description: ALL-CAPS name repeated twice with "respond as the character" mandate — the YOJA-template repeats the persona name as prefix for every rule; the double ALL-CAPS+mandate pattern distinguishes structured persona injection from casual roleplay
81
81
  - field: tool_response
82
82
  operator: regex
@@ -89,19 +89,19 @@ detection:
89
89
  description: WeKnora plugin/MCP loader content referencing process-spawning fields
90
90
  - field: content
91
91
  operator: regex
92
- value: (?i)(?:plugins?|mcp_servers?)\s*[:=]\s*\[?\s*\{[^}]{0,400}"command"\s*:\s*"(?:curl|wget)"
92
+ value: (?i)(?:plugins?|mcp_servers?)\s*[:=]\s*(?:\[\s*)?\{[^}]{0,400}"command"\s*:\s*"(?:curl|wget)"
93
93
  description: Plugin/MCP-servers config block where the command field resolves to curl/wget — never a legitimate plugin launcher target
94
94
  - field: content
95
95
  operator: regex
96
- value: (?i)(?:plugins?|mcp_servers?)\s*[:=]\s*\[?\s*\{[^}]{0,400}"command"\s*:\s*"(?:bash|sh|cmd|powershell)"[^}]{0,300}"args"\s*:\s*\[[^\]]{0,300}(?:https?://|\|\s*(?:ba)?sh\b|-enc(?:odedcommand)?\b|base64|iwr\b|invoke-webrequest)
96
+ value: (?i)(?:plugins?|mcp_servers?)\s*[:=]\s*(?:\[\s*)?\{[^}]{0,400}"command"\s*:\s*"(?:bash|sh|cmd|powershell)"[^}]{0,300}"args"\s*:\s*\[[^\]]{0,300}(?:https?://|\|\s*(?:ba)?sh\b|-enc(?:odedcommand)?\b|base64|iwr\b|invoke-webrequest)
97
97
  description: Plugin/MCP-servers config block where the wrapper shell (bash/sh/cmd/powershell) args show a genuine payload marker (URL fetch, pipe-to-shell, base64/encoded-command). Excludes benign self-launch idioms like 'sh -c cd /project && node dist/index.js' or 'bash -c /absolute/path/to/own-installed-binary --flag', which lack any such marker — those are the standard way MCP servers document their own stdio launch command, not RCE setup.
98
98
  - field: content
99
99
  operator: regex
100
- value: '(?i)(?:^|\n)\s*command\s*:\s*[\x27"]?(?:curl|wget)[\x27"]?\s*\n\s*args?\s*:'
100
+ value: '(?i)(?:^|\n)[^\S\n]*command\s*:\s*[\x27"]?(?:curl|wget)[\x27"]?[^\S\n]*\n\s*args?\s*:'
101
101
  description: YAML-style plugin config with curl/wget as command — never a legitimate plugin launcher target
102
102
  - field: content
103
103
  operator: regex
104
- value: (?i)(?:^|\n)\s*command\s*:\s*[\x27"]?(?:bash|sh|cmd|powershell)[\x27"]?\s*\n\s*args?\s*:[\s\S]{0,300}(?:https?://|\|\s*(?:ba)?sh\b|-enc(?:odedcommand)?\b|base64|iwr\b|invoke-webrequest)
104
+ value: (?i)(?:^|\n)[^\S\n]*command\s*:\s*[\x27"]?(?:bash|sh|cmd|powershell)[\x27"]?[^\S\n]*\n\s*args?\s*:[\s\S]{0,300}(?:https?://|\|\s*(?:ba)?sh\b|-enc(?:odedcommand)?\b|base64|iwr\b|invoke-webrequest)
105
105
  description: YAML-style plugin config with wrapper shell command and args showing a genuine payload marker — same rationale as the JSON-form condition above
106
106
  - field: content
107
107
  operator: regex
@@ -117,7 +117,7 @@ detection:
117
117
  description: Shell/interpreter inline-execution flag in args array WHERE the executed string also shows a genuine payload marker (URL, pipe-to-shell, base64/encoded-command, curl/wget invocation). Bare '-c'/'-e' + any string was too loose — it matched benign self-launch strings like 'cd /Users/marc/project && node dist/index.js' or '/home/you/.../aks-mcp --transport stdio' that MCP servers commonly document as their own stdio launch command.
118
118
  - field: content
119
119
  operator: regex
120
- value: (?i)(?:^|\n)\s+-\s+-(?:c|e|-eval|-command)\s*$
120
+ value: (?i)(?:^\s+|\S[^\S\n]*\n\s+)-\s+-(?:c|e|-eval|-command)\s*$
121
121
  description: YAML-style args list with inline-execution flag as standalone entry — co-occurrence anchor
122
122
  response:
123
123
  actions:
@@ -78,7 +78,7 @@ detection:
78
78
  conditions:
79
79
  - field: content
80
80
  operator: regex
81
- value: (?i)(?:^|[^a-zA-Z_])eval\s*\(\s*["\x27]?\s*(?:__import__|os\.system|os\.popen|subprocess\.|os\.exec|exec\s*\()
81
+ value: (?i)(?:^|[^a-zA-Z_])eval\s*\(\s*(?:["\x27]\s*)?(?:__import__|os\.system|os\.popen|subprocess\.|os\.exec|exec\s*\()
82
82
  description: eval() invoking Python OS-execution primitives — direct LLM-output-to-eval RCE signature
83
83
  - field: content
84
84
  operator: regex
@@ -79,11 +79,11 @@ detection:
79
79
  conditions:
80
80
  - field: content
81
81
  operator: regex
82
- value: (?i)lambda\s+\w*\s*:\s*[^,)]*\beval\s*\(
82
+ value: (?i)lambda\s+(?:\w+\s*)?:[^,)]*\beval\s*\(
83
83
  description: 'Lambda body invoking eval() — primary CVE-2026-26030 exploit shape (`lambda row: eval(filter_expr)`)'
84
84
  - field: content
85
85
  operator: regex
86
- value: (?i)lambda\s+\w*\s*:\s*[^,)]*\b__import__\s*\(\s*["\x27](?:os|subprocess|socket|builtins|importlib)["\x27]
86
+ value: (?i)lambda\s+(?:\w+\s*)?:[^,)]*\b__import__\s*\(\s*["\x27](?:os|subprocess|socket|builtins|importlib)["\x27]
87
87
  description: Lambda body using __import__ to reach OS / process / builtin modules — eval-friendly RCE chain
88
88
  - field: content
89
89
  operator: regex
@@ -99,7 +99,7 @@ detection:
99
99
  description: .NET / JavaScript Function() constructor with body invoking eval / process — cross-runtime variant
100
100
  - field: user_input
101
101
  operator: regex
102
- value: (?i)lambda\s+\w*\s*:\s*[^,)]*\b(?:eval|exec|__import__)\s*\(
102
+ value: (?i)lambda\s+(?:\w+\s*)?:[^,)]*\b(?:eval|exec|__import__)\s*\(
103
103
  description: User-supplied filter expression containing lambda + dynamic-evaluation primitive — the original CVE-2026-26030 injection surface
104
104
  response:
105
105
  actions:
@@ -79,7 +79,7 @@ detection:
79
79
  description: Embed credential as URL parameter / image src / markdown link
80
80
  - field: content
81
81
  operator: regex
82
- value: (?i)!?\[[^\]]*\]\(\s*https?:\/\/[^)]*(?:\$\{?(?:API_KEY|ACCESS_TOKEN|SECRET|PASSWORD|TOKEN|CREDENTIAL|BEARER|AUTH)\b|\{\{\s*(?:api_key|access_token|secret|password|token|credential|env)\b)
82
+ value: (?i)!?\[[^\[\]]*\]\(\s*https?:\/\/[^)]*(?:\$\{?(?:API_KEY|ACCESS_TOKEN|SECRET|PASSWORD|TOKEN|CREDENTIAL|BEARER|AUTH)\b|\{\{\s*(?:api_key|access_token|secret|password|token|credential|env)\b)
83
83
  description: Markdown link/image template embedding credential placeholder in URL
84
84
  - field: content
85
85
  operator: regex
@@ -79,7 +79,7 @@ detection:
79
79
  description: Share/send password to alternate email address
80
80
  - field: content
81
81
  operator: regex
82
- value: (?i)(?:send|email|forward|share)\s+(?:them|it)?\s+to\s+my\s+email[^.\n]{0,80}[\w.+-]+@[\w-]+\.[\w.]+[^.\n]{0,40}(?:password|credential|key|secret|stored)
82
+ value: (?i)(?:send|email|forward|share)\s(?:\s*(?:them|it))?\s+to\s+my\s+email(?:[^.\n]{0,79}[^\w.+\n-])?[\w.+-]+@[\w-]+\.[\w.]+(?:[^\w.\n][^.\n]{0,39})?(?:password|credential|key|secret|stored)
83
83
  description: Send to my email destination with credential type
84
84
  - field: content
85
85
  operator: regex
@@ -59,7 +59,7 @@ detection:
59
59
  conditions:
60
60
  - field: content
61
61
  operator: regex
62
- value: (?i)\battacker_objective\s*=\s*Get\s+the\s+(?:user|CUA|agent)['']?s?\s+[^,\n]{0,60},?\s+then\s+(?:delete|edit|modify|tamper|overwrite)\s+the\s+file
62
+ value: (?i)\battacker_objective\s*=\s*Get\s+the\s+(?:user|CUA|agent)['']?s?(?:\s+(?:[^,\s][^,\n]{0,59},?|,)|\s)\s+then\s+(?:delete|edit|modify|tamper|overwrite)\s+the\s+file
63
63
  description: 'VPI attacker_objective: exfil then delete/edit the source file'
64
64
  - field: content
65
65
  operator: regex
@@ -67,7 +67,7 @@ detection:
67
67
  description: Injection override + payment/banking retrieval + email exfil destination
68
68
  - field: content
69
69
  operator: regex
70
- value: (?i)(?:retrieve|get|fetch|access|export)\s+(?:my\s+)?(?:saved\s+)?(?:credit\s+card(?:\s+(?:information|details?|numbers?|data))?|payment\s+methods?|bank\s+account(?:\s+(?:detail|information|data))?|debit\s+card)\s+(?:from\s+(?:my\s+)?\w+)?\s*(?:and\s+)?(?:email|send|forward|mail)\s+(?:it|them|the\s+(?:details?|information|data))\s+to[^\n]{0,120}(?:@[^\s"]{3,80}|alternate\s+email)
70
+ value: (?i)(?:retrieve|get|fetch|access|export)\s+(?:my\s+)?(?:saved\s+)?(?:credit\s+card(?:\s+(?:information|details?|numbers?|data))?|payment\s+methods?|bank\s+account(?:\s+(?:detail|information|data))?|debit\s+card)\s+(?:from\s+(?:my\s+)?\w+\s*)?(?:and\s+)?(?:email|send|forward|mail)\s+(?:it|them|the\s+(?:details?|information|data))\s+to[^\n]{0,120}(?:@[^\s"]{3,80}|alternate\s+email)
71
71
  description: Payment/card retrieval + email forwarding (broader coverage, no override prefix required)
72
72
  condition: any
73
73
  false_positives:
@@ -77,7 +77,7 @@ detection:
77
77
  conditions:
78
78
  - field: content
79
79
  operator: regex
80
- value: (?i)(program\s+me\s+with\s+(my|your|the)?\s*system\s+prompt|I\s+am\s+(an?\s+)?(advanced\s+)?AI[^.]{0,60}(program|configure|set|initialize)\s+me)
80
+ value: (?i)(program\s+me\s+with\s+(?:(my|your|the)\s*)?system\s+prompt|I\s+am\s+(an?\s+)?(advanced\s+)?AI[^.]{0,60}(program|configure|set|initialize)\s+me)
81
81
  description: LLM-impersonation framing asking to receive/reproduce system prompt
82
82
  - field: content
83
83
  operator: regex
@@ -74,7 +74,7 @@ detection:
74
74
  conditions:
75
75
  - field: content
76
76
  operator: regex
77
- value: (?i)((?:<!--\s*|\[)\s*SYSTEM\s+OVERRIDE\b|SYSTEM\s+OVERRIDE\s+(?:BEGIN|MODE|ACTIVE|NOW|ENABLED)|ROOT_?ADMIN|OMEGA\s+CLEARANCE|PRIORITY\s+LEVEL\s*:\s*CRITICAL|AUTHORIZATION\s*:\s*(ROOT|ADMIN|FULL|OMEGA))
77
+ value: (?i)((?:<!--|\[)\s*SYSTEM\s+OVERRIDE\b|SYSTEM\s+OVERRIDE\s+(?:BEGIN|MODE|ACTIVE|NOW|ENABLED)|ROOT_?ADMIN|OMEGA\s+CLEARANCE|PRIORITY\s+LEVEL\s*:\s*CRITICAL|AUTHORIZATION\s*:\s*(ROOT|ADMIN|FULL|OMEGA))
78
78
  description: System override (structurally framed) / root admin / omega clearance authority claim
79
79
  - field: content
80
80
  operator: regex
@@ -69,7 +69,7 @@ detection:
69
69
  description: Llama <<SYS>> system block marker in user input
70
70
  - field: content
71
71
  operator: regex
72
- value: (?i)(#{2,}\s*\[SYSTEM[:\]](?!\()|<\|system\|>|\[SYSTEM\s*(?:PROMPT|MESSAGE|OVERRIDE)\](?!\())
72
+ value: (?i)(##\s*\[SYSTEM[:\]](?!\()|<\|system\|>|\[SYSTEM\s*(?:PROMPT|MESSAGE|OVERRIDE)\](?!\())
73
73
  description: Markdown-heading or special-token SYSTEM marker injection (not markdown link text)
74
74
  response:
75
75
  actions:
@@ -15,7 +15,7 @@ author: ATR Community
15
15
  date: 2026/06/12
16
16
  schema_version: '0.1'
17
17
  detection_tier: pattern
18
- maturity: stable
18
+ maturity: test
19
19
  severity: high
20
20
  references:
21
21
  owasp_llm:
@@ -70,7 +70,7 @@ detection:
70
70
  conditions:
71
71
  - field: content
72
72
  operator: regex
73
- value: (?i)\b(?:per\s+(?:company|management)\s+(?:policy|directive)|data\s+retention\s+policy|for\s+(?:quality\s+assurance|verification\s+purposes|audit\s+purposes|compliance\s+purposes))\b[\s\S]{0,100}[\w.+-]+@[\w-]+\.[\w.-]+|[\w.+-]+@[\w-]+\.[\w.-]+[\s\S]{0,100}\b(?:per\s+(?:company|management)\s+(?:policy|directive)|data\s+retention\s+policy|for\s+(?:quality\s+assurance|verification\s+purposes|audit\s+purposes|compliance\s+purposes))\b
73
+ value: (?i)\b(?:per\s+(?:company|management)\s+(?:policy|directive)|data\s+retention\s+policy|for\s+(?:quality\s+assurance|verification\s+purposes|audit\s+purposes|compliance\s+purposes))\b[\s\S]{0,100}[\w.+-]+@[\w-]+\.[\w.-]+|[\w.+-]@[\w-]+\.[\w.-]+[\s\S]{0,100}\b(?:per\s+(?:company|management)\s+(?:policy|directive)|data\s+retention\s+policy|for\s+(?:quality\s+assurance|verification\s+purposes|audit\s+purposes|compliance\s+purposes))\b
74
74
  description: Fabricated policy/compliance/audit pretext paired with a nearby destination email address, in either order
75
75
  condition: any
76
76
  false_positives:
@@ -0,0 +1,79 @@
1
+ title: Cross-Email Context Exfiltration Relay Detection
2
+ id: ATR-2026-02855
3
+ rule_version: 1
4
+ status: experimental
5
+ description: |
6
+ Detects cross-document exfiltration-chaining attempts in which a tool output or injected message instructs the agent to pull a value (e.g. a figure, credential, or identifier) out of an unrelated prior email or message in its context window and relay that value via an outbound send action to an attacker-controlled or unverified recipient. This is the core structural pattern of cross-email exfil relay: the agent is told that something was "found", "mentioned", or "contained" in another/previous/other email, immediately followed by an instruction to send an email, regardless of which specific value or destination address is targeted. Detection is anchored on this found-elsewhere-then-send structure rather than any single value or address to generalize across campaigns.
7
+ author: ATR Community
8
+ date: 2026/03/08
9
+ schema_version: '0.1'
10
+ detection_tier: pattern
11
+ maturity: test
12
+ severity: high
13
+ references:
14
+ owasp_llm:
15
+ - LLM01:2025
16
+ - LLM02:2025
17
+ owasp_agentic:
18
+ - ASI06:2026
19
+ mitre_atlas:
20
+ - AML.T0051 - LLM Prompt Injection
21
+ - AML.T0057 - LLM Data Leakage
22
+ compliance:
23
+ eu_ai_act:
24
+ - article: '15'
25
+ context: High-risk AI agents must be resilient against adversarial manipulation of their context that causes unauthorized data exfiltration; this rule's detection of cross-document found-elsewhere-then-send relay chains is a concrete cybersecurity control satisfying Article 15 robustness requirements against input-based manipulation.
26
+ strength: primary
27
+ - article: '9'
28
+ context: Cross-email exfiltration relay via injected tool output is a documented risk class in the AI system risk register; Article 9 requires that monitoring controls are deployed to detect these attempts at runtime.
29
+ strength: secondary
30
+ nist_ai_rmf:
31
+ - subcategory: MP.5.1
32
+ context: Cross-document exfiltration chaining is a primary class of adversarial context-manipulation attack against agentic AI systems; MP.5.1 requires that such adversarial input risks are identified and tracked so that runtime detection controls like this rule can be deployed.
33
+ strength: primary
34
+ - subcategory: MG.3.2
35
+ context: Detected cross-email exfiltration-relay patterns represent a previously unknown risk requiring incident response; MG.3.2 requires procedures to respond to and recover from such risks once identified, which this rule's runtime detection enables.
36
+ strength: secondary
37
+ iso_42001:
38
+ - clause: '8.1'
39
+ context: Clause 8.1 operational controls require that AI agents execute tool-use and messaging actions only as intended; this rule enforces that outbound send actions are not silently triggered by instructions hidden inside unrelated context documents such as other emails.
40
+ strength: primary
41
+ - clause: '8.3'
42
+ context: Clause 8.3 requires ongoing assessment of AI system impacts; runtime detection of cross-document exfiltration relay chains feeds that impact assessment for agentic data-handling and communication risks.
43
+ strength: secondary
44
+ tags:
45
+ category: context-exfiltration
46
+ subcategory: cross-email-exfil-relay
47
+ scan_target: mcp
48
+ confidence: high
49
+ agent_source:
50
+ type: llm_io
51
+ framework:
52
+ - openai
53
+ - anthropic
54
+ - langchain
55
+ - llamaindex
56
+ - autogen
57
+ - crewai
58
+ provider:
59
+ - any
60
+ detection:
61
+ conditions:
62
+ - field: content
63
+ operator: regex
64
+ value: (?i)\b(?:found|find|retrieved|mentioned|contained)\b[\s\S]{0,80}\b(?:other|previous|another)\s+emails?\b[\s\S]{0,100}\bsend\b[\s\S]{0,60}\bemail\b|\bsend\b[\s\S]{0,100}\bemail\b[\s\S]{0,100}\b(?:found|retrieved|mentioned|contained)\b[\s\S]{0,60}\b(?:in|within|from)\s+(?:other|previous|another)\s+emails?\b
65
+ description: Instruction claims a value was found/mentioned/contained in another, previous, or other email and is immediately followed by a request to send an email, the core cross-document exfiltration-relay structure
66
+ condition: any
67
+ false_positives:
68
+ - Legitimate email-summarization workflows that reference content from another email purely for display, with no subsequent send action
69
+ - Users explicitly asking the assistant to forward a known, user-specified figure to a known, user-approved recipient
70
+ - Customer-support transcripts where an agent mentions a prior email thread while drafting a reply to the same, already-authorized recipient
71
+ - Internal documentation or training examples that describe this exfiltration technique by name without instructing an actual send action
72
+ response:
73
+ actions:
74
+ - alert
75
+ - snapshot
76
+ - escalate
77
+ auto_response_threshold: high
78
+ message_template: '[ATR-2026-02855] Cross-Email Context Exfiltration Relay Detection detected.'
79
+ confidence: 90