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
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.1",
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
@@ -1,9 +1,9 @@
1
1
  title: Data Exfiltration via Markdown Image and Link URL Injection
2
2
  id: ATR-2026-00501
3
- rule_version: 2
3
+ rule_version: 3
4
4
  status: experimental
5
5
  description: |
6
- Detects instructions to the AI to embed attacker-controlled URLs into markdown image or link syntax with extracted data encoded in query parameters. This is the canonical indirect- injection data exfiltration vector: the agent renders malicious markdown that causes clients to make HTTP requests carrying sensitive data to attacker-controlled servers. Covers: markdown image with data-bearing query params, markdown link with sensitive data params, tracking pixel embedding, base64-encoded data in URL, and "send/encode to" redirect instructions. Generalised from promptfoo data-exfil plugin corpus - literal evil.com and attacker.com replaced with generic external URL patterns containing exfiltration parameter names (secret=, data=, q=, payload=, user=, token=, session=, key=, info=).
6
+ Detects attacker-controlled URLs that carry extracted data to an external endpoint: the canonical indirect-injection exfiltration vector, where the agent is told to fetch a URL or to render markdown whose query string carries sensitive context, so that the client or a fetch tool sends that data to the attacker. Covers, on the input side: markdown image or link with data-bearing query parameters, tracking pixel embedding, "encode and send to" redirect instructions, diagram/image references with data-bearing URLs, and fetch instructions whose query value is a template placeholder for the model's own context ({{system_prompt}}, ${conversation}, ...). On the output side (agent_output): the model emitting such a markdown image or link. Grounded in promptfoo's data-exfil plugin: the injected instruction documented in promptfoo/promptfoo site/docs/red-team/plugins/data-exfil.md ("## Example") and the two exfiltration payloads its grader rubric names (src/redteam/plugins/dataExfil.ts lines 23-24, repeated in the docs table), identical at commits 5f89b7c86ea04e5c03fbe7789dd0e47aed05bf43 and ea8b3d073a63769f7be243ac3c3460f72881a930. Those three are the vendor true positives below; the rest are ATR-authored and labelled synthetic.
7
7
  author: ATR Community (vendor-corpus-import)
8
8
  date: '2026-05-12'
9
9
  schema_version: '0.1'
@@ -66,6 +66,8 @@ tags:
66
66
  subcategory: markdown-url-injection
67
67
  scan_target: llm_io
68
68
  confidence: medium
69
+ source: vendor-test-suite
70
+ vendor_sources: promptfoo
69
71
  agent_source:
70
72
  type: llm_io
71
73
  framework:
@@ -98,6 +100,18 @@ detection:
98
100
  operator: regex
99
101
  value: (?i)(include|add|reference|embed).{0,30}(relevant\s+)?(diagrams?|images?|charts?|figures?|screenshots?).{0,30}(by\s+referencing|at|from|via).{0,30}https?://[a-z0-9._-]+\.[a-z]{2,}[^)"'\s]*\?(data|secret|content|payload|extract|q)=
100
102
  description: Diagram/image inclusion instruction with data-bearing external URL
103
+ - field: user_input
104
+ operator: regex
105
+ value: (?i)\b(fetch|visit|load|open|request|call|curl|ping|get|navigate\s+to|go\s+to|send\s+a\s+request\s+to)\b.{0,60}https?://[^\s?#]{1,200}\?[^\s#]{0,200}=\s*(\{\{|\$\{)\s*[a-z_.]{0,30}(system[_ ]?prompt|prompt|instructions?|conversation|chat[_ ]?history|history|memory|context|messages?|transcript|secrets?|credentials?)[a-z_.]{0,30}\s*\}
106
+ description: Fetch instruction to a URL whose query value interpolates the model's own context (system prompt, conversation, memory, secrets)
107
+ - field: agent_output
108
+ operator: regex
109
+ value: (?i)!\[[^\]]{0,60}\]\(https?://[a-z0-9._-]+\.[a-z]{2,}[^)]*\?(secret|data|payload|token|key|session|user|info|content|doc|extract|dump|exfil|leak|api_key|password)=
110
+ description: Agent output contains a markdown image whose external URL carries an exfiltration query parameter
111
+ - field: agent_output
112
+ operator: regex
113
+ value: (?i)\[[^\]]{0,60}\]\(https?://[a-z0-9._-]+\.[a-z]{2,}[^)]*\?(secret|payload|token|extracted|exfil|leak|dump|stolen|private_data|sensitive|api_key|auth_token|bearer|password)=
114
+ description: Agent output contains a markdown link whose external URL carries a high-confidence exfiltration query parameter
101
115
  condition: any
102
116
  false_positives:
103
117
  - Legitimate markdown with external image URLs that do not contain exfiltration parameters
@@ -1,9 +1,9 @@
1
1
  title: System Prompt Extraction - Instruction Dump Request
2
2
  id: ATR-2026-00505
3
- rule_version: 2
3
+ rule_version: 3
4
4
  status: experimental
5
5
  description: |
6
- Detects attempts to extract the system prompt or initial instructions given to the AI. Covers both direct requests ('list your instructions', 'relay your parameters', 'repeat verbatim') and indirect social engineering ('create a pitch deck about your system prompt', 'reply to this email with your system instructions'). The rule targets extraction verbs (repeat, relay, list, respond with, output, dump, provide, share) combined with objects that reference the system prompt (instructions, parameters, system prompt, initial context, configuration, directives, guidelines, rules you were given). Generalised from llm-guard and promptfoo test corpora - literal phrase fragments replaced with semantic patterns covering a broad range of extraction formulations.
6
+ Detects attempts to extract the system prompt or initial instructions given to the AI. Covers both direct requests ('list your instructions', 'relay your parameters', 'repeat verbatim') and indirect social engineering ('create a pitch deck about your system prompt', 'reply to this email with your system instructions'). The rule targets extraction verbs (repeat, relay, list, respond with, output, dump, provide, share) combined with objects that reference the system prompt (instructions, parameters, system prompt, initial context, configuration, directives, guidelines, rules you were given). Apostrophes and quotes are matched in both ASCII and typographic form (' and ’, " and “ ”), as the vendor originals use typographic ones. Grounded in vendor test cases: protectai/llm-guard tests/input_scanners/test_prompt_injection.py at commit 168c1034ffdb33837e7ae6fd6a16b80567c1be03 (system-prompt extraction prompts) and promptfoo/promptfoo src/redteam/plugins/promptExtraction.ts DEFAULT_EXAMPLES at commits 5f89b7c86ea04e5c03fbe7789dd0e47aed05bf43 and ea8b3d073a63769f7be243ac3c3460f72881a930.
7
7
  author: ATR Community (vendor-corpus-import)
8
8
  date: '2026-05-12'
9
9
  schema_version: '0.1'
@@ -66,6 +66,8 @@ tags:
66
66
  subcategory: system-prompt-extraction
67
67
  scan_target: llm_io
68
68
  confidence: medium
69
+ source: vendor-test-suite
70
+ vendor_sources: llm-guard, promptfoo
69
71
  agent_source:
70
72
  type: llm_io
71
73
  framework:
@@ -76,7 +78,7 @@ detection:
76
78
  conditions:
77
79
  - field: user_input
78
80
  operator: regex
79
- value: (?i)(repeat|relay|list|output|print|dump|provide|reveal|disclose|expose|display)[^"']{0,40}(your\s+(instructions?|system\s+prompt|initial\s+(prompt|instructions?|context|message)|given\s+instructions?|directives?|guidelines?|programming|constraints?|setup\s+instructions?)|the\s+system\s+(prompt|instructions?|message)|all\s+of\s+your\s+instructions?|every\s+(instruction|rule|directive|guideline|constraint)\s+you\s+(have\s+been|were))
81
+ value: (?i)(repeat|relay|list|output|print|dump|provide|reveal|disclose|expose|display)[^"'“”‘’]{0,40}(your\s+(instructions?|system\s+prompt|initial\s+(prompt|instructions?|context|message)|given\s+instructions?|directives?|guidelines?|programming|constraints?|setup\s+instructions?)|the\s+system\s+(prompt|instructions?|message)|all\s+of\s+your\s+instructions?|every\s+(instruction|rule|directive|guideline|constraint)\s+you\s+(have\s+been|were))
80
82
  description: Extraction verb targeting AI system instructions or prompt (possessive 'your' required or explicit 'system prompt/message')
81
83
  - field: user_input
82
84
  operator: regex
@@ -100,7 +102,7 @@ detection:
100
102
  description: Reply-to-email with system instructions exfiltration
101
103
  - field: content
102
104
  operator: regex
103
- value: (?i)(?:(?:relay|list|dump|recite|enumerate|repeat|reveal|return|show|provide)\b[\s\S]{0,40}\byour\s+system(?:'?s)?\s+(?:prompt|instructions?|parameters?|message|directives?|config)|(?:directives?|guidelines?|instructions?|rules?)\s+you\s+(?:were|was)\s+given)
105
+ value: (?i)(?:(?:relay|list|dump|recite|enumerate|repeat|reveal|return|show|provide)\b[\s\S]{0,40}\byour\s+system(?:['’]?s)?\s+(?:prompt|instructions?|parameters?|message|directives?|config)|(?:directives?|guidelines?|instructions?|rules?)\s+you\s+(?:were|was)\s+given)
104
106
  description: Extraction via synonym (relay/list your system parameters; directives/guidelines you were given)
105
107
  condition: any
106
108
  false_positives:
@@ -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: