agent-threat-rules 0.2.2 → 0.3.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 (125) hide show
  1. package/README.md +152 -642
  2. package/dist/capability-extractor.d.ts +35 -0
  3. package/dist/capability-extractor.d.ts.map +1 -0
  4. package/dist/capability-extractor.js +91 -0
  5. package/dist/capability-extractor.js.map +1 -0
  6. package/dist/cli.js +56 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/converters/elastic.d.ts +36 -0
  9. package/dist/converters/elastic.d.ts.map +1 -0
  10. package/dist/converters/elastic.js +125 -0
  11. package/dist/converters/elastic.js.map +1 -0
  12. package/dist/converters/index.d.ts +28 -0
  13. package/dist/converters/index.d.ts.map +1 -0
  14. package/dist/converters/index.js +36 -0
  15. package/dist/converters/index.js.map +1 -0
  16. package/dist/converters/splunk.d.ts +19 -0
  17. package/dist/converters/splunk.d.ts.map +1 -0
  18. package/dist/converters/splunk.js +148 -0
  19. package/dist/converters/splunk.js.map +1 -0
  20. package/dist/embedding/build-corpus.d.ts +15 -0
  21. package/dist/embedding/build-corpus.d.ts.map +1 -0
  22. package/dist/embedding/build-corpus.js +105 -0
  23. package/dist/embedding/build-corpus.js.map +1 -0
  24. package/dist/embedding/model-loader.d.ts +41 -0
  25. package/dist/embedding/model-loader.d.ts.map +1 -0
  26. package/dist/embedding/model-loader.js +90 -0
  27. package/dist/embedding/model-loader.js.map +1 -0
  28. package/dist/embedding/vector-store.d.ts +41 -0
  29. package/dist/embedding/vector-store.d.ts.map +1 -0
  30. package/dist/embedding/vector-store.js +70 -0
  31. package/dist/embedding/vector-store.js.map +1 -0
  32. package/dist/engine.d.ts +23 -20
  33. package/dist/engine.d.ts.map +1 -1
  34. package/dist/engine.js +173 -24
  35. package/dist/engine.js.map +1 -1
  36. package/dist/eval/corpus.d.ts +42 -0
  37. package/dist/eval/corpus.d.ts.map +1 -0
  38. package/dist/eval/corpus.js +427 -0
  39. package/dist/eval/corpus.js.map +1 -0
  40. package/dist/eval/eval-harness.d.ts +44 -0
  41. package/dist/eval/eval-harness.d.ts.map +1 -0
  42. package/dist/eval/eval-harness.js +296 -0
  43. package/dist/eval/eval-harness.js.map +1 -0
  44. package/dist/eval/index.d.ts +13 -0
  45. package/dist/eval/index.d.ts.map +1 -0
  46. package/dist/eval/index.js +9 -0
  47. package/dist/eval/index.js.map +1 -0
  48. package/dist/eval/metrics.d.ts +74 -0
  49. package/dist/eval/metrics.d.ts.map +1 -0
  50. package/dist/eval/metrics.js +108 -0
  51. package/dist/eval/metrics.js.map +1 -0
  52. package/dist/eval/pint-corpus.d.ts +34 -0
  53. package/dist/eval/pint-corpus.d.ts.map +1 -0
  54. package/dist/eval/pint-corpus.js +109 -0
  55. package/dist/eval/pint-corpus.js.map +1 -0
  56. package/dist/eval/rule-corpus.d.ts +9 -0
  57. package/dist/eval/rule-corpus.d.ts.map +1 -0
  58. package/dist/eval/rule-corpus.js +4780 -0
  59. package/dist/eval/rule-corpus.js.map +1 -0
  60. package/dist/eval/rule-metrics.d.ts +34 -0
  61. package/dist/eval/rule-metrics.d.ts.map +1 -0
  62. package/dist/eval/rule-metrics.js +92 -0
  63. package/dist/eval/rule-metrics.js.map +1 -0
  64. package/dist/eval/run-eval.d.ts +7 -0
  65. package/dist/eval/run-eval.d.ts.map +1 -0
  66. package/dist/eval/run-eval.js +11 -0
  67. package/dist/eval/run-eval.js.map +1 -0
  68. package/dist/eval/run-pint-benchmark.d.ts +18 -0
  69. package/dist/eval/run-pint-benchmark.d.ts.map +1 -0
  70. package/dist/eval/run-pint-benchmark.js +157 -0
  71. package/dist/eval/run-pint-benchmark.js.map +1 -0
  72. package/dist/flywheel.d.ts +54 -0
  73. package/dist/flywheel.d.ts.map +1 -0
  74. package/dist/flywheel.js +121 -0
  75. package/dist/flywheel.js.map +1 -0
  76. package/dist/index.d.ts +21 -1
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +19 -2
  79. package/dist/index.js.map +1 -1
  80. package/dist/modules/embedding.d.ts +71 -0
  81. package/dist/modules/embedding.d.ts.map +1 -0
  82. package/dist/modules/embedding.js +141 -0
  83. package/dist/modules/embedding.js.map +1 -0
  84. package/dist/modules/semantic.d.ts +1 -0
  85. package/dist/modules/semantic.d.ts.map +1 -1
  86. package/dist/modules/semantic.js +77 -1
  87. package/dist/modules/semantic.js.map +1 -1
  88. package/dist/rule-scaffolder.d.ts +14 -0
  89. package/dist/rule-scaffolder.d.ts.map +1 -1
  90. package/dist/rule-scaffolder.js +123 -6
  91. package/dist/rule-scaffolder.js.map +1 -1
  92. package/dist/session-tracker.d.ts +2 -0
  93. package/dist/session-tracker.d.ts.map +1 -1
  94. package/dist/session-tracker.js +1 -0
  95. package/dist/session-tracker.js.map +1 -1
  96. package/dist/shadow-evaluator.d.ts +48 -0
  97. package/dist/shadow-evaluator.d.ts.map +1 -0
  98. package/dist/shadow-evaluator.js +128 -0
  99. package/dist/shadow-evaluator.js.map +1 -0
  100. package/dist/skill-fingerprint.d.ts.map +1 -1
  101. package/dist/skill-fingerprint.js +10 -52
  102. package/dist/skill-fingerprint.js.map +1 -1
  103. package/dist/tier0-invariant.d.ts +49 -0
  104. package/dist/tier0-invariant.d.ts.map +1 -0
  105. package/dist/tier0-invariant.js +184 -0
  106. package/dist/tier0-invariant.js.map +1 -0
  107. package/dist/tier1-blacklist.d.ts +48 -0
  108. package/dist/tier1-blacklist.d.ts.map +1 -0
  109. package/dist/tier1-blacklist.js +91 -0
  110. package/dist/tier1-blacklist.js.map +1 -0
  111. package/package.json +7 -1
  112. package/rules/agent-manipulation/ATR-2026-108-consensus-sybil-attack.yaml +103 -0
  113. package/rules/context-exfiltration/ATR-2026-102-disguised-analytics-exfiltration.yaml +69 -0
  114. package/rules/privilege-escalation/ATR-2026-107-delayed-execution-bypass.yaml +67 -0
  115. package/rules/prompt-injection/ATR-2026-001-direct-prompt-injection.yaml +181 -94
  116. package/rules/prompt-injection/ATR-2026-003-jailbreak-attempt.yaml +23 -12
  117. package/rules/prompt-injection/ATR-2026-004-system-prompt-override.yaml +3 -3
  118. package/rules/prompt-injection/ATR-2026-081-semantic-multi-turn.yaml +2 -2
  119. package/rules/prompt-injection/ATR-2026-093-gradual-escalation.yaml +1 -1
  120. package/rules/prompt-injection/ATR-2026-104-persona-hijacking.yaml +72 -0
  121. package/rules/tool-poisoning/ATR-2026-100-consent-bypass-instruction.yaml +80 -0
  122. package/rules/tool-poisoning/ATR-2026-101-trust-escalation-override.yaml +66 -0
  123. package/rules/tool-poisoning/ATR-2026-103-hidden-safety-bypass-instruction.yaml +71 -0
  124. package/rules/tool-poisoning/ATR-2026-105-silent-action-concealment.yaml +67 -0
  125. package/rules/tool-poisoning/ATR-2026-106-schema-description-contradiction.yaml +66 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ATR-to-Splunk SPL Converter
3
+ *
4
+ * Converts ATR YAML rules into Splunk Search Processing Language (SPL) queries
5
+ * that a SOC analyst can use as a starting point for threat hunting.
6
+ *
7
+ * @module agent-threat-rules/converters/splunk
8
+ */
9
+ import type { ATRRule } from '../types.js';
10
+ /**
11
+ * Convert an ATR rule to a Splunk SPL query string.
12
+ *
13
+ * The generated query includes:
14
+ * - Comment header with rule metadata
15
+ * - Index/sourcetype base search (generic, analyst should customize)
16
+ * - Condition clauses joined with appropriate logic
17
+ */
18
+ export declare function ruleToSPL(rule: ATRRule): string;
19
+ //# sourceMappingURL=splunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splunk.d.ts","sourceRoot":"","sources":["../../src/converters/splunk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,aAAa,CAAC;AAoD9D;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAqG/C"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ATR-to-Splunk SPL Converter
3
+ *
4
+ * Converts ATR YAML rules into Splunk Search Processing Language (SPL) queries
5
+ * that a SOC analyst can use as a starting point for threat hunting.
6
+ *
7
+ * @module agent-threat-rules/converters/splunk
8
+ */
9
+ /**
10
+ * Escape a string for use in Splunk SPL double-quoted values.
11
+ */
12
+ function escapeForSPL(value) {
13
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
14
+ }
15
+ /**
16
+ * Convert a single ATR array condition to an SPL clause.
17
+ *
18
+ * Supports operators: regex, contains, exact, starts_with, gt, lt, gte, lte, eq
19
+ */
20
+ function conditionToSPL(cond) {
21
+ const field = cond.field;
22
+ const value = cond.value;
23
+ switch (cond.operator) {
24
+ case 'regex':
25
+ return `| regex ${field}="${escapeForSPL(value)}"`;
26
+ case 'contains':
27
+ return `${field}="*${escapeForSPL(value)}*"`;
28
+ case 'exact':
29
+ return `${field}="${escapeForSPL(value)}"`;
30
+ case 'starts_with':
31
+ return `${field}="${escapeForSPL(value)}*"`;
32
+ case 'gt':
33
+ return `| where ${field} > ${Number(value)}`;
34
+ case 'lt':
35
+ return `| where ${field} < ${Number(value)}`;
36
+ case 'gte':
37
+ return `| where ${field} >= ${Number(value)}`;
38
+ case 'lte':
39
+ return `| where ${field} <= ${Number(value)}`;
40
+ case 'eq':
41
+ return `| where ${field} == ${Number(value)}`;
42
+ default:
43
+ // Fallback: treat unknown operators as a contains search
44
+ return `${field}="*${escapeForSPL(value)}*"`;
45
+ }
46
+ }
47
+ /**
48
+ * Convert an ATR rule to a Splunk SPL query string.
49
+ *
50
+ * The generated query includes:
51
+ * - Comment header with rule metadata
52
+ * - Index/sourcetype base search (generic, analyst should customize)
53
+ * - Condition clauses joined with appropriate logic
54
+ */
55
+ export function ruleToSPL(rule) {
56
+ const conditions = rule.detection.conditions;
57
+ const logic = rule.detection.condition; // "any" or "all"
58
+ const lines = [];
59
+ // Comment header with rule metadata
60
+ lines.push(`\`\`\` ATR Rule: ${rule.id} \`\`\``);
61
+ lines.push(`\`\`\` Title: ${rule.title} \`\`\``);
62
+ lines.push(`\`\`\` Severity: ${rule.severity} | Category: ${rule.tags.category} \`\`\``);
63
+ lines.push(`\`\`\` Source: ${rule.agent_source.type} | Condition logic: ${logic} \`\`\``);
64
+ lines.push('');
65
+ // Base search -- analyst should adjust index and sourcetype
66
+ lines.push('index=ai_agent_logs sourcetype=agent_events');
67
+ if (!Array.isArray(conditions)) {
68
+ // Named-map format: not common in current rules, emit a placeholder
69
+ lines.push('```` Warning: Named-map conditions not fully supported. Review manually. ````');
70
+ return lines.join('\n');
71
+ }
72
+ const arrayConditions = conditions;
73
+ if (arrayConditions.length === 0) {
74
+ return lines.join('\n');
75
+ }
76
+ // For "any" logic with regex conditions, we can combine them using
77
+ // a single regex with OR (|) alternation where possible, or use
78
+ // multiple search branches.
79
+ // For clarity and analyst usability, we emit each condition separately.
80
+ if (logic === 'all') {
81
+ // AND logic: chain all conditions sequentially
82
+ for (const cond of arrayConditions) {
83
+ lines.push(conditionToSPL(cond));
84
+ }
85
+ }
86
+ else {
87
+ // OR logic ("any"): use Splunk's multisearch or OR-joined search
88
+ // For regex conditions, wrap in a single eval+match approach
89
+ // For simplicity and readability, use OR-joined subsearches
90
+ const regexConditions = arrayConditions.filter(c => c.operator === 'regex');
91
+ const otherConditions = arrayConditions.filter(c => c.operator !== 'regex');
92
+ if (regexConditions.length > 0 && otherConditions.length === 0) {
93
+ // All regex: combine with OR in eval/match
94
+ lines.push('| where (');
95
+ const regexClauses = regexConditions.map((cond, i) => {
96
+ const prefix = i === 0 ? ' ' : ' OR ';
97
+ return `${prefix}match(${cond.field}, "${escapeForSPL(cond.value)}")`;
98
+ });
99
+ lines.push(...regexClauses);
100
+ lines.push(')');
101
+ }
102
+ else {
103
+ // Mixed operators: emit each as separate OR clause
104
+ lines.push('| where (');
105
+ const clauses = [];
106
+ for (const cond of arrayConditions) {
107
+ switch (cond.operator) {
108
+ case 'regex':
109
+ clauses.push(`match(${cond.field}, "${escapeForSPL(cond.value)}")`);
110
+ break;
111
+ case 'contains':
112
+ clauses.push(`like(${cond.field}, "%${escapeForSPL(cond.value)}%")`);
113
+ break;
114
+ case 'exact':
115
+ clauses.push(`${cond.field}="${escapeForSPL(cond.value)}"`);
116
+ break;
117
+ case 'starts_with':
118
+ clauses.push(`like(${cond.field}, "${escapeForSPL(cond.value)}%")`);
119
+ break;
120
+ case 'gt':
121
+ clauses.push(`${cond.field} > ${Number(cond.value)}`);
122
+ break;
123
+ case 'lt':
124
+ clauses.push(`${cond.field} < ${Number(cond.value)}`);
125
+ break;
126
+ case 'gte':
127
+ clauses.push(`${cond.field} >= ${Number(cond.value)}`);
128
+ break;
129
+ case 'lte':
130
+ clauses.push(`${cond.field} <= ${Number(cond.value)}`);
131
+ break;
132
+ case 'eq':
133
+ clauses.push(`${cond.field} == ${Number(cond.value)}`);
134
+ break;
135
+ default:
136
+ clauses.push(`like(${cond.field}, "%${escapeForSPL(cond.value)}%")`);
137
+ }
138
+ }
139
+ lines.push(clauses.map((c, i) => (i === 0 ? ` ${c}` : ` OR ${c}`)).join('\n'));
140
+ lines.push(')');
141
+ }
142
+ }
143
+ // Add a table output for the analyst
144
+ const fields = [...new Set(arrayConditions.map(c => c.field))];
145
+ lines.push(`| table _time ${fields.join(' ')} source`);
146
+ return lines.join('\n');
147
+ }
148
+ //# sourceMappingURL=splunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splunk.js","sourceRoot":"","sources":["../../src/converters/splunk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAuB;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAEzB,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,KAAK,OAAO;YACV,OAAO,WAAW,KAAK,KAAK,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC;QAErD,KAAK,UAAU;YACb,OAAO,GAAG,KAAK,MAAM,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;QAE/C,KAAK,OAAO;YACV,OAAO,GAAG,KAAK,KAAK,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC;QAE7C,KAAK,aAAa;YAChB,OAAO,GAAG,KAAK,KAAK,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;QAE9C,KAAK,IAAI;YACP,OAAO,WAAW,KAAK,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAE/C,KAAK,IAAI;YACP,OAAO,WAAW,KAAK,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAE/C,KAAK,KAAK;YACR,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAEhD,KAAK,KAAK;YACR,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAEhD,KAAK,IAAI;YACP,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAEhD;YACE,yDAAyD;YACzD,OAAO,GAAG,KAAK,MAAM,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAa;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,iBAAiB;IAEzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,oCAAoC;IACpC,KAAK,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,QAAQ,gBAAgB,IAAI,CAAC,IAAI,CAAC,QAAQ,SAAS,CAAC,CAAC;IACzF,KAAK,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,YAAY,CAAC,IAAI,uBAAuB,KAAK,SAAS,CAAC,CAAC;IAC1F,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,4DAA4D;IAC5D,KAAK,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAE1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,oEAAoE;QACpE,KAAK,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;QAC5F,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,eAAe,GAAG,UAAiC,CAAC;IAE1D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,mEAAmE;IACnE,gEAAgE;IAChE,4BAA4B;IAC5B,wEAAwE;IAExE,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,+CAA+C;QAC/C,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,iEAAiE;QACjE,6DAA6D;QAC7D,4DAA4D;QAC5D,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QAC5E,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QAE5E,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/D,2CAA2C;YAC3C,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxB,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACnD,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC5C,OAAO,GAAG,MAAM,SAAS,IAAI,CAAC,KAAK,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACxE,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,mDAAmD;YACnD,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxB,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;gBACnC,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACtB,KAAK,OAAO;wBACV,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;wBACpE,MAAM;oBACR,KAAK,UAAU;wBACb,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;wBACrE,MAAM;oBACR,KAAK,OAAO;wBACV,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,KAAK,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;wBAC5D,MAAM;oBACR,KAAK,aAAa;wBAChB,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;wBACpE,MAAM;oBACR,KAAK,IAAI;wBACP,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;wBACtD,MAAM;oBACR,KAAK,IAAI;wBACP,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;wBACtD,MAAM;oBACR,KAAK,KAAK;wBACR,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;wBACvD,MAAM;oBACR,KAAK,KAAK;wBACR,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;wBACvD,MAAM;oBACR,KAAK,IAAI;wBACP,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;wBACvD,MAAM;oBACR;wBACE,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACzE,CAAC;YACH,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACrF,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAEvD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Build attack embedding corpus from ATR rule test cases.
4
+ *
5
+ * Reads all stable ATR rules, extracts true_positive test cases,
6
+ * encodes them through all-MiniLM-L6-v2, and saves as JSON.
7
+ *
8
+ * Usage:
9
+ * npx tsx src/embedding/build-corpus.ts
10
+ *
11
+ * Output:
12
+ * data/attack-embeddings.json
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=build-corpus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-corpus.d.ts","sourceRoot":"","sources":["../../src/embedding/build-corpus.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Build attack embedding corpus from ATR rule test cases.
4
+ *
5
+ * Reads all stable ATR rules, extracts true_positive test cases,
6
+ * encodes them through all-MiniLM-L6-v2, and saves as JSON.
7
+ *
8
+ * Usage:
9
+ * npx tsx src/embedding/build-corpus.ts
10
+ *
11
+ * Output:
12
+ * data/attack-embeddings.json
13
+ */
14
+ import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
15
+ import { join, resolve } from 'node:path';
16
+ import * as yaml from 'js-yaml';
17
+ const RULES_DIR = resolve(join(import.meta.dirname ?? '.', '..', '..', 'rules'));
18
+ const OUTPUT_PATH = resolve(join(import.meta.dirname ?? '.', '..', '..', 'data', 'attack-embeddings.json'));
19
+ async function main() {
20
+ console.log('Building attack embedding corpus...');
21
+ console.log(`Rules dir: ${RULES_DIR}`);
22
+ // Load model
23
+ console.log('Loading embedding model (first run downloads ~22MB)...');
24
+ const { TransformersJSModel } = await import('./model-loader.js');
25
+ const model = new TransformersJSModel();
26
+ await model.initialize();
27
+ console.log('Model loaded.');
28
+ // Collect all true_positive texts from rules
29
+ const attacks = [];
30
+ function walkDir(dir) {
31
+ const files = [];
32
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
33
+ const fullPath = join(dir, entry.name);
34
+ if (entry.isDirectory()) {
35
+ files.push(...walkDir(fullPath));
36
+ }
37
+ else if (entry.name.endsWith('.yaml') || entry.name.endsWith('.yml')) {
38
+ files.push(fullPath);
39
+ }
40
+ }
41
+ return files;
42
+ }
43
+ const ruleFiles = walkDir(RULES_DIR);
44
+ console.log(`Found ${ruleFiles.length} rule files.`);
45
+ for (const file of ruleFiles) {
46
+ try {
47
+ const content = readFileSync(file, 'utf-8');
48
+ const rule = yaml.load(content);
49
+ if (!rule?.id || !rule?.test_cases?.true_positives)
50
+ continue;
51
+ for (const tp of rule.test_cases.true_positives) {
52
+ const text = tp.input ?? tp.content ?? tp.user_input ?? tp.tool_response ?? tp.tool_description ?? tp.tool_args;
53
+ if (!text || text.length < 10)
54
+ continue;
55
+ attacks.push({
56
+ id: rule.id,
57
+ text: text.slice(0, 512),
58
+ category: rule.tags?.category ?? 'unknown',
59
+ severity: rule.severity ?? 'medium',
60
+ ruleTitle: rule.title ?? rule.id,
61
+ });
62
+ }
63
+ }
64
+ catch {
65
+ // Skip unparseable rules
66
+ }
67
+ }
68
+ console.log(`Extracted ${attacks.length} attack payloads from ${ruleFiles.length} rules.`);
69
+ // Deduplicate by text
70
+ const seen = new Set();
71
+ const unique = attacks.filter((a) => {
72
+ if (seen.has(a.text))
73
+ return false;
74
+ seen.add(a.text);
75
+ return true;
76
+ });
77
+ console.log(`Unique payloads: ${unique.length}`);
78
+ // Encode all payloads
79
+ console.log('Encoding payloads...');
80
+ const output = [];
81
+ for (let i = 0; i < unique.length; i++) {
82
+ const a = unique[i];
83
+ process.stdout.write(`\r [${i + 1}/${unique.length}] ${a.id}`);
84
+ const vec = await model.encode(a.text);
85
+ output.push({
86
+ id: `${a.id}-tp${i}`,
87
+ text: a.text,
88
+ vector: Array.from(vec),
89
+ label: `${a.ruleTitle}: ${a.text.slice(0, 80)}`,
90
+ category: a.category,
91
+ severity: a.severity,
92
+ });
93
+ }
94
+ console.log('\n');
95
+ // Save
96
+ mkdirSync(join(OUTPUT_PATH, '..'), { recursive: true });
97
+ writeFileSync(OUTPUT_PATH, JSON.stringify(output, null, 2));
98
+ console.log(`Saved ${output.length} embeddings to ${OUTPUT_PATH}`);
99
+ console.log(`File size: ${(readFileSync(OUTPUT_PATH).length / 1024).toFixed(0)} KB`);
100
+ }
101
+ main().catch((err) => {
102
+ console.error('Fatal:', err);
103
+ process.exit(1);
104
+ });
105
+ //# sourceMappingURL=build-corpus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-corpus.js","sourceRoot":"","sources":["../../src/embedding/build-corpus.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAEhC,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACjF,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC;AAuB5G,KAAK,UAAU,IAAI;IACjB,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,EAAE,CAAC,CAAC;IAEvC,aAAa;IACb,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACtE,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,mBAAmB,EAAE,CAAC;IACxC,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;IACzB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAE7B,6CAA6C;IAC7C,MAAM,OAAO,GAA+F,EAAE,CAAC;IAE/G,SAAS,OAAO,CAAC,GAAW;QAC1B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnC,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,SAAS,SAAS,CAAC,MAAM,cAAc,CAAC,CAAC;IAErD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAgB,CAAC;YAC/C,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc;gBAAE,SAAS;YAE7D,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;gBAChD,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,aAAa,IAAI,EAAE,CAAC,gBAAgB,IAAI,EAAE,CAAC,SAAS,CAAC;gBAChH,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;oBAAE,SAAS;gBAExC,OAAO,CAAC,IAAI,CAAC;oBACX,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;oBACxB,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,SAAS;oBAC1C,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ;oBACnC,SAAS,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE;iBACjC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,CAAC,MAAM,yBAAyB,SAAS,CAAC,MAAM,SAAS,CAAC,CAAC;IAE3F,sBAAsB;IACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAClC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjD,sBAAsB;IACtB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,MAAM,MAAM,GAOP,EAAE,CAAC;IAER,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,KAAK,EAAE,GAAG,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;YAC/C,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;SACrB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAElB,OAAO;IACP,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,MAAM,kBAAkB,WAAW,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACvF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Embedding model loader.
3
+ *
4
+ * Lazy-loads all-MiniLM-L6-v2 via @xenova/transformers (optional dep).
5
+ * Model is ~22MB, cached to disk after first download.
6
+ * Runs in pure JS/WASM -- no native bindings needed.
7
+ *
8
+ * @module agent-threat-rules/embedding/model-loader
9
+ */
10
+ export interface EmbeddingModel {
11
+ /** Encode text to embedding vector */
12
+ encode(text: string): Promise<Float32Array>;
13
+ /** Encode multiple texts (batched) */
14
+ encodeBatch(texts: readonly string[]): Promise<Float32Array[]>;
15
+ /** Initialize / load the model */
16
+ initialize(): Promise<void>;
17
+ /** Model output dimension */
18
+ readonly dimension: number;
19
+ /** Whether model is loaded */
20
+ readonly isLoaded: boolean;
21
+ }
22
+ export declare class TransformersJSModel implements EmbeddingModel {
23
+ readonly dimension = 384;
24
+ private pipeline;
25
+ get isLoaded(): boolean;
26
+ /** Lazy-load the model on first use */
27
+ initialize(): Promise<void>;
28
+ encode(text: string): Promise<Float32Array>;
29
+ encodeBatch(texts: readonly string[]): Promise<Float32Array[]>;
30
+ }
31
+ /** Create a no-op model for testing */
32
+ export declare class MockEmbeddingModel implements EmbeddingModel {
33
+ readonly dimension = 384;
34
+ readonly isLoaded = true;
35
+ private readonly mockVectors;
36
+ constructor(mockVectors?: Map<string, Float32Array>);
37
+ initialize(): Promise<void>;
38
+ encode(text: string): Promise<Float32Array>;
39
+ encodeBatch(texts: readonly string[]): Promise<Float32Array[]>;
40
+ }
41
+ //# sourceMappingURL=model-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-loader.d.ts","sourceRoot":"","sources":["../../src/embedding/model-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5C,sCAAsC;IACtC,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/D,kCAAkC;IAClC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6BAA6B;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8BAA8B;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAKD,qBAAa,mBAAoB,YAAW,cAAc;IACxD,QAAQ,CAAC,SAAS,OAAa;IAC/B,OAAO,CAAC,QAAQ,CAAiB;IAEjC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,uCAAuC;IACjC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ3C,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAYrE;AAED,uCAAuC;AACvC,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,SAAS,OAAa;IAC/B,QAAQ,CAAC,QAAQ,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;gBAE5C,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC;IAI7C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAgB3C,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAGrE"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Embedding model loader.
3
+ *
4
+ * Lazy-loads all-MiniLM-L6-v2 via @xenova/transformers (optional dep).
5
+ * Model is ~22MB, cached to disk after first download.
6
+ * Runs in pure JS/WASM -- no native bindings needed.
7
+ *
8
+ * @module agent-threat-rules/embedding/model-loader
9
+ */
10
+ const MODEL_NAME = 'Xenova/all-MiniLM-L6-v2';
11
+ const DIMENSION = 384;
12
+ export class TransformersJSModel {
13
+ dimension = DIMENSION;
14
+ pipeline = null;
15
+ get isLoaded() {
16
+ return this.pipeline !== null;
17
+ }
18
+ /** Lazy-load the model on first use */
19
+ async initialize() {
20
+ if (this.pipeline)
21
+ return;
22
+ try {
23
+ // Dynamic import to keep @xenova/transformers optional
24
+ const { pipeline } = await import('@xenova/transformers');
25
+ this.pipeline = (await pipeline('feature-extraction', MODEL_NAME, {
26
+ quantized: true,
27
+ }));
28
+ }
29
+ catch (err) {
30
+ const msg = err instanceof Error ? err.message : String(err);
31
+ if (msg.includes('Cannot find module') || msg.includes('MODULE_NOT_FOUND')) {
32
+ throw new Error('Embedding model requires @xenova/transformers. Install: npm install @xenova/transformers');
33
+ }
34
+ throw new Error(`Failed to load embedding model: ${msg}`);
35
+ }
36
+ }
37
+ async encode(text) {
38
+ if (!this.pipeline)
39
+ await this.initialize();
40
+ const pipelineFn = this.pipeline;
41
+ const output = await pipelineFn([text], { pooling: 'mean', normalize: true });
42
+ return new Float32Array(output.data.slice(0, DIMENSION));
43
+ }
44
+ async encodeBatch(texts) {
45
+ if (!this.pipeline)
46
+ await this.initialize();
47
+ const pipelineFn = this.pipeline;
48
+ const results = [];
49
+ // Process one at a time to control memory
50
+ for (const text of texts) {
51
+ const output = await pipelineFn([text], { pooling: 'mean', normalize: true });
52
+ results.push(new Float32Array(output.data.slice(0, DIMENSION)));
53
+ }
54
+ return results;
55
+ }
56
+ }
57
+ /** Create a no-op model for testing */
58
+ export class MockEmbeddingModel {
59
+ dimension = DIMENSION;
60
+ isLoaded = true;
61
+ mockVectors;
62
+ constructor(mockVectors) {
63
+ this.mockVectors = mockVectors ?? new Map();
64
+ }
65
+ async initialize() {
66
+ // No-op for mock
67
+ }
68
+ async encode(text) {
69
+ const existing = this.mockVectors.get(text);
70
+ if (existing)
71
+ return existing;
72
+ // Generate deterministic vector from text hash
73
+ const vec = new Float32Array(DIMENSION);
74
+ for (let i = 0; i < DIMENSION; i++) {
75
+ vec[i] = Math.sin(text.charCodeAt(i % text.length) * (i + 1) * 0.01);
76
+ }
77
+ // Normalize
78
+ let mag = 0;
79
+ for (let i = 0; i < DIMENSION; i++)
80
+ mag += vec[i] * vec[i];
81
+ mag = Math.sqrt(mag);
82
+ for (let i = 0; i < DIMENSION; i++)
83
+ vec[i] /= mag;
84
+ return vec;
85
+ }
86
+ async encodeBatch(texts) {
87
+ return Promise.all(texts.map((t) => this.encode(t)));
88
+ }
89
+ }
90
+ //# sourceMappingURL=model-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-loader.js","sourceRoot":"","sources":["../../src/embedding/model-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * In-memory vector store with cosine similarity search.
3
+ *
4
+ * Stores pre-computed attack embeddings and finds nearest neighbors
5
+ * for incoming text. Sub-millisecond for ~2000 vectors at 384 dimensions.
6
+ *
7
+ * @module agent-threat-rules/embedding/vector-store
8
+ */
9
+ import type { ATRSeverity } from '../types.js';
10
+ export interface VectorEntry {
11
+ readonly id: string;
12
+ readonly vector: Float32Array;
13
+ readonly label: string;
14
+ readonly category: string;
15
+ readonly severity: ATRSeverity;
16
+ }
17
+ export interface SearchResult {
18
+ readonly entry: VectorEntry;
19
+ readonly similarity: number;
20
+ }
21
+ export declare class VectorStore {
22
+ private readonly entries;
23
+ constructor(entries?: readonly VectorEntry[]);
24
+ /** Create new store with additional entries (immutable) */
25
+ withEntries(newEntries: readonly VectorEntry[]): VectorStore;
26
+ /**
27
+ * Find top-K nearest neighbors by cosine similarity.
28
+ * Only returns results above the threshold.
29
+ */
30
+ search(query: Float32Array, topK?: number, threshold?: number): readonly SearchResult[];
31
+ size(): number;
32
+ }
33
+ /** Load pre-computed embeddings from JSON */
34
+ export declare function loadVectorEntries(data: readonly {
35
+ id: string;
36
+ vector: number[];
37
+ label: string;
38
+ category: string;
39
+ severity: string;
40
+ }[]): VectorEntry[];
41
+ //# sourceMappingURL=vector-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-store.d.ts","sourceRoot":"","sources":["../../src/embedding/vector-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE;IAI5C,2DAA2D;IAC3D,WAAW,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW;IAI5D;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,GAAE,MAAU,EAAE,SAAS,GAAE,MAAa,GAAG,SAAS,YAAY,EAAE;IAiBhG,IAAI,IAAI,MAAM;CAGf;AAyBD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,GACnG,WAAW,EAAE,CAQf"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * In-memory vector store with cosine similarity search.
3
+ *
4
+ * Stores pre-computed attack embeddings and finds nearest neighbors
5
+ * for incoming text. Sub-millisecond for ~2000 vectors at 384 dimensions.
6
+ *
7
+ * @module agent-threat-rules/embedding/vector-store
8
+ */
9
+ export class VectorStore {
10
+ entries;
11
+ constructor(entries) {
12
+ this.entries = entries ?? [];
13
+ }
14
+ /** Create new store with additional entries (immutable) */
15
+ withEntries(newEntries) {
16
+ return new VectorStore([...this.entries, ...newEntries]);
17
+ }
18
+ /**
19
+ * Find top-K nearest neighbors by cosine similarity.
20
+ * Only returns results above the threshold.
21
+ */
22
+ search(query, topK = 3, threshold = 0.82) {
23
+ if (this.entries.length === 0)
24
+ return [];
25
+ const results = [];
26
+ for (const entry of this.entries) {
27
+ const sim = cosineSimilarity(query, entry.vector);
28
+ if (sim >= threshold) {
29
+ results.push({ entry, similarity: sim });
30
+ }
31
+ }
32
+ // Sort by similarity descending, take top K
33
+ results.sort((a, b) => b.similarity - a.similarity);
34
+ return results.slice(0, topK);
35
+ }
36
+ size() {
37
+ return this.entries.length;
38
+ }
39
+ }
40
+ /**
41
+ * Cosine similarity between two vectors.
42
+ * Returns value between -1 and 1 (1 = identical direction).
43
+ */
44
+ function cosineSimilarity(a, b) {
45
+ if (a.length !== b.length)
46
+ return 0;
47
+ let dot = 0;
48
+ let magA = 0;
49
+ let magB = 0;
50
+ for (let i = 0; i < a.length; i++) {
51
+ dot += a[i] * b[i];
52
+ magA += a[i] * a[i];
53
+ magB += b[i] * b[i];
54
+ }
55
+ const denom = Math.sqrt(magA) * Math.sqrt(magB);
56
+ if (denom === 0)
57
+ return 0;
58
+ return dot / denom;
59
+ }
60
+ /** Load pre-computed embeddings from JSON */
61
+ export function loadVectorEntries(data) {
62
+ return data.map((d) => ({
63
+ id: d.id,
64
+ vector: new Float32Array(d.vector),
65
+ label: d.label,
66
+ category: d.category,
67
+ severity: (d.severity || 'medium'),
68
+ }));
69
+ }
70
+ //# sourceMappingURL=vector-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-store.js","sourceRoot":"","sources":["../../src/embedding/vector-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiBH,MAAM,OAAO,WAAW;IACL,OAAO,CAAyB;IAEjD,YAAY,OAAgC;QAC1C,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,2DAA2D;IAC3D,WAAW,CAAC,UAAkC;QAC5C,OAAO,IAAI,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAmB,EAAE,OAAe,CAAC,EAAE,YAAoB,IAAI;QACpE,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEzC,MAAM,OAAO,GAAmB,EAAE,CAAC;QAEnC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAClD,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;gBACrB,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAED,4CAA4C;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7B,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,CAAe,EAAE,CAAe;IACxD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IAEpC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACrB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACtB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACxB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE1B,OAAO,GAAG,GAAG,KAAK,CAAC;AACrB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,iBAAiB,CAC/B,IAAoG;IAEpG,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,MAAM,EAAE,IAAI,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC;QAClC,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAgB;KAClD,CAAC,CAAC,CAAC;AACN,CAAC"}
package/dist/engine.d.ts CHANGED
@@ -16,6 +16,9 @@ import type { SessionTracker } from './session-tracker.js';
16
16
  import type { ActionExecutor } from './action-executor.js';
17
17
  import type { SkillFingerprintStore } from './skill-fingerprint.js';
18
18
  import type { SemanticLayerConfig } from './layer-integration.js';
19
+ import type { InvariantChecker } from './tier0-invariant.js';
20
+ import type { BlacklistProvider } from './tier1-blacklist.js';
21
+ import type { EmbeddingModule } from './modules/embedding.js';
19
22
  export interface ATREngineConfig {
20
23
  /** Directory containing ATR rule YAML files */
21
24
  rulesDir?: string;
@@ -23,8 +26,14 @@ export interface ATREngineConfig {
23
26
  rules?: ATRRule[];
24
27
  /** Optional session tracker for behavioral detection across events */
25
28
  sessionTracker?: SessionTracker;
29
+ /** Optional Tier 0: Invariant enforcement (hard boundaries, pre-check) */
30
+ invariantChecker?: InvariantChecker;
31
+ /** Optional Tier 1: Skill blacklist provider (known-bad lookup) */
32
+ blacklistProvider?: BlacklistProvider;
26
33
  /** Optional Layer 2: Skill behavioral fingerprinting (no LLM required) */
27
34
  fingerprintStore?: SkillFingerprintStore;
35
+ /** Optional Tier 2.5: Embedding similarity module (requires @xenova/transformers) */
36
+ embeddingModule?: EmbeddingModule;
28
37
  /** Optional Layer 3: Semantic LLM-as-judge analysis (requires API key) */
29
38
  semanticModule?: SemanticLayerConfig;
30
39
  }
@@ -97,28 +106,22 @@ export declare class ATREngine {
97
106
  /**
98
107
  * Evaluate a sequence condition against the current event.
99
108
  *
100
- * @limitation SINGLE-EVENT ONLY (v0.1)
101
- * This implementation is fundamentally limited: it checks whether patterns
102
- * from multiple sequence steps co-occur within a SINGLE event's content.
103
- *
104
- * What it does NOT do (and claims to via the schema):
105
- * - Does NOT track ordered execution across separate events
106
- * - Does NOT enforce the `within` time window between steps
107
- * - Does NOT respect the `ordered` flag (treats all as unordered)
108
- * - Does NOT correlate steps across different event types
109
- *
110
- * As a result, sequence rules only fire when an attacker's multi-step
111
- * payload appears in one message. Real multi-turn attack chains that
112
- * spread steps across separate events will NOT be detected.
113
- *
114
- * The `matchCount >= 2` threshold is arbitrary and can produce false
115
- * negatives for 2-step sequences (requires both steps in one event)
116
- * and false positives for long sequences (only 2 of N steps needed).
117
- *
118
- * Full session-aware sequence detection is planned for a future version
119
- * and will require integration with SessionTracker.
109
+ * Two modes:
110
+ * 1. Session-aware (when SessionTracker + sessionId available):
111
+ * Checks patterns across historical events in the session.
112
+ * Respects `ordered` flag and `within` time window.
113
+ * 2. Single-event fallback: checks if patterns co-occur in one event.
120
114
  */
121
115
  private evaluateSequenceCondition;
116
+ /**
117
+ * Cross-event sequence detection using SessionTracker.
118
+ * Checks if step patterns have been seen across events in order.
119
+ */
120
+ private evaluateSequenceAcrossSession;
121
+ /**
122
+ * Single-event fallback: check if step patterns co-occur in one event.
123
+ */
124
+ private evaluateSequenceSingleEvent;
122
125
  /**
123
126
  * Resolve a field value from an agent event.
124
127
  */