agent-threat-rules 2.1.3 → 2.1.5

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 (31) hide show
  1. package/dist/action-executor.d.ts +1 -1
  2. package/dist/action-executor.d.ts.map +1 -1
  3. package/dist/action-executor.js +13 -11
  4. package/dist/action-executor.js.map +1 -1
  5. package/dist/adapters/default-adapter.d.ts +2 -1
  6. package/dist/adapters/default-adapter.d.ts.map +1 -1
  7. package/dist/adapters/default-adapter.js +14 -11
  8. package/dist/adapters/default-adapter.js.map +1 -1
  9. package/dist/adapters/stdio-adapter.d.ts +2 -1
  10. package/dist/adapters/stdio-adapter.d.ts.map +1 -1
  11. package/dist/adapters/stdio-adapter.js +43 -26
  12. package/dist/adapters/stdio-adapter.js.map +1 -1
  13. package/dist/converters/index.d.ts +4 -0
  14. package/dist/converters/index.d.ts.map +1 -1
  15. package/dist/converters/index.js +2 -0
  16. package/dist/converters/index.js.map +1 -1
  17. package/dist/converters/sage-reverse.d.ts +52 -0
  18. package/dist/converters/sage-reverse.d.ts.map +1 -0
  19. package/dist/converters/sage-reverse.js +216 -0
  20. package/dist/converters/sage-reverse.js.map +1 -0
  21. package/dist/converters/sage.d.ts +123 -0
  22. package/dist/converters/sage.d.ts.map +1 -0
  23. package/dist/converters/sage.js +702 -0
  24. package/dist/converters/sage.js.map +1 -0
  25. package/dist/types.d.ts +24 -17
  26. package/dist/types.d.ts.map +1 -1
  27. package/package.json +9 -1
  28. package/rules/context-exfiltration/ATR-2026-00449-spring-ai-chatmemory-cross-user-leak.yaml +196 -0
  29. package/rules/data-poisoning/ATR-2026-00450-spring-ai-prompt-memory-poisoning.yaml +196 -0
  30. package/rules/privilege-escalation/ATR-2026-00451-litellm-admin-sqli-cisa-kev.yaml +204 -0
  31. package/rules/tool-poisoning/ATR-2026-00448-spring-ai-milvus-filter-injection.yaml +193 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Sage → ATR Converter (reverse direction)
3
+ *
4
+ * Converts a Sage threat rule (from gendigitalinc/sage `threats/*.yaml`) into
5
+ * an ATR YAML rule suitable for contribution back to the ATR corpus.
6
+ *
7
+ * This is the smaller half of the bidirectional bridge. The forward
8
+ * direction (atrToSage) sees the heavy traffic; this reverse direction
9
+ * exists so that Sage maintainers who write rules in Sage's format can
10
+ * contribute them upstream to ATR without manual schema rewriting.
11
+ *
12
+ * Lossy spots:
13
+ * - Sage has no description field; we emit a placeholder description that
14
+ * humans must fill in before merging into ATR.
15
+ * - Sage has no test_cases; we emit a TODO block instructing humans to add
16
+ * true_positives + true_negatives (required for ATR PR acceptance).
17
+ * - Sage has no compliance metadata (eu_ai_act, nist_ai_rmf, etc.); humans
18
+ * must add these if the rule maps to a regulatory framework.
19
+ * - Sage has no references (mitre_atlas, owasp_llm, etc.); humans must add.
20
+ * - Sage `match_on: command|url|file_path|content|domain` → ATR field name.
21
+ * The translation is heuristic since Sage's "command" channel doesn't have
22
+ * a perfect ATR equivalent (closest is tool_args at invocation time).
23
+ *
24
+ * @module agent-threat-rules/converters/sage-reverse
25
+ */
26
+ // ── Reverse mappings ───────────────────────────────────────────────────────
27
+ /**
28
+ * Sage category → ATR category. Inverse of CATEGORY_MAP in sage.ts.
29
+ * Unknown Sage categories pass through with a warning; humans should review.
30
+ */
31
+ const CATEGORY_REVERSE_MAP = Object.freeze({
32
+ prompt_injection: 'prompt-injection',
33
+ mcp_poisoning: 'tool-poisoning',
34
+ context_exfiltration: 'context-exfiltration',
35
+ agent_manipulation: 'agent-manipulation',
36
+ privilege_escalation: 'privilege-escalation',
37
+ excessive_autonomy: 'excessive-autonomy',
38
+ data_poisoning: 'data-poisoning',
39
+ model_abuse: 'model-abuse',
40
+ skill_compromise: 'skill-compromise',
41
+ });
42
+ const SEVERITY_REVERSE_MAP = Object.freeze({
43
+ critical: 'critical',
44
+ high: 'high',
45
+ medium: 'medium',
46
+ low: 'low',
47
+ });
48
+ /**
49
+ * Sage action → ATR action list. Sage's `block` could mean block_input,
50
+ * block_output, or block_tool depending on context; default to the most
51
+ * conservative `block_input + alert` combination.
52
+ */
53
+ function reverseAction(sage) {
54
+ switch (sage) {
55
+ case 'block':
56
+ return ['block_input', 'alert'];
57
+ case 'require_approval':
58
+ return ['escalate', 'alert'];
59
+ case 'log':
60
+ return ['alert'];
61
+ }
62
+ }
63
+ /**
64
+ * Sage match_on → ATR field name. Sage's "command" is closest to ATR's
65
+ * tool_args at invocation; "file_path" maps similarly. "domain" has no clean
66
+ * ATR equivalent (no DNS-tier rules in current ATR corpus).
67
+ */
68
+ function reverseMatchOn(sageMatchOn) {
69
+ switch (sageMatchOn) {
70
+ case 'url':
71
+ return 'url';
72
+ case 'command':
73
+ return 'tool_args';
74
+ case 'file_path':
75
+ return 'tool_args';
76
+ case 'content':
77
+ return 'content';
78
+ case 'domain':
79
+ return 'tool_args';
80
+ }
81
+ }
82
+ // ── Confidence ─────────────────────────────────────────────────────────────
83
+ /**
84
+ * Convert Sage's numeric confidence (0.0-1.0) to ATR's `tags.confidence`
85
+ * string enum (high/medium/low).
86
+ */
87
+ function sageConfidenceToAtrConfidence(c) {
88
+ if (c >= 0.85)
89
+ return 'high';
90
+ if (c >= 0.6)
91
+ return 'medium';
92
+ return 'low';
93
+ }
94
+ // ── Core reverse conversion ────────────────────────────────────────────────
95
+ /**
96
+ * Generate an ATR id placeholder. Real production use should override this
97
+ * with a maintainer-assigned final id at PR time.
98
+ */
99
+ function placeholderAtrId(sageId) {
100
+ // e.g. CLT-PI-001 → ATR-2026-PI001 (placeholder, human reviews)
101
+ const idPart = sageId.replace(/^CLT-/, '').replace(/-/g, '');
102
+ const year = new Date().getFullYear();
103
+ return `ATR-${year}-${idPart.padStart(5, '0').slice(0, 5)}`;
104
+ }
105
+ /**
106
+ * Convert a single Sage rule to an ATR rule.
107
+ *
108
+ * The output rule has TODO markers in description and test_cases fields
109
+ * that humans must fill in before merging. See module docstring for lossy
110
+ * spots that require human enrichment.
111
+ */
112
+ export function sageToAtr(sage) {
113
+ const warnings = [];
114
+ // Category map with warning on unknown
115
+ const atrCategory = CATEGORY_REVERSE_MAP[sage.category];
116
+ if (!atrCategory) {
117
+ warnings.push({
118
+ sageId: sage.id,
119
+ kind: 'category_unknown',
120
+ detail: `Sage category "${sage.category}" not in known reverse map; defaulting to skill-compromise (closest catch-all)`,
121
+ });
122
+ }
123
+ // match_on can be single or array. For ATR, we emit one condition per
124
+ // match_on field.
125
+ const matchOnList = Array.isArray(sage.match_on)
126
+ ? [...sage.match_on]
127
+ : [sage.match_on];
128
+ if (matchOnList.length > 1) {
129
+ warnings.push({
130
+ sageId: sage.id,
131
+ kind: 'match_on_ambiguous',
132
+ detail: `Multi-channel match_on=${matchOnList.join(',')} expanded to ${matchOnList.length} ATR conditions`,
133
+ });
134
+ }
135
+ // Build conditions. If Sage rule has case_insensitive: true, wrap the
136
+ // regex with (?i) prefix so the ATR rule preserves case-insensitive
137
+ // semantics when run on an ATR engine.
138
+ const regexValue = sage.case_insensitive
139
+ ? sage.pattern.startsWith('(?i)')
140
+ ? sage.pattern
141
+ : `(?i)${sage.pattern}`
142
+ : sage.pattern;
143
+ const conditions = matchOnList.map((m) => ({
144
+ field: reverseMatchOn(m),
145
+ operator: 'regex',
146
+ value: regexValue,
147
+ description: `Imported from Sage rule ${sage.id}`,
148
+ }));
149
+ warnings.push({
150
+ sageId: sage.id,
151
+ kind: 'missing_description',
152
+ detail: 'Description placeholder used — fill in attack details before merging',
153
+ });
154
+ warnings.push({
155
+ sageId: sage.id,
156
+ kind: 'missing_test_cases',
157
+ detail: 'Sage rules ship without test cases — add true_positives + true_negatives before ATR PR',
158
+ });
159
+ warnings.push({
160
+ sageId: sage.id,
161
+ kind: 'missing_compliance',
162
+ detail: 'Add compliance.eu_ai_act + nist_ai_rmf + iso_42001 if rule maps to regulatory frameworks',
163
+ });
164
+ warnings.push({
165
+ sageId: sage.id,
166
+ kind: 'missing_references',
167
+ detail: 'Add references.mitre_atlas / mitre_attack / owasp_llm / cve where applicable',
168
+ });
169
+ const now = new Date();
170
+ const dateStr = `${now.getUTCFullYear()}/${String(now.getUTCMonth() + 1).padStart(2, '0')}/${String(now.getUTCDate()).padStart(2, '0')}`;
171
+ const rule = {
172
+ title: sage.title,
173
+ id: placeholderAtrId(sage.id),
174
+ status: 'draft', // require human review before stable
175
+ description: 'TODO: replace this placeholder. Describe the attack pattern this rule detects, what it does NOT detect, ' +
176
+ `and any known false-positive surface. Imported from Sage rule ${sage.id} ` +
177
+ `(${sage.upstream_url ?? 'no upstream url'}).`,
178
+ author: 'Sage (Gen Digital Inc.)',
179
+ date: dateStr,
180
+ schema_version: '0.1',
181
+ detection_tier: 'pattern',
182
+ maturity: 'experimental',
183
+ severity: SEVERITY_REVERSE_MAP[sage.severity],
184
+ tags: {
185
+ category: atrCategory ?? 'skill-compromise',
186
+ confidence: sageConfidenceToAtrConfidence(sage.confidence),
187
+ },
188
+ agent_source: {
189
+ type: 'tool_call',
190
+ },
191
+ detection: {
192
+ conditions,
193
+ condition: 'any',
194
+ },
195
+ response: {
196
+ actions: reverseAction(sage.action),
197
+ },
198
+ // Confidence: ATR's numeric scale is 0-100; multiply Sage's 0-1.
199
+ confidence: Math.round(sage.confidence * 100),
200
+ };
201
+ return { rule, warnings };
202
+ }
203
+ /**
204
+ * Reverse-convert many Sage rules.
205
+ */
206
+ export function sageToAtrBatch(sageRules) {
207
+ const rules = [];
208
+ const warnings = [];
209
+ for (const sage of sageRules) {
210
+ const result = sageToAtr(sage);
211
+ rules.push(result.rule);
212
+ warnings.push(...result.warnings);
213
+ }
214
+ return { rules, warnings };
215
+ }
216
+ //# sourceMappingURL=sage-reverse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sage-reverse.js","sourceRoot":"","sources":["../../src/converters/sage-reverse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * ATR → Sage (gendigitalinc/sage) Converter
3
+ *
4
+ * Converts ATR YAML rules into Sage's threat rule schema, suitable for
5
+ * Sage's `threats/agent-layer.yaml`. Sage uses a single-pattern-per-rule
6
+ * schema with `match_on` controlling the artifact channel; ATR uses
7
+ * multi-condition rules with `field`-targeted regexes.
8
+ *
9
+ * Conversion strategy (per research memo):
10
+ * - One ATR rule → one or more Sage rules, grouped by `field` target.
11
+ * Conditions on the same field combine via regex alternation.
12
+ * - ATR `field` values (user_input, agent_output, content, tool_response,
13
+ * tool_args, tool_name, tool_description) collapse to Sage `match_on:
14
+ * content`. Sage's other channels (command, url, file_path, domain) are
15
+ * command-time matchers and do not have ATR equivalents in the current
16
+ * corpus.
17
+ * - ATR `response.actions` (10 values) collapse to Sage `action` (3
18
+ * values: block / require_approval / log) by strongest-wins semantics.
19
+ * - ATR `(?i)` inline flag (PCRE) is extracted into Sage's rule-level
20
+ * `case_insensitive: true` because the Sage runtime regex compiler is
21
+ * JavaScript and does not support inline flag groups.
22
+ * - ATR `detection_tier: semantic` rules are SKIPPED — they require LLM
23
+ * evaluation, not deterministic regex.
24
+ * - License: ATR rules are MIT; Sage's `threats/*.yaml` are DRL 1.1. Per-rule
25
+ * comment in the output preserves upstream attribution per both licenses.
26
+ *
27
+ * Used by: scripts/sync-with-atr.ts (Sage maintainers can run this script
28
+ * to regenerate threats/agent-layer.yaml from a pinned ATR version), and
29
+ * by any downstream tool that consumes ATR but wants Sage-flavoured output.
30
+ *
31
+ * @module agent-threat-rules/converters/sage
32
+ */
33
+ import type { ATRRule } from '../types.js';
34
+ export type SageSeverity = 'critical' | 'high' | 'medium' | 'low';
35
+ export type SageAction = 'block' | 'require_approval' | 'log';
36
+ export type SageMatchOn = 'command' | 'url' | 'file_path' | 'content' | 'domain';
37
+ export interface SageRule {
38
+ readonly id: string;
39
+ readonly category: string;
40
+ readonly severity: SageSeverity;
41
+ readonly confidence: number;
42
+ readonly action: SageAction;
43
+ readonly pattern: string;
44
+ readonly match_on: SageMatchOn | readonly SageMatchOn[];
45
+ readonly title: string;
46
+ readonly expires_at: string | null;
47
+ readonly revoked: boolean;
48
+ readonly case_insensitive?: boolean;
49
+ /** Non-standard provenance fields preserved per the Sage loader's permissive parsing. */
50
+ readonly upstream?: string;
51
+ readonly upstream_url?: string;
52
+ readonly upstream_license?: string;
53
+ /** 1-2 sentence attack-scenario summary derived from ATR `description`, emitted as a `# Detects:` comment in YAML output. */
54
+ readonly detects?: string;
55
+ }
56
+ export interface ConversionWarning {
57
+ readonly ruleId: string;
58
+ readonly kind: 'semantic_tier_skipped' | 'unsupported_action_dropped' | 'regex_compat_issue' | 'split_by_length' | 'condition_without_field' | 'no_convertible_conditions' | 'deprecated_skipped' | 'draft_marked_revoked';
59
+ readonly detail: string;
60
+ }
61
+ export interface ConvertResult {
62
+ readonly rules: readonly SageRule[];
63
+ readonly warnings: readonly ConversionWarning[];
64
+ }
65
+ /**
66
+ * Generate a Sage rule id matching Sage's existing 3-digit-suffix convention.
67
+ * Format: CLT-<PREFIX>-<NNN> where PREFIX is the 2-3 letter category code.
68
+ *
69
+ * The id is generated sequentially by an IdAllocator passed in by the caller,
70
+ * NOT derived from the ATR id. This matches Sage's convention (CLT-PI-001,
71
+ * CLT-MCP-001, etc.) instead of producing weird-looking ids like CLT-PRV-0441.
72
+ *
73
+ * The ATR provenance survives in the `# Upstream: ATR-2026-NNNNN` comment.
74
+ */
75
+ export declare class SageIdAllocator {
76
+ private counters;
77
+ /**
78
+ * Construct with optional starting offsets per category. Pass
79
+ * `{prompt_injection: 8}` to start prompt_injection ids at 008 (after
80
+ * Sage's existing CLT-PI-001..007, for example).
81
+ */
82
+ constructor(startingOffsets?: Readonly<Record<string, number>>);
83
+ next(sageCategory: string, channelDisambiguator: string | null): string;
84
+ }
85
+ /**
86
+ * Convert a single ATR rule to one or more Sage rules.
87
+ *
88
+ * Returns one Sage rule per unique `field` target in the ATR rule's detection
89
+ * conditions. Most ATR rules in the current corpus target a single field
90
+ * (user_input or tool_response or content), producing 1 Sage rule each.
91
+ *
92
+ * Lossy spots (documented in the bridge research memo):
93
+ * - Multi-condition rules with mixed case-sensitivity → whole rule becomes
94
+ * case-insensitive
95
+ * - `condition: all` semantics → ignored; alternation always treats as OR
96
+ * - `response.actions` reduced to strongest single action
97
+ * - All compliance/metadata fields (eu_ai_act, mitre_atlas, references, etc.)
98
+ * are dropped (not in Sage schema). They survive in the upstream_url link.
99
+ */
100
+ export declare function atrToSage(atr: ATRRule, idAllocator?: SageIdAllocator): ConvertResult;
101
+ /**
102
+ * Convert many ATR rules to Sage rules. Aggregates warnings and uses a shared
103
+ * id allocator so that the resulting Sage rules have sequential 3-digit ids
104
+ * (CLT-PRV-001, CLT-PRV-002, ...) within each category, matching Sage's
105
+ * existing convention.
106
+ *
107
+ * @param atrRules the ATR rules to convert
108
+ * @param startingOffsets optional starting offsets per Sage category — pass
109
+ * `{prompt_injection: 7}` if Sage's existing rules already use CLT-PI-001
110
+ * through CLT-PI-007 so the new rules start at CLT-PI-008.
111
+ */
112
+ export declare function atrToSageBatch(atrRules: readonly ATRRule[], startingOffsets?: Readonly<Record<string, number>>): ConvertResult;
113
+ /**
114
+ * Serialize a list of Sage rules to a YAML string suitable for inclusion in
115
+ * `threats/agent-layer.yaml`. The output matches the style used in Sage's
116
+ * existing agent-layer.yaml (no list indentation, double-quoted ids,
117
+ * single-line regex pattern, upstream comment after each rule).
118
+ *
119
+ * Caller is responsible for prepending file-level comments (license,
120
+ * generation timestamp, upstream version) before this output.
121
+ */
122
+ export declare function sageRulesToYaml(rules: readonly SageRule[]): string;
123
+ //# sourceMappingURL=sage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sage.d.ts","sourceRoot":"","sources":["../../src/converters/sage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EACX,OAAO,EAIP,MAAM,aAAa,CAAC;AAIrB,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAClE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,kBAAkB,GAAG,KAAK,CAAC;AAC9D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,6HAA6H;IAC7H,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EACV,uBAAuB,GACvB,4BAA4B,GAC5B,oBAAoB,GACpB,iBAAiB,GACjB,yBAAyB,GACzB,2BAA2B,GAC3B,oBAAoB,GACpB,sBAAsB,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAChD;AAmOD;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAA6B;IAE7C;;;;OAIG;gBACS,eAAe,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAMlE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;CAQvE;AAkLD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CACxB,GAAG,EAAE,OAAO,EACZ,WAAW,GAAE,eAAuC,GAClD,aAAa,CAuHf;AAuGD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,eAAe,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GACpD,aAAa,CAUf;AAID;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,CAElE"}