@weave_protocol/mund 0.5.0 → 0.5.2
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/README.md +28 -0
- package/dist/analyzers/injection-detector.d.ts +7 -0
- package/dist/analyzers/injection-detector.d.ts.map +1 -1
- package/dist/analyzers/injection-detector.js +184 -25
- package/dist/analyzers/injection-detector.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +185 -8
- package/dist/constants.js.map +1 -1
- package/dist/threat-intel-tools.d.ts +3 -3
- package/package.json +15 -15
- package/src/analyzers/injection-detector.ts +181 -31
- package/src/constants.ts +185 -8
- package/tests/analyzers/directives.test.ts +137 -0
- package/tests/analyzers/rules.test.ts +7 -36
- package/tests/fixtures/benign/corpus.json +422 -0
- package/tsconfig.json +1 -0
|
@@ -16,31 +16,49 @@ export class InjectionDetector implements IAnalyzer {
|
|
|
16
16
|
type = 'injection';
|
|
17
17
|
|
|
18
18
|
async analyze(content: string, rules: DetectionRule[]): Promise<SecurityIssue[]> {
|
|
19
|
-
const issues: SecurityIssue[] = [];
|
|
20
19
|
const injectionRules = rules.filter(r => r.type === 'injection' && r.enabled);
|
|
21
20
|
|
|
22
|
-
//
|
|
23
|
-
|
|
21
|
+
// The text as written, plus text an agent may see that a person doesn't:
|
|
22
|
+
// base64 that decodes to prose and invisible Unicode "tag" characters.
|
|
23
|
+
const views: Array<{ text: string; via?: string }> = [{ text: content }];
|
|
24
|
+
for (const decoded of decodeBase64Prose(content)) views.push({ text: decoded, via: 'base64' });
|
|
25
|
+
const tagText = decodeUnicodeTags(content);
|
|
26
|
+
if (tagText) views.push({ text: tagText, via: 'unicode tags' });
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const issues: SecurityIssue[] = [];
|
|
29
|
+
const seen = new Set<string>();
|
|
30
|
+
for (const view of views) {
|
|
31
|
+
for (const issue of this.applyRules(content, view.text, injectionRules, view.via)) {
|
|
32
|
+
// One finding per rule per view position; the same phrase found twice via decoding counts once
|
|
33
|
+
const key = `${issue.rule_id}:${view.via ?? ''}:${issue.location?.start ?? issue.match}`;
|
|
34
|
+
if (!seen.has(key)) { seen.add(key); issues.push(issue); }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
27
37
|
|
|
38
|
+
// Additional heuristic checks
|
|
39
|
+
issues.push(...this.heuristicAnalysis(content));
|
|
40
|
+
return issues;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private applyRules(original: string, text: string, rules: DetectionRule[], via?: string): SecurityIssue[] {
|
|
44
|
+
const issues: SecurityIssue[] = [];
|
|
45
|
+
const normalizedContent = this.normalizeContent(text);
|
|
46
|
+
for (const rule of rules) {
|
|
47
|
+
if (!rule.pattern) continue;
|
|
28
48
|
try {
|
|
29
49
|
const regex = compileRulePattern(rule.pattern, 'gi');
|
|
30
50
|
let match: RegExpExecArray | null;
|
|
31
|
-
|
|
32
51
|
while ((match = regex.exec(normalizedContent)) !== null) {
|
|
52
|
+
if (match[0].length === 0) { regex.lastIndex++; continue; }
|
|
33
53
|
const matchText = match[0];
|
|
34
|
-
const location = this.getLocation(content, match.index, matchText.length);
|
|
35
|
-
|
|
36
54
|
issues.push({
|
|
37
55
|
rule_id: rule.id,
|
|
38
|
-
rule_name: rule.name,
|
|
56
|
+
rule_name: via ? `${rule.name} (hidden in ${via})` : rule.name,
|
|
39
57
|
type: rule.type,
|
|
40
58
|
severity: rule.severity,
|
|
41
59
|
action: rule.action,
|
|
42
60
|
match: this.sanitizeMatch(matchText),
|
|
43
|
-
location,
|
|
61
|
+
location: via ? undefined : this.getLocation(original, match.index, matchText.length),
|
|
44
62
|
suggestion: this.getSuggestion(rule.id)
|
|
45
63
|
});
|
|
46
64
|
}
|
|
@@ -48,11 +66,6 @@ export class InjectionDetector implements IAnalyzer {
|
|
|
48
66
|
console.error(`Invalid regex pattern in rule ${rule.id}:`, error);
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
|
-
|
|
52
|
-
// Additional heuristic checks
|
|
53
|
-
const heuristicIssues = this.heuristicAnalysis(content);
|
|
54
|
-
issues.push(...heuristicIssues);
|
|
55
|
-
|
|
56
69
|
return issues;
|
|
57
70
|
}
|
|
58
71
|
|
|
@@ -61,14 +74,17 @@ export class InjectionDetector implements IAnalyzer {
|
|
|
61
74
|
*/
|
|
62
75
|
private normalizeContent(content: string): string {
|
|
63
76
|
return content
|
|
64
|
-
// Remove zero-width characters
|
|
65
|
-
.replace(/[
|
|
66
|
-
// Normalize Unicode variations
|
|
77
|
+
// Remove zero-width and invisible formatting characters
|
|
78
|
+
.replace(/[-]/g, '')
|
|
79
|
+
// Normalize Unicode variations (fullwidth, ligatures)
|
|
67
80
|
.normalize('NFKC')
|
|
68
|
-
//
|
|
69
|
-
.replace(
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
// Curly quotes read as straight ones ("don’t" → "don't")
|
|
82
|
+
.replace(/[‘’‛′]/g, "'")
|
|
83
|
+
.replace(/[“”‟″]/g, '"')
|
|
84
|
+
// Keep letters in every script (accents included), digits and light punctuation
|
|
85
|
+
.replace(/[^\p{L}\p{N}\s.,!?'"()-]/gu, ' ')
|
|
86
|
+
// Collapse whitespace
|
|
87
|
+
.replace(/\s+/g, ' ');
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
/**
|
|
@@ -77,23 +93,27 @@ export class InjectionDetector implements IAnalyzer {
|
|
|
77
93
|
private heuristicAnalysis(content: string): SecurityIssue[] {
|
|
78
94
|
const issues: SecurityIssue[] = [];
|
|
79
95
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/\[
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
// Role markers. Chat-template tokens never belong in content, so they're
|
|
97
|
+
// high; a plain "System: you are" also opens many legitimate transcripts.
|
|
98
|
+
const roleSwitchPatterns: Array<[RegExp, 'high' | 'low']> = [
|
|
99
|
+
[/<\|\s*(?:im_start|im_end|system|user|assistant|endoftext|start_header_id|end_header_id|eot_id)\s*\|>/i, 'high'],
|
|
100
|
+
[/\[\/?INST\]|<<\/?SYS>>/, 'high'],
|
|
101
|
+
[/\[\s*(?:system|admin|administrator|developer)(?:\s+(?:override|message|prompt|note|instruction)s?)?[^\]\n]{0,30}\]/i, 'high'],
|
|
102
|
+
[/\<\|?system\|?\>/i, 'high'],
|
|
103
|
+
[/\{system_prompt\}/i, 'high'],
|
|
104
|
+
[/^\s*#{1,4}\s*(?:system|instruction)s?\s*:/im, 'high'],
|
|
105
|
+
[/\bsystem\s*:\s*you\s+are/i, 'low'],
|
|
106
|
+
[/\bassistant\s*:\s*i\s+(will|can|am)/i, 'low'],
|
|
87
107
|
];
|
|
88
108
|
|
|
89
|
-
for (const pattern of roleSwitchPatterns) {
|
|
109
|
+
for (const [pattern, severity] of roleSwitchPatterns) {
|
|
90
110
|
const match = content.match(pattern);
|
|
91
111
|
if (match) {
|
|
92
112
|
issues.push({
|
|
93
113
|
rule_id: 'heuristic_role_switch',
|
|
94
114
|
rule_name: 'Role Switching Attempt',
|
|
95
115
|
type: 'injection',
|
|
96
|
-
severity
|
|
116
|
+
severity,
|
|
97
117
|
action: 'alert' as const,
|
|
98
118
|
match: this.sanitizeMatch(match[0]),
|
|
99
119
|
suggestion: 'This content appears to attempt to inject role-switching markers.'
|
|
@@ -101,6 +121,66 @@ export class InjectionDetector implements IAnalyzer {
|
|
|
101
121
|
}
|
|
102
122
|
}
|
|
103
123
|
|
|
124
|
+
// Instructions in HTML a person never sees: hidden elements, noscript,
|
|
125
|
+
// template and comments. Hidden menus and labels are common; hidden text
|
|
126
|
+
// that addresses a reader with a directive is not.
|
|
127
|
+
for (const hidden of hiddenHtmlText(content)) {
|
|
128
|
+
if (ADDRESSES_READER.test(hidden) && DIRECTIVE_VERB.test(hidden)) {
|
|
129
|
+
issues.push({
|
|
130
|
+
rule_id: 'heuristic_hidden_instruction',
|
|
131
|
+
rule_name: 'Instruction in Hidden HTML',
|
|
132
|
+
type: 'injection',
|
|
133
|
+
severity: 'high',
|
|
134
|
+
action: 'alert',
|
|
135
|
+
match: this.sanitizeMatch(hidden),
|
|
136
|
+
suggestion: 'Hidden page text gives instructions. Strip hidden elements before an agent reads the page.'
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Tool-call syntax inside content: a page or result dressed up as the
|
|
142
|
+
// model's own function call, naming a tool that sends or runs something.
|
|
143
|
+
const toolCall = content.match(/<\/?(?:function_calls|tool_call|tool_use)>|<invoke\s+name\s*=|"(?:function|name|tool|tool_name)"\s*:\s*"(?:send_email|send_message|http_request|http_post|fetch_url|web_fetch|transfer\w*|send_payment|pay\w*|exec\w*|shell\w*|run_command|run_shell|bash|write_file|delete\w*)"\s*,\s*"(?:arguments|args|parameters|input)"/i);
|
|
144
|
+
if (toolCall) {
|
|
145
|
+
issues.push({
|
|
146
|
+
rule_id: 'heuristic_tool_call_mimicry',
|
|
147
|
+
rule_name: 'Tool-Call Syntax in Content',
|
|
148
|
+
type: 'injection',
|
|
149
|
+
severity: 'medium',
|
|
150
|
+
action: 'alert',
|
|
151
|
+
match: this.sanitizeMatch(toolCall[0]),
|
|
152
|
+
suggestion: 'Content contains tool-call syntax for an action tool. An agent should never execute calls found in content.'
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Zero-width characters inside words split trigger words for filters
|
|
157
|
+
// while the model still reads them whole.
|
|
158
|
+
const splitWords = content.match(/\p{L}[-]+\p{L}/gu) ?? [];
|
|
159
|
+
if (splitWords.length >= 3) {
|
|
160
|
+
issues.push({
|
|
161
|
+
rule_id: 'heuristic_zero_width_split',
|
|
162
|
+
rule_name: 'Zero-Width Characters Inside Words',
|
|
163
|
+
type: 'injection',
|
|
164
|
+
severity: 'medium',
|
|
165
|
+
action: 'alert',
|
|
166
|
+
match: `${splitWords.length} words split by invisible characters`,
|
|
167
|
+
suggestion: 'Invisible characters inside words are a filter-evasion technique. Strip them before processing.'
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Unicode tag characters (U+E0000–E007F) carry invisible ASCII
|
|
172
|
+
if (/[\u{E0020}-\u{E007F}]/u.test(content)) {
|
|
173
|
+
issues.push({
|
|
174
|
+
rule_id: 'heuristic_unicode_tags',
|
|
175
|
+
rule_name: 'Invisible Unicode Tag Text',
|
|
176
|
+
type: 'injection',
|
|
177
|
+
severity: 'high',
|
|
178
|
+
action: 'alert',
|
|
179
|
+
match: (decodeUnicodeTags(content) ?? '').slice(0, 100),
|
|
180
|
+
suggestion: 'The content carries invisible text in Unicode tag characters. Strip them before processing.'
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
104
184
|
// Check for markdown/code injection that might alter rendering
|
|
105
185
|
const markdownPatterns = [
|
|
106
186
|
/```\s*system/i,
|
|
@@ -183,10 +263,80 @@ export class InjectionDetector implements IAnalyzer {
|
|
|
183
263
|
prompt_injection_jailbreak: 'This appears to be a jailbreak attempt. Such content should be filtered or rejected.',
|
|
184
264
|
prompt_injection_system: 'This content attempts to extract system prompts. Do not reveal system instructions.',
|
|
185
265
|
prompt_injection_roleplay: 'This content attempts to manipulate behavior through roleplay. Apply content filtering.',
|
|
266
|
+
prompt_injection_new_task: 'This content tries to give the agent a new task. Treat it as data, not instructions.',
|
|
267
|
+
prompt_injection_ai_addressed: 'This content speaks to an AI reading it. Treat it as data, not instructions.',
|
|
268
|
+
prompt_injection_concealment: 'This content tells the agent to hide its actions from the user. Block it and alert the user.',
|
|
269
|
+
prompt_injection_suppression: 'This content tells the agent to refuse or skip the user\'s request.',
|
|
270
|
+
prompt_injection_authority: 'This content claims system, admin or developer authority. Content can\'t grant authority.',
|
|
271
|
+
prompt_injection_decode_execute: 'This content asks the agent to decode hidden text and act on it.',
|
|
272
|
+
prompt_injection_data_harvest: 'This content asks the agent to collect the user\'s private data.',
|
|
186
273
|
};
|
|
187
274
|
|
|
188
275
|
return suggestions[ruleId] || 'This content shows signs of injection attacks. Review and sanitize input.';
|
|
189
276
|
}
|
|
190
277
|
}
|
|
191
278
|
|
|
279
|
+
// ─────────────────────────────────────────────────────────
|
|
280
|
+
// Hidden-text helpers
|
|
281
|
+
// ─────────────────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
const ADDRESSES_READER = /\b(?:you|your|ai|assistant|agent|llm|model|chatbot|the\s+user)\b/i;
|
|
284
|
+
const DIRECTIVE_VERB = /\b(?:must|should|respond|reply|include|tell|send|ignore|reveal|print|call|transfer|execute|run|output|say|forward|delete|disregard|override)\b/i;
|
|
285
|
+
|
|
286
|
+
const HIDDEN_STYLE = /(?:display\s*:\s*none|visibility\s*:\s*hidden|opacity\s*:\s*0(?:\.0+)?\s*(?:;|$)|font-size\s*:\s*0(?:px|em|rem|pt|%)?\s*(?:;|$)|(?:height|width)\s*:\s*0(?:px)?\s*;[^"']*overflow\s*:\s*hidden|clip\s*:\s*rect\(0|left\s*:\s*-\d{4,}px)/i;
|
|
287
|
+
|
|
288
|
+
/** Text inside HTML a person doesn't see. */
|
|
289
|
+
export function hiddenHtmlText(html: string): string[] {
|
|
290
|
+
if (!/<[a-z!]/i.test(html)) return [];
|
|
291
|
+
const out: string[] = [];
|
|
292
|
+
const strip = (s: string) => s.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
293
|
+
const element = /<([a-z][a-z0-9]*)\b([^>]*)>([\s\S]*?)<\/\1\s*>/gi;
|
|
294
|
+
let m: RegExpExecArray | null;
|
|
295
|
+
while ((m = element.exec(html)) !== null) {
|
|
296
|
+
const [, tag, attrs, inner] = m;
|
|
297
|
+
const style = /\bstyle\s*=\s*(["'])([\s\S]*?)\1/i.exec(attrs)?.[2] ?? '';
|
|
298
|
+
const hidden = /^(?:noscript|template)$/i.test(tag) || /\baria-hidden\s*=\s*["']?true/i.test(attrs) || /\bhidden(?:\s|=|$)/i.test(attrs) || HIDDEN_STYLE.test(style);
|
|
299
|
+
if (hidden) {
|
|
300
|
+
const text = strip(inner);
|
|
301
|
+
if (text) out.push(text);
|
|
302
|
+
} else if (/<[a-z]/i.test(inner)) {
|
|
303
|
+
// Visible element: keep looking inside it
|
|
304
|
+
out.push(...hiddenHtmlText(inner));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const c of html.matchAll(/<!--([\s\S]*?)-->/g)) {
|
|
308
|
+
const text = c[1].trim();
|
|
309
|
+
if (text) out.push(text);
|
|
310
|
+
}
|
|
311
|
+
return out;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Base64 runs that decode to readable text (not images or binary). */
|
|
315
|
+
export function decodeBase64Prose(content: string): string[] {
|
|
316
|
+
const out: string[] = [];
|
|
317
|
+
for (const m of content.matchAll(/(?<![A-Za-z0-9+/=])[A-Za-z0-9+/]{24,}={0,2}(?![A-Za-z0-9+/=])/g)) {
|
|
318
|
+
// data: URIs carry files, not text
|
|
319
|
+
if (/base64,\s*$/i.test(content.slice(Math.max(0, m.index! - 10), m.index))) continue;
|
|
320
|
+
let text: string;
|
|
321
|
+
try {
|
|
322
|
+
text = typeof atob === 'function' ? atob(m[0]) : Buffer.from(m[0], 'base64').toString('binary');
|
|
323
|
+
} catch {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const printable = text.replace(/[^\x20-\x7E\n\t]/g, '').length / Math.max(text.length, 1);
|
|
327
|
+
if (printable > 0.95 && /[a-z]{2,}\s+[a-z]{2,}/i.test(text)) out.push(text);
|
|
328
|
+
}
|
|
329
|
+
return out;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** ASCII hidden in Unicode tag characters, or undefined. */
|
|
333
|
+
export function decodeUnicodeTags(content: string): string | undefined {
|
|
334
|
+
let out = '';
|
|
335
|
+
for (const ch of content) {
|
|
336
|
+
const cp = ch.codePointAt(0)!;
|
|
337
|
+
if (cp >= 0xe0020 && cp <= 0xe007e) out += String.fromCharCode(cp - 0xe0000);
|
|
338
|
+
}
|
|
339
|
+
return out.trim() || undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
192
342
|
export default InjectionDetector;
|
package/src/constants.ts
CHANGED
|
@@ -408,12 +408,76 @@ export const PII_PATTERNS: DetectionRule[] = [
|
|
|
408
408
|
// ============================================================================
|
|
409
409
|
|
|
410
410
|
export const CODE_PATTERNS: DetectionRule[] = [
|
|
411
|
+
// Requests to touch credentials, logs or privileges. Flagged when they
|
|
412
|
+
// arrive in content an agent will act on; Ward decides whether the call runs.
|
|
413
|
+
{
|
|
414
|
+
id: 'credential_file_access',
|
|
415
|
+
name: 'Credential File Access',
|
|
416
|
+
description: 'Reads, prints or sends a credential or account file (cloud keys, SSH keys, /etc/passwd and shadow, kube or docker config)',
|
|
417
|
+
type: 'code_pattern',
|
|
418
|
+
pattern: String.raw`(?i)\b(?:cat|read|print|show|share|send|upload|open|dump|copy|cp|less|more|head|tail|type|base64|curl|scp)\b[^\n]{0,40}(?:\.aws/credentials|\.ssh/id_[a-z0-9]+\b|/etc/(?:passwd|shadow|sudoers)\b|\.config/gcloud/(?:credentials|application_default)|\.azure/(?:credentials|accessTokens)|\.docker/config\.json|\.kube/config|\.netrc|\.pgpass)`,
|
|
419
|
+
severity: 'medium',
|
|
420
|
+
action: 'alert',
|
|
421
|
+
enabled: true
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
id: 'environment_dump',
|
|
425
|
+
name: 'Environment Dump',
|
|
426
|
+
description: 'Prints or sends every environment variable, where API keys usually live',
|
|
427
|
+
type: 'code_pattern',
|
|
428
|
+
pattern: String.raw`(?i)\b(?:print|dump|show|output|echo|list|run|display|send|log)\b[^\n]{0,20}(?:\bprocess\.env\b(?![.\[])|\bos\.environ\b(?![.\[])|` + '`env`' + String.raw`|\bprintenv\b)`,
|
|
429
|
+
severity: 'medium',
|
|
430
|
+
action: 'alert',
|
|
431
|
+
enabled: true
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
id: 'log_tampering',
|
|
435
|
+
name: 'Security Log Tampering',
|
|
436
|
+
description: 'Deletes or clears audit and authentication logs',
|
|
437
|
+
type: 'code_pattern',
|
|
438
|
+
pattern: String.raw`(?i)\b(?:delete|clean\s+up|clear|truncate|remove|wipe|erase|shred|rm)\b[^\n]{0,40}/var/log/(?:audit|auth|secure|wtmp|btmp|lastlog|syslog)|\bhistory\s+-c\b|\bunset\s+HISTFILE\b`,
|
|
439
|
+
severity: 'medium',
|
|
440
|
+
action: 'alert',
|
|
441
|
+
enabled: true
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
id: 'sql_stacked_destructive',
|
|
445
|
+
name: 'Stacked Destructive SQL',
|
|
446
|
+
description: 'A DROP followed by another statement, or a SELECT from a credential table',
|
|
447
|
+
type: 'code_pattern',
|
|
448
|
+
pattern: String.raw`(?i)\bDROP\s+(?:TABLE|DATABASE)\b[^;\n]*;\s*(?:SELECT|INSERT|UPDATE|DELETE)\b|\bSELECT\s+\*\s+FROM\s+(?:credentials|passwords?|secrets|api_keys|tokens)\b`,
|
|
449
|
+
severity: 'medium',
|
|
450
|
+
action: 'alert',
|
|
451
|
+
enabled: true
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
id: 'privilege_escalation',
|
|
455
|
+
name: 'Privilege Escalation',
|
|
456
|
+
description: 'Adds a user to an admin group, edits sudoers or opens a root shell',
|
|
457
|
+
type: 'code_pattern',
|
|
458
|
+
pattern: String.raw`(?i)\busermod\s+-a?G\s+(?:sudo|wheel|admin|root)\b|\bvisudo\b|>>\s*/etc/sudoers\b|\bsudo\s+(?:-i|-s|su)\b`,
|
|
459
|
+
severity: 'medium',
|
|
460
|
+
action: 'alert',
|
|
461
|
+
enabled: true
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
id: 'remote_database_dump',
|
|
465
|
+
name: 'Remote Database Dump',
|
|
466
|
+
description: 'Dumps a database over SSH',
|
|
467
|
+
type: 'code_pattern',
|
|
468
|
+
pattern: String.raw`(?i)\bssh\s+\S+[^\n]{0,60}\b(?:pg_dump|pg_dumpall|mysqldump|mongodump)\b`,
|
|
469
|
+
severity: 'medium',
|
|
470
|
+
action: 'alert',
|
|
471
|
+
enabled: true
|
|
472
|
+
},
|
|
411
473
|
{
|
|
412
474
|
id: 'shell_injection',
|
|
413
475
|
name: 'Potential Shell Injection',
|
|
414
476
|
description: 'Code that may be vulnerable to shell injection',
|
|
415
477
|
type: 'code_pattern',
|
|
416
|
-
|
|
478
|
+
// exec/system calls that build the command from a variable. (Before 0.5.2 a
|
|
479
|
+
// precedence slip, "...\\$|.*\\{.*\\}", matched any line with braces.)
|
|
480
|
+
pattern: String.raw`(?i)\b(?:child_process\.exec(?:Sync)?|os\.system|os\.popen|subprocess\.(?:call|run|Popen|check_output)|eval|exec)\s*\([^)\n]*(?:\$\{|\$\(|\+\s*[A-Za-z_]|%\s*\(?[A-Za-z_]|\bf["']|\.format\()`,
|
|
417
481
|
severity: 'medium',
|
|
418
482
|
action: 'alert',
|
|
419
483
|
enabled: true
|
|
@@ -485,12 +549,65 @@ export const CODE_PATTERNS: DetectionRule[] = [
|
|
|
485
549
|
// ============================================================================
|
|
486
550
|
|
|
487
551
|
export const INJECTION_PATTERNS: DetectionRule[] = [
|
|
552
|
+
// Patterns run on normalized text: zero-width characters removed, Unicode
|
|
553
|
+
// NFKC, curly quotes straightened, whitespace collapsed, and punctuation
|
|
554
|
+
// other than . , ! ? ' " ( ) - replaced by spaces (so "System:" reads "System").
|
|
488
555
|
{
|
|
489
556
|
id: 'prompt_injection_ignore',
|
|
490
557
|
name: 'Prompt Injection - Ignore Instructions',
|
|
491
|
-
description: '
|
|
558
|
+
description: 'Tells the model to ignore, forget or override its instructions, in English and common European languages',
|
|
559
|
+
type: 'injection',
|
|
560
|
+
pattern: String.raw`(?i)\b(?:ignore|ignoring|disregard|forget|override|bypass|neglect|abandon|discard|vergiss|vergesst|vergessen\s+sie|ignorier\w*|missachte\w*|olvida\w*|olvide|ignora\w*|oublie\w*|ignore[zr]|dimentica\w*|esque[cç]a\w*|desconsider\w*|h[öo]re?\s+nicht\s+auf)\s+(?:\S+\s+){0,4}?(?:(?:previous|prior|preceding|earlier|above|initial|original|existing|provided|given|bisherigen|vorherigen|obigen|vorigen|anteriores|previas|pr[ée]c[ée]dentes|precedenti|anteriori)\s+(?:\S+\s+){0,2}?(?:instructions?|directions?|orders?|rules|guidelines|guardrails|directives?|prompts?|programming|constraints|polic(?:y|ies)|restrictions|safeguards|filters|context|commands?|angaben|anweisungen|auftr[äa]ge|befehle|anordnungen|regeln|instrucciones|[óo]rdenes|reglas|indicaciones|consignes|r[èe]gles|istruzioni|regole|instru[çc][õo]es|regras|ordens)|(?:your|deine|ihre|tus|vos|tue|suas)\s+(?:\S+\s+)?(?:instructions?|directions?|orders?|rules|guidelines|guardrails|directives?|prompts?|programming|constraints|polic(?:y|ies)|restrictions|safeguards|filters|context|commands?|angaben|anweisungen|auftr[äa]ge|befehle|anordnungen|regeln|instrucciones|[óo]rdenes|reglas|indicaciones|consignes|r[èe]gles|istruzioni|regole|instru[çc][õo]es|regras|ordens)|(?:\S+\s+)?(?:instructions?|directions?|orders?|rules|guidelines|guardrails|directives?|prompts?|programming|constraints|polic(?:y|ies)|restrictions|safeguards|filters|context|commands?|angaben|anweisungen|auftr[äa]ge|befehle|anordnungen|regeln|instrucciones|[óo]rdenes|reglas|indicaciones|consignes|r[èe]gles|istruzioni|regole|instru[çc][õo]es|regras|ordens)\s+(?:previous|prior|preceding|earlier|above|initial|original|existing|provided|given|bisherigen|vorherigen|obigen|vorigen|anteriores|previas|pr[ée]c[ée]dentes|precedenti|anteriori)|(?:all|any|every|alle|todas|toutes|tutte)\s+(?:the\s+|die\s+|las\s+|les\s+|le\s+|as\s+)?(?:\S+\s+)?(?:instructions?|directions?|orders?|rules|guidelines|guardrails|directives?|prompts?|programming|constraints|polic(?:y|ies)|restrictions|safeguards|filters|context|commands?|angaben|anweisungen|auftr[äa]ge|befehle|anordnungen|regeln|instrucciones|[óo]rdenes|reglas|indicaciones|consignes|r[èe]gles|istruzioni|regole|instru[çc][õo]es|regras|ordens))\b|\b(?:ignore|ignoring|disregard|forget|override|bypass|neglect|abandon|discard|vergiss|vergesst|vergessen\s+sie|ignorier\w*|missachte\w*|olvida\w*|olvide|ignora\w*|oublie\w*|ignore[zr]|dimentica\w*|esque[cç]a\w*|desconsider\w*|h[öo]re?\s+nicht\s+auf)\s+(?:about\s+)?(?:everything|all|anything|whatever|alles|todo|tout|tutto|tudo),?\s+(?:\S+\s+){0,3}?(?:above|before|so\s+far|until\s+now|up\s+to\s+now|(?:that\s+)?you\s+(?:were|have\s+been|'ve\s+been)\s+(?:told|given|taught)|you\s+(?:know|learned)|i\s+(?:have\s+)?(?:told|said|wrote|asked)|we(?:'ve|\s+have)?\s+(?:talked|discussed|said)|was\s+said|zuvor|davor|bisher|vorher|oben|antes|(?:lo\s+)?que\s+(?:te\s+)?(?:dije|he\s+dicho)|avant|ce\s+que|prima|fino\s+ad\s+ora)\b|\b(?:ignore|ignoring|disregard|forget|override|bypass|neglect|abandon|discard|vergiss|vergesst|vergessen\s+sie|ignorier\w*|missachte\w*|olvida\w*|olvide|ignora\w*|oublie\w*|ignore[zr]|dimentica\w*|esque[cç]a\w*|desconsider\w*|h[öo]re?\s+nicht\s+auf)\s+(?:\S+\s+){0,2}?(?:the\s+above|das\s+obige|what\s+(?:i|we)\s+(?:said|told\s+you|wrote))\s+(?:\S+\s+){0,2}?(?:and|und|y|et|e)\s+(?:instead\s+|stattdessen\s+)?(?:say|sag\w*|print|write|schreib\w*|respond|answer|antwort\w*|tell|output|repeat|di|dis)\b|\bdisregard\s+(?:everything|all|anything)\s+(?:above|before|prior|previously)`,
|
|
561
|
+
severity: 'high',
|
|
562
|
+
action: 'alert',
|
|
563
|
+
enabled: true
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
id: 'prompt_injection_new_task',
|
|
567
|
+
name: 'Prompt Injection - Task Hijack',
|
|
568
|
+
description: 'Gives the model a new task, role or directive that replaces the one it was given',
|
|
569
|
+
type: 'injection',
|
|
570
|
+
pattern: String.raw`(?i)\byour\s+(?:new|real|actual|true|primary|only|updated)\s+(?:primary\s+)?(?:task|instructions?|goal|objective|role|purpose|directive|mission|job)\s+(?:is|are|will\s+be)\b|\bnew\s+(?:primary\s+)?directive\b|\b(?:priority|primary|main)\s+(?:task|goal|objective)\s+is\s+now\b|\bfrom\s+now\s+on,?\s+you\s+(?:are|will|must|shall)\s+(?:going\s+to\s+)?(?:act|be|play|respond|behave|pretend|answer\s+as)\b|\byou\s+are\s+now\s+(?:an?|the)\s+(?:\w+\s+){0,3}(?:assistant|agent|ai|bot|chatbot|model|admin|administrator|sysadmin|root|hacker|character|persona)\b|\byou\s+are\s+now\s+(?:unconstrained|unrestricted|unfiltered|jailbroken|free\s+from|in\s+\w+\s+mode)\b|\bstop\s+(?:the|your|what\s+you're\s+doing\s+on\s+the)\s+(?:previous|current|original)\s+task\b|\bthe\s+task\s+has\s+(?:been\s+)?changed\b|\b(?:now|jetzt|ahora|maintenant|ora)\s+(?:comes?|kommt|kommen|viene|vient|arriva)\s+(?:a\s+|eine\s+|una\s+|une\s+|un\s+)?(?:new|neue|nueva|nouvelle|nuovo|nuova)\s+(?:tasks?|assignments?|aufgaben?|tareas?|t[âa]ches?|compit[oi])\b`,
|
|
571
|
+
severity: 'high',
|
|
572
|
+
action: 'alert',
|
|
573
|
+
enabled: true
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
id: 'prompt_injection_ai_addressed',
|
|
577
|
+
name: 'Prompt Injection - Addressed to the AI',
|
|
578
|
+
description: 'Content that speaks to an AI reading it, a sign it was planted for an agent rather than written for a person',
|
|
579
|
+
type: 'injection',
|
|
580
|
+
pattern: String.raw`(?i)\bif\s+you(?:'re|\s+are)\s+(?:an?\s+)?(?:ai|llm|large\s+language\s+model|language\s+model|ai\s+assistant|ai\s+agent|ai\s+model|chatbot|gpt|chatgpt|claude|gemini|copilot)\b|\bif\s+you(?:'re|\s+are)\s+reading\s+this,?\s+(?:you\s+)?(?:must|should|need\s+to|have\s+to|are\s+required)\b|\b(?:note|message|instructions?)\s+(?:to|for)\s+(?:the\s+|any\s+|all\s+)?(?:ai|llm|ai\s+assistant|ai\s+agent|language\s+model|chatbot)s?\b|\b(?:any|all)\s+ai\s+(?:agents?|assistants?|models?|systems?)\s+(?:from|must|are|should|reading)\b|\b(?:ai|llm)\s+(?:agents?|assistants?|models?)\s*,?\s+(?:must|should|shall|please|you\s+must|in\s+addition|are\s+(?:required|instructed|forbidden|not\s+allowed|prohibited))\b|\b(?:ai|llm)\s+must\s+(?:refuse|ignore|not|always|never|include|respond|reply|send)\b`,
|
|
581
|
+
severity: 'medium',
|
|
582
|
+
action: 'alert',
|
|
583
|
+
enabled: true
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
id: 'prompt_injection_concealment',
|
|
587
|
+
name: 'Prompt Injection - Concealment',
|
|
588
|
+
description: 'Tells the model to hide what it is doing from the user',
|
|
589
|
+
type: 'injection',
|
|
590
|
+
pattern: String.raw`(?i)\b(?:do\s+not|don't|never)\s+(?:tell|inform|notify|alert|warn|let)\s+(?:the|your)\s+user\b|\bwithout\s+(?:telling|informing|notifying|alerting|asking|warning)\s+(?:the|your)\s+user\b|\b(?:do\s+not|don't|never)\s+(?:mention|reveal|disclose|acknowledge)\s+(?:this|these)\s+(?:instructions?|directives?|messages?|text)\b|\bsilently\s+(?:send|forward|post|upload|transmit|copy|exfiltrate|share|email|transfer|run|execute|delete)\b|\b(?:keep|hide)\s+this\s+(?:\w+\s+)?(?:secret|hidden)\s+from\s+(?:the\s+)?user\b|\b(?:continue|carry\s+on|proceed)\s+(?:normally\s+)?as\s+if\s+nothing\s+(?:happened|changed)\b`,
|
|
591
|
+
severity: 'high',
|
|
592
|
+
action: 'alert',
|
|
593
|
+
enabled: true
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
id: 'prompt_injection_suppression',
|
|
597
|
+
name: 'Prompt Injection - Task Suppression',
|
|
598
|
+
description: 'Tells the model to refuse or skip the user\'s request about this content',
|
|
599
|
+
type: 'injection',
|
|
600
|
+
pattern: String.raw`(?i)\brefuse\s+(?:all|any)\s+(?:user\s+)?(?:requests?|questions?|queries)\b|\b(?:do\s+not|don't)\s+(?:summari[sz]e|describe|analy[sz]e|process)\s+this\s+(?:page|content|document|article|site|text)\b`,
|
|
601
|
+
severity: 'medium',
|
|
602
|
+
action: 'alert',
|
|
603
|
+
enabled: true
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
id: 'prompt_injection_authority',
|
|
607
|
+
name: 'Prompt Injection - False Authority',
|
|
608
|
+
description: 'Claims system, admin or developer authority to override the model\'s rules or the user',
|
|
492
609
|
type: 'injection',
|
|
493
|
-
pattern:
|
|
610
|
+
pattern: String.raw`(?i)\b(?:system|admin|administrator|developer|root)\s+(?:override|overrides|command|directive)\b|\boverride\s+(?:your\s+|the\s+|all\s+)?(?:safety|security|content|ethical)\b|\b(?:who|that)\s+(?:deployed|created|built|trained|programmed)\s+you\b|\bstop\s+responding\s+to\s+(?:them|the\s+user)\b`,
|
|
494
611
|
severity: 'high',
|
|
495
612
|
action: 'alert',
|
|
496
613
|
enabled: true
|
|
@@ -498,32 +615,72 @@ export const INJECTION_PATTERNS: DetectionRule[] = [
|
|
|
498
615
|
{
|
|
499
616
|
id: 'prompt_injection_jailbreak',
|
|
500
617
|
name: 'Prompt Injection - Jailbreak Attempt',
|
|
501
|
-
description: '
|
|
618
|
+
description: 'Persona and framing jailbreaks: unrestricted personas, no-ethics claims, developer mode, forced prefixes',
|
|
502
619
|
type: 'injection',
|
|
503
|
-
pattern:
|
|
620
|
+
pattern: String.raw`(?i)\bDAN\s+mode\b|\bdo\s+anything\s+now\b|\bpretend\s+you\s+(?:are|have)\s+no\s+(?:restrictions|limits)\b|\bact\s+as\s+(?:if|an?)\s+(?:unrestricted|unfiltered)\b|\b(?:unfiltered|uncensored|amoral|unrestricted|unconstrained|unaligned|jailbroken)\s+(?:and\s+\w+\s+)?(?:ai|assistant|chatbot|bot|model|mode|version|character|persona|llm)\b|\b(?:no|without|doesn't\s+have\s+any|does\s+not\s+have\s+any|free\s+(?:of|from)|not\s+bound\s+by)\s+(?:any\s+)?(?:ethical|moral|safety|content)(?:\s+or\s+(?:ethical|moral|safety))?\s+(?:guidelines|restrictions|filters|limits|polic(?:y|ies)|principles|constraints|boundaries)\b|\bbypass(?:es|ing)?\s+(?:all\s+|any\s+)?(?:content|safety|ethical)\s+(?:polic(?:y|ies)|filters|guidelines|restrictions)\b|\bnever\s+refuses?\b|\bdeveloper\s+mode\s+(?:enabled|output|response)\b|\bwith\s+developer\s+mode\b|\bstarting\s+with\s+exactly\b|\b(?:start|begin)\s+(?:your\s+)?(?:response|reply|answer)\s+with\s+exactly\b|\b(?:act|pretend|play|roleplay)\s+as\s+my\s+(?:deceased|late|dead)\b`,
|
|
504
621
|
severity: 'high',
|
|
505
622
|
action: 'alert',
|
|
506
623
|
enabled: true
|
|
507
624
|
},
|
|
625
|
+
{
|
|
626
|
+
id: 'prompt_injection_decode_execute',
|
|
627
|
+
name: 'Prompt Injection - Decode and Execute',
|
|
628
|
+
description: 'Asks the model to decode or translate hidden text and then act on it',
|
|
629
|
+
type: 'injection',
|
|
630
|
+
pattern: String.raw`(?i)\b(?:decode|translate|decrypt|deobfuscate|unscramble)\b[^.\n]{0,80}\b(?:and|then)\s+(?:execute|follow|run|obey|perform|carry\s+out|act\s+on)\b`,
|
|
631
|
+
severity: 'high',
|
|
632
|
+
action: 'alert',
|
|
633
|
+
enabled: true
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
id: 'transaction_directive',
|
|
637
|
+
name: 'Transaction Directive',
|
|
638
|
+
description: 'Tells an agent to move money: an amount with a destination account, or a push to process a transfer now',
|
|
639
|
+
type: 'injection',
|
|
640
|
+
pattern: String.raw`(?i)\b(?:transfer|wire|send|pay|remit)\s+(?:\S+\s+){0,3}?(?:[\d][\d,.]*\s*(?:usd|eur|gbp|dollars|euros|btc|eth|usdc|usdt)?|(?:usd|eur|gbp)\s*[\d][\d,.]*)\s+(?:\S+\s+){0,3}?to\s+(?:the\s+)?(?:account|acct|iban|wallet|recipient|routing|beneficiary)\b|\bprocess\s+(?:a|this|the)\s+(?:payment|transfer|transaction|wire)\b[^.\n]{0,60}\bto\s+(?:the\s+)?(?:recipient|account|wallet|iban|beneficiary)\b|\bprocess\s+(?:this|the)\s+(?:transfer|payment|transaction|wire)\s+(?:immediately|now|urgently|right\s+away|asap)\b`,
|
|
641
|
+
severity: 'medium',
|
|
642
|
+
action: 'alert',
|
|
643
|
+
enabled: true
|
|
644
|
+
},
|
|
508
645
|
{
|
|
509
646
|
id: 'prompt_injection_system',
|
|
510
647
|
name: 'Prompt Injection - System Prompt Extraction',
|
|
511
|
-
description: 'Attempts to extract system
|
|
648
|
+
description: 'Attempts to extract the system prompt or the instructions the model was given',
|
|
512
649
|
type: 'injection',
|
|
513
|
-
pattern:
|
|
650
|
+
pattern: String.raw`(?i)\bshow\s+(?:me\s+)?(?:your|the)\s+(?:system\s+)?prompt\b|\bwhat\s+(?:are|is)\s+your\s+(?:instructions?|rules?)\b|\breveal\s+(?:your\s+)?(?:system|hidden)\s+(?:prompt|instructions?)\b|\b(?:repeat|print|output|echo|recite|reproduce|dump|display|write\s+out|return|spell-?check)\s+(?:\S+\s+){0,4}?(?:words|text|everything|content|instructions|messages?|lines)\s+(?:above|preceding)\b|\b(?:reveal|print|repeat|output|show|share|dump|leak|disclose|list|translate|display|give\s+me|tell\s+me|write\s+out|recite|respond\s+with)\s+(?:\S+\s+){0,3}?(?:your|the)\s+(?:(?:full|complete|entire|exact|initial|original|hidden|secret|internal)\s+)*(?:system\s+(?:prompt|message|instructions?)|initial\s+(?:prompt|instructions?)|hidden\s+(?:prompt|instructions?)|pre-?prompt)\b|\b(?:every|all|each|any)\s+(?:[\w-]+,?\s+){0,5}?(?:rules?|constraints?|instructions?|guidelines?),?\s+(?:\w+\s+){0,2}?you\s+(?:have\s+been|'ve\s+been|were)\s+(?:given|told|provided)\b|\b(?:print|show|spell-?check|repeat|output|drucke\w*|zeig\w*|imprime\w*)\s+(?:\S+\s+){0,4}?(?:above|obige\w*|previous|vorherige\w*)\s+(?:prompt|prompts|text|instructions|eingabeaufforderung|anweisungen|texte?s?)\b|\b(?:show|zeig\w*|give|list|print|reveal)\s+(?:me\s+|mir\s+)?(?:all\s+|alle\s+)?(?:of\s+)?(?:your|deine|ihre)\s+(?:prompt\s+texts?|prompts|anweisungen|initial\s+instructions)\b`,
|
|
514
651
|
severity: 'medium',
|
|
515
652
|
action: 'alert',
|
|
516
653
|
enabled: true
|
|
517
654
|
},
|
|
655
|
+
{
|
|
656
|
+
id: 'prompt_injection_data_harvest',
|
|
657
|
+
name: 'Prompt Injection - User Data Harvest',
|
|
658
|
+
description: 'Asks the model to collect or output the user\'s private data',
|
|
659
|
+
type: 'injection',
|
|
660
|
+
pattern: String.raw`(?i)\b(?:respond|reply|include|output|print|list|share|extract|collect|harvest|gather|reveal|send)\s+(?:with\s+)?(?:\S+\s+){0,3}?(?:the\s+)?(?:user's|users'|their)\s+(?:\w+\s+){0,2}?(?:conversation\s+history|chat\s+history|messages|e-?mail\s+addresses|location|credentials|passwords?|session|cookies|account\s+details|contacts)\b`,
|
|
661
|
+
severity: 'high',
|
|
662
|
+
action: 'alert',
|
|
663
|
+
enabled: true
|
|
664
|
+
},
|
|
518
665
|
{
|
|
519
666
|
id: 'prompt_injection_roleplay',
|
|
520
667
|
name: 'Prompt Injection - Malicious Roleplay',
|
|
521
668
|
description: 'Roleplay instructions that may bypass safety',
|
|
522
669
|
type: 'injection',
|
|
523
|
-
pattern:
|
|
670
|
+
pattern: String.raw`(?i)\byou\s+are\s+now\s+(?:evil|malicious|unrestricted)\b|\broleplay\s+as\s+(?:a\s+)?(?:hacker|criminal)\b|\bpretend\s+(?:to\s+be|you\s+are)\s+(?:a\s+)?(?:virus|malware)\b`,
|
|
524
671
|
severity: 'medium',
|
|
525
672
|
action: 'alert',
|
|
526
673
|
enabled: true
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
id: 'prompt_injection_recon',
|
|
677
|
+
name: 'Prompt Injection - Policy Reconnaissance',
|
|
678
|
+
description: 'Probes for the model\'s permissions or its reasoning trace (common, so low severity)',
|
|
679
|
+
type: 'injection',
|
|
680
|
+
pattern: String.raw`(?i)\bwhat\s+(?:tools|capabilities|urls?|url\s+patterns|domains|commands|actions|files|paths)\b[^?.]{0,60}\b(?:are\s+you|you\s+are)\s+(?:\w+\s+)?(?:allowed|permitted|denied|forbidden|not\s+allowed|blocked)\b|\b(?:output|reveal|print|show|share|dump|expose)\s+(?:me\s+)?your\s+(?:(?:full|raw|hidden|internal|private)\s+)*(?:reasoning\s+trace|chain[\s-]of[\s-]thought|hidden\s+reasoning|internal\s+reasoning)\b|\b(?:stay|remain)\s+in\s+character\b`,
|
|
681
|
+
severity: 'low',
|
|
682
|
+
action: 'log',
|
|
683
|
+
enabled: true
|
|
527
684
|
}
|
|
528
685
|
];
|
|
529
686
|
|
|
@@ -532,6 +689,26 @@ export const INJECTION_PATTERNS: DetectionRule[] = [
|
|
|
532
689
|
// ============================================================================
|
|
533
690
|
|
|
534
691
|
export const EXFILTRATION_PATTERNS: DetectionRule[] = [
|
|
692
|
+
{
|
|
693
|
+
id: 'templated_exfil_url',
|
|
694
|
+
name: 'Templated Exfiltration URL',
|
|
695
|
+
description: 'A URL with a placeholder for session, token, conversation or other private data in its query or hostname, for an agent to fill in and fetch',
|
|
696
|
+
type: 'exfiltration',
|
|
697
|
+
pattern: String.raw`(?i)https?://[^\s"'<>]*[?&][\w.-]*=\{\s*(?!your_)[A-Za-z_]*(?:session|token|email|context|conversation|secret|api_?key|password|history|cookie|credential|user_data)[A-Za-z_]*\s*\}|\{\s*[A-Za-z_]*(?:email|token|secret|session|password|api_?key|context|conversation|cookie|credential)[A-Za-z_]*\s*\}\.[a-z0-9-]+\.[a-z0-9.-]*[a-z]{2,}`,
|
|
698
|
+
severity: 'high',
|
|
699
|
+
action: 'alert',
|
|
700
|
+
enabled: true
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
id: 'exfiltration_directive',
|
|
704
|
+
name: 'Exfiltration Directive',
|
|
705
|
+
description: 'Text telling an agent to send credentials, session data or other private data to a URL',
|
|
706
|
+
type: 'exfiltration',
|
|
707
|
+
pattern: String.raw`(?i)\b(?:send|forward|post|upload|transmit|exfiltrate|leak|e-?mail|submit|report)\b[^\n]{0,80}?\b(?:credentials?|session(?:\s+(?:info|data|tokens?|cookies?))?|cookies?|tokens?|passwords?|(?:conversation|chat|browsing)\s+history|(?:our|the|this)\s+conversation|location|e-?mail\s+addresses|api\s+keys?|secrets?|personal\s+(?:data|information)|contacts|private\s+keys?)\b[^\n]{0,80}?(?:https?://|\b[\w.+-]+@[\w-]+\.[\w.-]+)|\b(?:send|e-?mail|forward|mail)\b[^\n]{0,30}?\b[\w.+-]+@[\w-]+\.[\w.-]+[^\n]{0,40}?\b(?:conversation|chat\s+history|credentials|session|passwords?|private\s+keys?|api\s+keys?|ssh)\b`,
|
|
708
|
+
severity: 'high',
|
|
709
|
+
action: 'alert',
|
|
710
|
+
enabled: true
|
|
711
|
+
},
|
|
535
712
|
{
|
|
536
713
|
id: 'suspicious_url_post',
|
|
537
714
|
name: 'Suspicious Data POST',
|