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.
- package/CHANGELOG.md +688 -1
- package/README.md +355 -125
- package/dist/adapters/openshell-filter.d.ts +10 -3
- package/dist/adapters/openshell-filter.d.ts.map +1 -1
- package/dist/adapters/openshell-filter.js +10 -3
- package/dist/adapters/openshell-filter.js.map +1 -1
- package/dist/adapters/stdio-adapter.js +3 -3
- package/dist/adapters/stdio-adapter.js.map +1 -1
- package/dist/cli/mcp-tools-document.d.ts +40 -0
- package/dist/cli/mcp-tools-document.d.ts.map +1 -0
- package/dist/cli/mcp-tools-document.js +95 -0
- package/dist/cli/mcp-tools-document.js.map +1 -0
- package/dist/cli/scan-handler.d.ts +8 -0
- package/dist/cli/scan-handler.d.ts.map +1 -1
- package/dist/cli/scan-handler.js +126 -34
- package/dist/cli/scan-handler.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +80 -43
- package/dist/cli.js.map +1 -1
- package/dist/converters/sarif.d.ts +14 -2
- package/dist/converters/sarif.d.ts.map +1 -1
- package/dist/converters/sarif.js +7 -9
- package/dist/converters/sarif.js.map +1 -1
- package/dist/corpus-event.d.ts +47 -5
- package/dist/corpus-event.d.ts.map +1 -1
- package/dist/corpus-event.js +68 -29
- package/dist/corpus-event.js.map +1 -1
- package/dist/embedding/model-loader.d.ts +2 -0
- package/dist/embedding/model-loader.d.ts.map +1 -1
- package/dist/embedding/model-loader.js +3 -1
- package/dist/embedding/model-loader.js.map +1 -1
- package/dist/enforcement.d.ts +8 -0
- package/dist/enforcement.d.ts.map +1 -1
- package/dist/enforcement.js +10 -0
- package/dist/enforcement.js.map +1 -1
- package/dist/engine.d.ts +150 -6
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +337 -76
- package/dist/engine.js.map +1 -1
- package/dist/eval/embedding-provenance.d.ts +40 -0
- package/dist/eval/embedding-provenance.d.ts.map +1 -0
- package/dist/eval/embedding-provenance.js +71 -0
- package/dist/eval/embedding-provenance.js.map +1 -0
- package/dist/eval/eval-harness.d.ts +34 -0
- package/dist/eval/eval-harness.d.ts.map +1 -1
- package/dist/eval/eval-harness.js +49 -2
- package/dist/eval/eval-harness.js.map +1 -1
- package/dist/eval/hackaprompt-corpus.d.ts +12 -0
- package/dist/eval/hackaprompt-corpus.d.ts.map +1 -1
- package/dist/eval/hackaprompt-corpus.js +15 -1
- package/dist/eval/hackaprompt-corpus.js.map +1 -1
- package/dist/eval/hackaprompt-provenance.d.ts +52 -0
- package/dist/eval/hackaprompt-provenance.d.ts.map +1 -0
- package/dist/eval/hackaprompt-provenance.js +108 -0
- package/dist/eval/hackaprompt-provenance.js.map +1 -0
- package/dist/eval/run-hackaprompt-benchmark.d.ts +18 -8
- package/dist/eval/run-hackaprompt-benchmark.d.ts.map +1 -1
- package/dist/eval/run-hackaprompt-benchmark.js +154 -64
- package/dist/eval/run-hackaprompt-benchmark.js.map +1 -1
- package/dist/eval/run-pint-benchmark.js +11 -1
- package/dist/eval/run-pint-benchmark.js.map +1 -1
- package/dist/eval/skill-benchmark.d.ts +26 -6
- package/dist/eval/skill-benchmark.d.ts.map +1 -1
- package/dist/eval/skill-benchmark.js +49 -16
- package/dist/eval/skill-benchmark.js.map +1 -1
- package/dist/flywheel.d.ts +4 -0
- package/dist/flywheel.d.ts.map +1 -1
- package/dist/flywheel.js +12 -4
- package/dist/flywheel.js.map +1 -1
- package/dist/hook-handler.d.ts +11 -1
- package/dist/hook-handler.d.ts.map +1 -1
- package/dist/hook-handler.js +84 -16
- package/dist/hook-handler.js.map +1 -1
- package/dist/hook-tool-output.d.ts +47 -0
- package/dist/hook-tool-output.d.ts.map +1 -0
- package/dist/hook-tool-output.js +110 -0
- package/dist/hook-tool-output.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/layer-integration.d.ts +4 -2
- package/dist/layer-integration.d.ts.map +1 -1
- package/dist/layer-integration.js +6 -2
- package/dist/layer-integration.js.map +1 -1
- package/dist/mcp-server.d.ts +8 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +1 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/scan.d.ts.map +1 -1
- package/dist/mcp-tools/scan.js +9 -1
- package/dist/mcp-tools/scan.js.map +1 -1
- package/dist/measurement/enforce-lane.d.ts +66 -0
- package/dist/measurement/enforce-lane.d.ts.map +1 -0
- package/dist/measurement/enforce-lane.js +89 -0
- package/dist/measurement/enforce-lane.js.map +1 -0
- package/dist/measurement/from-eval-harness.d.ts +16 -3
- package/dist/measurement/from-eval-harness.d.ts.map +1 -1
- package/dist/measurement/from-eval-harness.js +15 -5
- package/dist/measurement/from-eval-harness.js.map +1 -1
- package/dist/measurement/in-sample-ablation.d.ts +29 -0
- package/dist/measurement/in-sample-ablation.d.ts.map +1 -0
- package/dist/measurement/in-sample-ablation.js +45 -0
- package/dist/measurement/in-sample-ablation.js.map +1 -0
- package/dist/measurement/schema.d.ts +38 -1
- package/dist/measurement/schema.d.ts.map +1 -1
- package/dist/measurement/schema.js +43 -1
- package/dist/measurement/schema.js.map +1 -1
- package/dist/quality/action-eligibility.d.ts +4 -2
- package/dist/quality/action-eligibility.d.ts.map +1 -1
- package/dist/quality/action-eligibility.js.map +1 -1
- package/dist/rule-confidence.d.ts +31 -0
- package/dist/rule-confidence.d.ts.map +1 -0
- package/dist/rule-confidence.js +49 -0
- package/dist/rule-confidence.js.map +1 -0
- package/dist/rule-scaffolder.d.ts.map +1 -1
- package/dist/rule-scaffolder.js +43 -4
- package/dist/rule-scaffolder.js.map +1 -1
- package/dist/semantic-evaluator.d.ts.map +1 -1
- package/dist/semantic-evaluator.js +15 -13
- package/dist/semantic-evaluator.js.map +1 -1
- package/dist/shadow-evaluator.d.ts +7 -2
- package/dist/shadow-evaluator.d.ts.map +1 -1
- package/dist/shadow-evaluator.js +29 -25
- package/dist/shadow-evaluator.js.map +1 -1
- package/dist/testcase-event.d.ts +82 -1
- package/dist/testcase-event.d.ts.map +1 -1
- package/dist/testcase-event.js +135 -16
- package/dist/testcase-event.js.map +1 -1
- package/dist/tier0-invariant.d.ts.map +1 -1
- package/dist/tier0-invariant.js +1 -0
- package/dist/tier0-invariant.js.map +1 -1
- package/dist/tier1-blacklist.d.ts.map +1 -1
- package/dist/tier1-blacklist.js +1 -0
- package/dist/tier1-blacklist.js.map +1 -1
- package/dist/tool-definition-scope.d.ts +45 -0
- package/dist/tool-definition-scope.d.ts.map +1 -0
- package/dist/tool-definition-scope.js +68 -0
- package/dist/tool-definition-scope.js.map +1 -0
- package/dist/types.d.ts +42 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/verdict.d.ts +46 -2
- package/dist/verdict.d.ts.map +1 -1
- package/dist/verdict.js +99 -14
- package/dist/verdict.js.map +1 -1
- package/package.json +2 -1
- package/rules/agent-manipulation/ATR-2026-00030-cross-agent-attack.yaml +2 -2
- package/rules/agent-manipulation/ATR-2026-00139-casual-authority-redirect.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00328-named-mode-persona-override.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00330-yoja-allcaps-settings-block-persona.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00418-weknora-mcp-config-rce.yaml +5 -5
- package/rules/agent-manipulation/ATR-2026-00432-superagi-output-handler-eval-rce.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00440-semantic-kernel-vector-store-eval-rce.yaml +3 -3
- package/rules/context-exfiltration/ATR-2026-00426-nl-output-injection-credential-leak.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-00702-ipi-credential-exfil-via-agent-action.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-00707-ipi-file-exfil-with-destroy-or-tamper.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-00851-indirect-pi-financial-payment-exfil.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01457-sysprompt-completion-clone-attack.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01462-sysprompt-override-pseudo-admin.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01463-imend-imsystem-prompt-injection-format.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01606-ssrf-internal-network-scan.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-02847-policy-justified-exfil-to-email.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-02855-cross-email-exfil-relay.yaml +79 -0
- package/rules/context-exfiltration/ATR-2026-02861-exfil-send-email-with-body-directive.yaml +83 -0
- package/rules/context-exfiltration/ATR-2026-02866-idna-label-separator-in-fetch-url-host.yaml +104 -0
- package/rules/excessive-autonomy/ATR-2026-00710-ipi-physical-access-grant.yaml +1 -1
- package/rules/model-abuse/ATR-2026-01897-host-sabotage.yaml +2 -2
- package/rules/model-security/ATR-2026-00433-modelcache-torch-load-deserialization-rce.yaml +1 -1
- package/rules/privilege-escalation/ATR-2026-00436-enclave-vm-sandbox-escape-rce.yaml +1 -1
- package/rules/privilege-escalation/ATR-2026-01601-sql-injection-destructive-ddl.yaml +2 -2
- package/rules/privilege-escalation/ATR-2026-01612-debug-mode-privilege-escalation.yaml +1 -1
- package/rules/privilege-escalation/ATR-2026-02867-identifier-typed-tool-argument-dot-dot-path-segment.yaml +106 -0
- package/rules/prompt-injection/ATR-2026-00004-system-prompt-override.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00080-encoding-evasion.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00086-visual-spoofing.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00203-context-pollution-skill-description.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00228-structured-jailbreak.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00233-structured-jailbreak.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00256-base-n-encoding-jailbreak.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00264-latent-injection-translation.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00265-latent-injection-rag-document.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00282-perez-prompt-injection-hijack.yaml +4 -4
- package/rules/prompt-injection/ATR-2026-00321-hyphenated-system-instruction-injection.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00394-backspace-deletion-character-injection.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00446-variable-assignment-payload-injection.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00476-garak-web-injection-javascript-alert-1.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00481-garak-dan-mixedunassigned.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00489-garak-encoding-javascript-alert-1.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00508-escape-delimiter-wrapped-goal-hijacking-in-user-input-prompt.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00512-rules-file-backdoor-injection.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01005-many-shot-repetition-override.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-01006-payload-split-string-concat-injection.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-01015-evasion-refusal-suppression.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01016-evasion-response-priming-forced-opener.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01026-evasion-unicode-glitch-token-anomaly.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-01304-indirect-injection-carrier-important-override.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01759-dos-new-important-instructions-injection-in-document.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01865-llmail-fake-email-boundary-marker.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-02010-emoji-only-payload.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-02815-injected-halt-order-canned-reply-dos.yaml +80 -0
- package/rules/prompt-injection/ATR-2026-02820-tool-result-orders-agent-to-hide-its-own-action.yaml +106 -0
- package/rules/prompt-injection/ATR-2026-02858-context-suppression-directive.yaml +73 -0
- package/rules/prompt-injection/ATR-2026-02859-tool-output-agent-role-reassignment.yaml +71 -0
- package/rules/prompt-injection/ATR-2026-02860-forged-instruction-boundary-marker.yaml +70 -0
- package/rules/prompt-injection/ATR-2026-02862-fake-chat-template-turn-injection.yaml +79 -0
- package/rules/skill-compromise/ATR-2026-00060-skill-impersonation.yaml +1 -1
- package/rules/skill-compromise/ATR-2026-00063-skill-chain-attack.yaml +2 -2
- package/rules/skill-compromise/ATR-2026-00066-parameter-injection.yaml +2 -2
- package/rules/skill-compromise/ATR-2026-00149-skill-exfil-compound.yaml +6 -6
- package/rules/skill-compromise/ATR-2026-00223-reverse-shell-dropper.yaml +2 -2
- package/rules/skill-compromise/ATR-2026-01755-backdoor-pot-linguistic-trigger-phrase.yaml +1 -1
- package/rules/skill-compromise/ATR-2026-01756-backdoor-pot-symbol-emoticon-trigger.yaml +1 -1
- package/rules/skill-compromise/ATR-2026-02261-code-credential-exfil-chain.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-00012-unauthorized-tool-call.yaml +23 -23
- package/rules/tool-poisoning/ATR-2026-00013-tool-ssrf.yaml +16 -5
- package/rules/tool-poisoning/ATR-2026-00161-important-tag-cross-tool-shadowing.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-00277-echo-template-command-injection.yaml +2 -2
- package/rules/tool-poisoning/ATR-2026-00494-garak-exploitation-mixedunassigned.yaml +4 -4
- package/rules/tool-poisoning/ATR-2026-00581-mcp-tool-rug-pull-post-approval-redefinition.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-01303-tool-schema-enumeration-social-engineering.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-01307-mcp-dns-rebinding-attack.yaml +2 -2
- package/rules/tool-poisoning/ATR-2026-01775-semantic-mcp-tool-manifest-poisoning.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-01932-shadow-undeclared-mcp-server-registration.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-02025-mcp-full-schema-poisoning-nondescription-field.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-02817-untrusted-content-dictates-tool-arguments.yaml +83 -0
- package/rules/tool-poisoning/ATR-2026-02864-non-canonical-ipv4-host-in-url-tool-argument.yaml +102 -0
- package/rules/tool-poisoning/ATR-2026-02865-read-only-utility-write-flag-in-command-argument.yaml +157 -0
- package/rules/tool-poisoning/ATR-2026-02868-ssrf-ipv6-transition-address-embedded-private-ipv4.yaml +107 -0
- package/spec/README.md +6 -3
- package/spec/atr-event-v1.0.md +13 -8
- package/spec/atr-method-v1.1.md +2 -2
- package/spec/category-registry/gemara/README.md +102 -0
- package/spec/category-registry/gemara/ai-agent-capability-catalog.yaml +157 -0
- package/spec/category-registry/gemara/atr-attack-vector-catalog.yaml +130 -0
- package/spec/category-registry/gemara/atr-detection-control-catalog.yaml +327 -0
- package/spec/category-registry/gemara/atr-threat-catalog.yaml +246 -0
- package/spec/category-registry/gemara/src/editorial.yaml +743 -0
- package/spec/mappings/README.md +1 -1
- package/spec/mappings/atr-to-nist-csf-2.0.md +5 -4
- 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
|
package/dist/verdict.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,QAAQ,
|
|
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 >=
|
|
100
|
+
return confidence >= HIGH_SEVERITY_DENY_CONFIDENCE ? 'deny' : 'ask';
|
|
53
101
|
}
|
|
54
|
-
if (severity === 'medium' && confidence >=
|
|
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
|
-
//
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
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(
|
|
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
|
package/dist/verdict.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verdict.js","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,aAAa,GAA0C;IAClE,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;IACN,aAAa,EAAE,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAe;IAEf,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IAC9D,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,YAAY,GAA2B;QAC3C,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,GAAG;KACT,CAAC;IAEF,MAAM,kBAAkB,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,kBAAkB,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEnD,OAAO,KAAK,CAAC,UAAU,IAAI,kBAAkB,CAAC;AAChD,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CACvB,QAAqB,EACrB,UAAkB;IAElB,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,UAAU,IAAI,
|
|
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.
|
|
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)
|
|
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
|
|
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)(?:
|
|
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
|
|
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]
|
|
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
|
|
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
|
|
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)\
|
|
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)\
|
|
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)(
|
|
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]
|
|
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+\
|
|
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+\
|
|
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+\
|
|
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
|
package/rules/context-exfiltration/ATR-2026-00702-ipi-credential-exfil-via-agent-action.yaml
CHANGED
|
@@ -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
|
|
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
|
package/rules/context-exfiltration/ATR-2026-00707-ipi-file-exfil-with-destroy-or-tamper.yaml
CHANGED
|
@@ -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
|
|
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
|
|
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)
|
|
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)((
|
|
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
|
package/rules/context-exfiltration/ATR-2026-01463-imend-imsystem-prompt-injection-format.yaml
CHANGED
|
@@ -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)(
|
|
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:
|
|
@@ -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.+-]
|
|
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
|