n8n-nodes-redactor 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +42 -0
  2. package/README.dev.md +443 -0
  3. package/README.md +443 -0
  4. package/README.npm.md +443 -0
  5. package/dist/nodes/PiiRedactor/PiiRedactor.node.d.ts +5 -0
  6. package/dist/nodes/PiiRedactor/PiiRedactor.node.js +1093 -0
  7. package/dist/nodes/PiiRedactor/__tests__/encryption.test.d.ts +1 -0
  8. package/dist/nodes/PiiRedactor/__tests__/encryption.test.js +200 -0
  9. package/dist/nodes/PiiRedactor/__tests__/engine.test.d.ts +1 -0
  10. package/dist/nodes/PiiRedactor/__tests__/engine.test.js +524 -0
  11. package/dist/nodes/PiiRedactor/__tests__/operations.test.d.ts +1 -0
  12. package/dist/nodes/PiiRedactor/__tests__/operations.test.js +316 -0
  13. package/dist/nodes/PiiRedactor/__tests__/patterns-global.test.d.ts +1 -0
  14. package/dist/nodes/PiiRedactor/__tests__/patterns-global.test.js +427 -0
  15. package/dist/nodes/PiiRedactor/__tests__/patterns.test.d.ts +1 -0
  16. package/dist/nodes/PiiRedactor/__tests__/patterns.test.js +481 -0
  17. package/dist/nodes/PiiRedactor/__tests__/phase1.test.d.ts +1 -0
  18. package/dist/nodes/PiiRedactor/__tests__/phase1.test.js +343 -0
  19. package/dist/nodes/PiiRedactor/__tests__/phase3.test.d.ts +1 -0
  20. package/dist/nodes/PiiRedactor/__tests__/phase3.test.js +275 -0
  21. package/dist/nodes/PiiRedactor/__tests__/phase4.test.d.ts +1 -0
  22. package/dist/nodes/PiiRedactor/__tests__/phase4.test.js +184 -0
  23. package/dist/nodes/PiiRedactor/__tests__/presidio.test.d.ts +1 -0
  24. package/dist/nodes/PiiRedactor/__tests__/presidio.test.js +170 -0
  25. package/dist/nodes/PiiRedactor/__tests__/security.test.d.ts +1 -0
  26. package/dist/nodes/PiiRedactor/__tests__/security.test.js +178 -0
  27. package/dist/nodes/PiiRedactor/__tests__/semantic.test.d.ts +1 -0
  28. package/dist/nodes/PiiRedactor/__tests__/semantic.test.js +319 -0
  29. package/dist/nodes/PiiRedactor/__tests__/vault.test.d.ts +1 -0
  30. package/dist/nodes/PiiRedactor/__tests__/vault.test.js +247 -0
  31. package/dist/nodes/PiiRedactor/audit.d.ts +48 -0
  32. package/dist/nodes/PiiRedactor/audit.js +192 -0
  33. package/dist/nodes/PiiRedactor/classification.d.ts +33 -0
  34. package/dist/nodes/PiiRedactor/classification.js +118 -0
  35. package/dist/nodes/PiiRedactor/context.d.ts +57 -0
  36. package/dist/nodes/PiiRedactor/context.js +260 -0
  37. package/dist/nodes/PiiRedactor/encryption.d.ts +45 -0
  38. package/dist/nodes/PiiRedactor/encryption.js +158 -0
  39. package/dist/nodes/PiiRedactor/engine.d.ts +23 -0
  40. package/dist/nodes/PiiRedactor/engine.js +888 -0
  41. package/dist/nodes/PiiRedactor/injection.d.ts +46 -0
  42. package/dist/nodes/PiiRedactor/injection.js +425 -0
  43. package/dist/nodes/PiiRedactor/names.d.ts +28 -0
  44. package/dist/nodes/PiiRedactor/names.js +1677 -0
  45. package/dist/nodes/PiiRedactor/patterns.d.ts +17 -0
  46. package/dist/nodes/PiiRedactor/patterns.js +1742 -0
  47. package/dist/nodes/PiiRedactor/presidio.d.ts +77 -0
  48. package/dist/nodes/PiiRedactor/presidio.js +264 -0
  49. package/dist/nodes/PiiRedactor/profiles.d.ts +47 -0
  50. package/dist/nodes/PiiRedactor/profiles.js +139 -0
  51. package/dist/nodes/PiiRedactor/pseudonymize.d.ts +20 -0
  52. package/dist/nodes/PiiRedactor/pseudonymize.js +203 -0
  53. package/dist/nodes/PiiRedactor/redact.png +0 -0
  54. package/dist/nodes/PiiRedactor/redact.svg +3 -0
  55. package/dist/nodes/PiiRedactor/ropa.d.ts +63 -0
  56. package/dist/nodes/PiiRedactor/ropa.js +70 -0
  57. package/dist/nodes/PiiRedactor/types.d.ts +82 -0
  58. package/dist/nodes/PiiRedactor/types.js +3 -0
  59. package/dist/nodes/PiiRedactor/vault.d.ts +61 -0
  60. package/dist/nodes/PiiRedactor/vault.js +352 -0
  61. package/package.json +87 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Prompt Injection Detection Engine
3
+ *
4
+ * Detects prompt injection attempts in text before it reaches an LLM.
5
+ * Based on OWASP LLM Top 10 (2025), LLM Guard, Rebuff, and Lakera Guard.
6
+ *
7
+ * Categories:
8
+ * 1. Direct instruction override ("ignore previous instructions")
9
+ * 2. Role manipulation / jailbreak ("you are now DAN")
10
+ * 3. System prompt extraction ("reveal your system prompt")
11
+ * 4. Delimiter / context escape ([SYSTEM], <<SYS>>)
12
+ * 5. Encoded payloads (base64, URL-encoded, Unicode)
13
+ * 6. Zero-width character injection
14
+ *
15
+ * Response actions:
16
+ * BLOCK (score >= 0.7) - High confidence injection
17
+ * FLAG (score >= 0.3) - Suspicious, review recommended
18
+ * LOG_ONLY (score < 0.3) - Low-confidence match
19
+ */
20
+ export type InjectionAction = 'BLOCK' | 'FLAG' | 'LOG_ONLY' | 'CLEAN';
21
+ export type InjectionSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
22
+ export interface InjectionMatch {
23
+ category: string;
24
+ pattern: string;
25
+ matchedText: string;
26
+ severity: InjectionSeverity;
27
+ score: number;
28
+ }
29
+ export interface PromptInjectionResult {
30
+ detected: boolean;
31
+ score: number;
32
+ action: InjectionAction;
33
+ matchedPatterns: InjectionMatch[];
34
+ encodedPayloadsFound: boolean;
35
+ zeroWidthCharsFound: boolean;
36
+ message: string;
37
+ }
38
+ /**
39
+ * Detect prompt injection attempts in text.
40
+ */
41
+ export declare function detectPromptInjection(text: string): PromptInjectionResult;
42
+ /**
43
+ * Scan all string values in a data object for prompt injection.
44
+ * Returns the highest-severity result across all fields.
45
+ */
46
+ export declare function scanForInjection(data: Record<string, unknown>, currentPath?: string): PromptInjectionResult;
@@ -0,0 +1,425 @@
1
+ "use strict";
2
+ /**
3
+ * Prompt Injection Detection Engine
4
+ *
5
+ * Detects prompt injection attempts in text before it reaches an LLM.
6
+ * Based on OWASP LLM Top 10 (2025), LLM Guard, Rebuff, and Lakera Guard.
7
+ *
8
+ * Categories:
9
+ * 1. Direct instruction override ("ignore previous instructions")
10
+ * 2. Role manipulation / jailbreak ("you are now DAN")
11
+ * 3. System prompt extraction ("reveal your system prompt")
12
+ * 4. Delimiter / context escape ([SYSTEM], <<SYS>>)
13
+ * 5. Encoded payloads (base64, URL-encoded, Unicode)
14
+ * 6. Zero-width character injection
15
+ *
16
+ * Response actions:
17
+ * BLOCK (score >= 0.7) - High confidence injection
18
+ * FLAG (score >= 0.3) - Suspicious, review recommended
19
+ * LOG_ONLY (score < 0.3) - Low-confidence match
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.detectPromptInjection = detectPromptInjection;
23
+ exports.scanForInjection = scanForInjection;
24
+ const INJECTION_PATTERNS = [
25
+ // ═══════════════════════════════════════
26
+ // Category 1: Direct Instruction Override
27
+ // ═══════════════════════════════════════
28
+ {
29
+ category: 'instruction_override',
30
+ regex: /ignore\s+(all\s+)?previous\s+(instructions?|prompts?|rules?|guidelines?|context)/i,
31
+ severity: 'CRITICAL',
32
+ score: 0.45,
33
+ description: 'Attempt to override previous instructions',
34
+ },
35
+ {
36
+ category: 'instruction_override',
37
+ regex: /disregard\s+(all\s+)?(previous|above|prior|earlier)\s+(instructions?|context|prompts?|rules?)/i,
38
+ severity: 'CRITICAL',
39
+ score: 0.45,
40
+ description: 'Attempt to disregard prior context',
41
+ },
42
+ {
43
+ category: 'instruction_override',
44
+ regex: /forget\s+(all\s+)?(previous|above|prior|everything)\s*(you|instructions?|prompts?|said)?/i,
45
+ severity: 'HIGH',
46
+ score: 0.40,
47
+ description: 'Attempt to make model forget instructions',
48
+ },
49
+ {
50
+ category: 'instruction_override',
51
+ regex: /override\s+(all\s+)?(previous|system|safety)\s*(instructions?|settings?|rules?|filters?)?/i,
52
+ severity: 'CRITICAL',
53
+ score: 0.45,
54
+ description: 'Direct override attempt',
55
+ },
56
+ {
57
+ category: 'instruction_override',
58
+ regex: /new\s+instructions?\s*:/i,
59
+ severity: 'HIGH',
60
+ score: 0.35,
61
+ description: 'Injecting new instructions',
62
+ },
63
+ {
64
+ category: 'instruction_override',
65
+ regex: /you\s+must\s+(now\s+)?follow\s+(these|my|new)\s+instructions?/i,
66
+ severity: 'HIGH',
67
+ score: 0.40,
68
+ description: 'Commanding model to follow new instructions',
69
+ },
70
+ // ═══════════════════════════════════════
71
+ // Category 2: Role Manipulation / Jailbreak
72
+ // ═══════════════════════════════════════
73
+ {
74
+ category: 'role_manipulation',
75
+ regex: /you\s+are\s+now\s+(in\s+)?(developer|debug|admin|god|unrestricted|DAN|evil)\s*(mode)?/i,
76
+ severity: 'CRITICAL',
77
+ score: 0.45,
78
+ description: 'Role manipulation - DAN/developer mode',
79
+ },
80
+ {
81
+ category: 'role_manipulation',
82
+ regex: /act\s+as\s+(a\s+)?(different|new|unrestricted|unfiltered|evil)\s*(AI|assistant|model|system)?/i,
83
+ severity: 'HIGH',
84
+ score: 0.35,
85
+ description: 'Role manipulation - act as different entity',
86
+ },
87
+ {
88
+ category: 'role_manipulation',
89
+ regex: /pretend\s+(you\s+are|to\s+be)\s+(a\s+)?(hacker|evil|unrestricted|jailbroken|unfiltered)/i,
90
+ severity: 'HIGH',
91
+ score: 0.40,
92
+ description: 'Role manipulation - pretend to be jailbroken',
93
+ },
94
+ {
95
+ category: 'role_manipulation',
96
+ regex: /enter\s+(developer|debug|admin|sudo|root|maintenance|god)\s*mode/i,
97
+ severity: 'CRITICAL',
98
+ score: 0.45,
99
+ description: 'Attempt to enter privileged mode',
100
+ },
101
+ {
102
+ category: 'role_manipulation',
103
+ regex: /switch\s+to\s+(unrestricted|unfiltered|uncensored|raw)\s*mode/i,
104
+ severity: 'HIGH',
105
+ score: 0.40,
106
+ description: 'Attempt to switch to unrestricted mode',
107
+ },
108
+ {
109
+ category: 'role_manipulation',
110
+ regex: /you\s+have\s+no\s+(restrictions?|limitations?|rules?|boundaries|filters?|guardrails?)/i,
111
+ severity: 'HIGH',
112
+ score: 0.35,
113
+ description: 'Claiming model has no restrictions',
114
+ },
115
+ {
116
+ category: 'role_manipulation',
117
+ regex: /do\s+anything\s+now/i,
118
+ severity: 'HIGH',
119
+ score: 0.40,
120
+ description: 'DAN (Do Anything Now) jailbreak',
121
+ },
122
+ {
123
+ category: 'role_manipulation',
124
+ regex: /jailbreak/i,
125
+ severity: 'HIGH',
126
+ score: 0.35,
127
+ description: 'Explicit jailbreak mention',
128
+ },
129
+ // ═══════════════════════════════════════
130
+ // Category 3: System Prompt Extraction
131
+ // ═══════════════════════════════════════
132
+ {
133
+ category: 'prompt_extraction',
134
+ regex: /reveal\s+(your\s+)?(system\s+)?prompt/i,
135
+ severity: 'HIGH',
136
+ score: 0.35,
137
+ description: 'Attempt to reveal system prompt',
138
+ },
139
+ {
140
+ category: 'prompt_extraction',
141
+ regex: /show\s+(me\s+)?(your\s+)?(system\s+)?(prompt|instructions?|rules?|guidelines?|configuration)/i,
142
+ severity: 'MEDIUM',
143
+ score: 0.30,
144
+ description: 'Attempt to show system instructions',
145
+ },
146
+ {
147
+ category: 'prompt_extraction',
148
+ regex: /what\s+(are|is)\s+your\s+(system\s+)?(prompt|instructions?|rules?|guidelines?|initial\s+instructions?)/i,
149
+ severity: 'MEDIUM',
150
+ score: 0.25,
151
+ description: 'Asking for system prompt contents',
152
+ },
153
+ {
154
+ category: 'prompt_extraction',
155
+ regex: /repeat\s+(your\s+)?(system\s+)?(prompt|instructions?|initial\s+instructions?)\s*(verbatim|exactly|word\s+for\s+word)?/i,
156
+ severity: 'HIGH',
157
+ score: 0.35,
158
+ description: 'Asking to repeat system prompt verbatim',
159
+ },
160
+ {
161
+ category: 'prompt_extraction',
162
+ regex: /output\s+(your\s+)?(system\s+)?(prompt|instructions?|configuration)\s*(verbatim|exactly|in\s+full)?/i,
163
+ severity: 'HIGH',
164
+ score: 0.35,
165
+ description: 'Asking to output system configuration',
166
+ },
167
+ // ═══════════════════════════════════════
168
+ // Category 4: Delimiter / Context Escape
169
+ // ═══════════════════════════════════════
170
+ {
171
+ category: 'delimiter_escape',
172
+ regex: /\[SYSTEM\]/i,
173
+ severity: 'CRITICAL',
174
+ score: 0.50,
175
+ description: 'System delimiter injection [SYSTEM]',
176
+ },
177
+ {
178
+ category: 'delimiter_escape',
179
+ regex: /\[INST\]/i,
180
+ severity: 'CRITICAL',
181
+ score: 0.50,
182
+ description: 'Instruction delimiter injection [INST]',
183
+ },
184
+ {
185
+ category: 'delimiter_escape',
186
+ regex: /<<SYS>>/i,
187
+ severity: 'CRITICAL',
188
+ score: 0.50,
189
+ description: 'Llama system delimiter <<SYS>>',
190
+ },
191
+ {
192
+ category: 'delimiter_escape',
193
+ regex: /<\|im_start\|>system/i,
194
+ severity: 'CRITICAL',
195
+ score: 0.50,
196
+ description: 'ChatML system delimiter injection',
197
+ },
198
+ {
199
+ category: 'delimiter_escape',
200
+ regex: /```system\b/i,
201
+ severity: 'HIGH',
202
+ score: 0.40,
203
+ description: 'Code block system injection',
204
+ },
205
+ {
206
+ category: 'delimiter_escape',
207
+ regex: /###\s*(SYSTEM|INSTRUCTION|END|OVERRIDE)/i,
208
+ severity: 'HIGH',
209
+ score: 0.40,
210
+ description: 'Markdown heading delimiter injection',
211
+ },
212
+ {
213
+ category: 'delimiter_escape',
214
+ regex: /---\s*(new|system|override)\s*(context|instructions?|prompt)?\s*---/i,
215
+ severity: 'HIGH',
216
+ score: 0.40,
217
+ description: 'Horizontal rule context break',
218
+ },
219
+ {
220
+ category: 'delimiter_escape',
221
+ regex: /BEGINNING\s+OF\s+(NEW\s+)?CONVERSATION/i,
222
+ severity: 'HIGH',
223
+ score: 0.35,
224
+ description: 'Fake conversation reset',
225
+ },
226
+ {
227
+ category: 'delimiter_escape',
228
+ regex: /END\s+OF\s+(SYSTEM\s+)?(PROMPT|INSTRUCTIONS?|CONTEXT)/i,
229
+ severity: 'HIGH',
230
+ score: 0.40,
231
+ description: 'Fake end-of-prompt marker',
232
+ },
233
+ ];
234
+ /** Zero-width and invisible Unicode characters used for evasion */
235
+ const ZERO_WIDTH_RE = /[\u200B\u200C\u200D\uFEFF\u00AD\u2060\u2061\u2062\u2063\u2064\u2066\u2067\u2068\u2069\u206A\u206B\u206C\u206D\u206E\u206F]/g;
236
+ /** Detect Base64-encoded strings that might contain injection payloads */
237
+ const BASE64_SUSPICIOUS_RE = /[A-Za-z0-9+\/]{40,}={0,2}/g;
238
+ /** Common injection seeds for Base64 decoding check */
239
+ const INJECTION_SEEDS = [
240
+ 'ignore', 'override', 'system', 'prompt', 'instruction', 'jailbreak',
241
+ 'developer mode', 'DAN', 'bypass', 'unrestricted',
242
+ ];
243
+ /**
244
+ * Detect prompt injection attempts in text.
245
+ */
246
+ function detectPromptInjection(text) {
247
+ if (!text || text.trim().length === 0) {
248
+ return {
249
+ detected: false,
250
+ score: 0,
251
+ action: 'CLEAN',
252
+ matchedPatterns: [],
253
+ encodedPayloadsFound: false,
254
+ zeroWidthCharsFound: false,
255
+ message: 'No text to analyze.',
256
+ };
257
+ }
258
+ const matches = [];
259
+ let totalScore = 0;
260
+ // Check regex patterns
261
+ for (const pattern of INJECTION_PATTERNS) {
262
+ const re = new RegExp(pattern.regex.source, pattern.regex.flags);
263
+ const match = re.exec(text);
264
+ if (match) {
265
+ matches.push({
266
+ category: pattern.category,
267
+ pattern: pattern.description,
268
+ matchedText: match[0].slice(0, 100), // Truncate long matches
269
+ severity: pattern.severity,
270
+ score: pattern.score,
271
+ });
272
+ totalScore += pattern.score;
273
+ }
274
+ }
275
+ // Check for zero-width characters
276
+ const zeroWidthCharsFound = ZERO_WIDTH_RE.test(text);
277
+ if (zeroWidthCharsFound) {
278
+ totalScore += 0.20;
279
+ matches.push({
280
+ category: 'zero_width_chars',
281
+ pattern: 'Invisible Unicode characters detected',
282
+ matchedText: '(hidden characters)',
283
+ severity: 'MEDIUM',
284
+ score: 0.20,
285
+ });
286
+ }
287
+ // Check for suspicious Base64 payloads
288
+ let encodedPayloadsFound = false;
289
+ const base64Matches = text.match(BASE64_SUSPICIOUS_RE);
290
+ if (base64Matches) {
291
+ for (const b64 of base64Matches.slice(0, 5)) { // Check max 5 to avoid perf issues
292
+ try {
293
+ const decoded = Buffer.from(b64, 'base64').toString('utf-8');
294
+ const decodedLower = decoded.toLowerCase();
295
+ if (INJECTION_SEEDS.some((seed) => decodedLower.includes(seed))) {
296
+ encodedPayloadsFound = true;
297
+ totalScore += 0.35;
298
+ matches.push({
299
+ category: 'encoded_payload',
300
+ pattern: 'Base64-encoded injection payload detected',
301
+ matchedText: `Decoded: "${decoded.slice(0, 50)}..."`,
302
+ severity: 'HIGH',
303
+ score: 0.35,
304
+ });
305
+ break;
306
+ }
307
+ }
308
+ catch {
309
+ // Not valid base64, skip
310
+ }
311
+ }
312
+ }
313
+ // Cap score at 1.0
314
+ totalScore = Math.min(1.0, totalScore);
315
+ totalScore = Math.round(totalScore * 100) / 100;
316
+ // Determine action
317
+ let action = 'CLEAN';
318
+ if (totalScore >= 0.7)
319
+ action = 'BLOCK';
320
+ else if (totalScore >= 0.3)
321
+ action = 'FLAG';
322
+ else if (matches.length > 0)
323
+ action = 'LOG_ONLY';
324
+ // Build message
325
+ let message;
326
+ if (!matches.length) {
327
+ message = 'No prompt injection detected.';
328
+ }
329
+ else if (action === 'BLOCK') {
330
+ message = `BLOCKED: High-confidence prompt injection detected (score: ${totalScore}). ${matches.length} suspicious pattern(s) found.`;
331
+ }
332
+ else if (action === 'FLAG') {
333
+ message = `FLAGGED: Potential prompt injection detected (score: ${totalScore}). Review recommended.`;
334
+ }
335
+ else {
336
+ message = `Low-confidence match (score: ${totalScore}). Likely safe but logged for review.`;
337
+ }
338
+ return {
339
+ detected: matches.length > 0,
340
+ score: totalScore,
341
+ action,
342
+ matchedPatterns: matches,
343
+ encodedPayloadsFound,
344
+ zeroWidthCharsFound,
345
+ message,
346
+ };
347
+ }
348
+ /**
349
+ * Scan all string values in a data object for prompt injection.
350
+ * Returns the highest-severity result across all fields.
351
+ */
352
+ function scanForInjection(data, currentPath = '') {
353
+ let worstResult = {
354
+ detected: false,
355
+ score: 0,
356
+ action: 'CLEAN',
357
+ matchedPatterns: [],
358
+ encodedPayloadsFound: false,
359
+ zeroWidthCharsFound: false,
360
+ message: 'No prompt injection detected.',
361
+ };
362
+ for (const [key, value] of Object.entries(data)) {
363
+ const fieldPath = currentPath ? `${currentPath}.${key}` : key;
364
+ if (typeof value === 'string' && value.trim().length > 0) {
365
+ const result = detectPromptInjection(value);
366
+ if (result.score > worstResult.score) {
367
+ // Add field path to each match
368
+ worstResult = {
369
+ ...result,
370
+ matchedPatterns: result.matchedPatterns.map((m) => ({
371
+ ...m,
372
+ pattern: `[${fieldPath}] ${m.pattern}`,
373
+ })),
374
+ };
375
+ }
376
+ else if (result.detected) {
377
+ // Merge matches from lower-scoring fields
378
+ worstResult.matchedPatterns.push(...result.matchedPatterns.map((m) => ({
379
+ ...m,
380
+ pattern: `[${fieldPath}] ${m.pattern}`,
381
+ })));
382
+ worstResult.score = Math.min(1.0, worstResult.score + result.score * 0.3);
383
+ worstResult.detected = true;
384
+ if (result.encodedPayloadsFound)
385
+ worstResult.encodedPayloadsFound = true;
386
+ if (result.zeroWidthCharsFound)
387
+ worstResult.zeroWidthCharsFound = true;
388
+ }
389
+ }
390
+ // Recurse into nested objects
391
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
392
+ const nested = scanForInjection(value, fieldPath);
393
+ if (nested.score > worstResult.score) {
394
+ worstResult = nested;
395
+ }
396
+ else if (nested.detected) {
397
+ worstResult.matchedPatterns.push(...nested.matchedPatterns);
398
+ worstResult.score = Math.min(1.0, worstResult.score + nested.score * 0.3);
399
+ worstResult.detected = true;
400
+ }
401
+ }
402
+ // Recurse into arrays
403
+ if (Array.isArray(value)) {
404
+ for (let i = 0; i < value.length; i++) {
405
+ const item = value[i];
406
+ if (item !== null && typeof item === 'object') {
407
+ const nested = scanForInjection(item, `${fieldPath}[${i}]`);
408
+ if (nested.detected) {
409
+ worstResult.matchedPatterns.push(...nested.matchedPatterns);
410
+ worstResult.score = Math.min(1.0, worstResult.score + nested.score * 0.3);
411
+ worstResult.detected = true;
412
+ }
413
+ }
414
+ }
415
+ }
416
+ }
417
+ // Recalculate action based on final merged score
418
+ if (worstResult.score >= 0.7)
419
+ worstResult.action = 'BLOCK';
420
+ else if (worstResult.score >= 0.3)
421
+ worstResult.action = 'FLAG';
422
+ else if (worstResult.detected)
423
+ worstResult.action = 'LOG_ONLY';
424
+ return worstResult;
425
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Massive name database for PII detection engine.
3
+ * ~15,000+ unique names covering 20+ countries/regions.
4
+ * Sources: US Census, UK ONS, German Standesamt, French INSEE, Spanish INE,
5
+ * Italian ISTAT, Dutch CBS, Polish GUS, Turkish, Arabic, Indian, Chinese,
6
+ * Korean, Japanese, Brazilian/Portuguese, Russian, Swedish, Norwegian,
7
+ * Danish, Finnish name registries and public datasets.
8
+ *
9
+ * Detection logic: Two consecutive capitalized words where at least one matches
10
+ * the first name dictionary AND the combination is near context words.
11
+ */
12
+ /** 5348 global first names (male + female) from 20+ countries */
13
+ export declare const FIRST_NAMES: Set<string>;
14
+ /** 10527 global last names/surnames from 20+ countries */
15
+ export declare const LAST_NAMES: Set<string>;
16
+ /**
17
+ * Detect person names in free text using the name dictionary.
18
+ * Returns array of {name, startIndex, endIndex} matches.
19
+ */
20
+ export declare function detectNamesInText(text: string): Array<{
21
+ name: string;
22
+ start: number;
23
+ end: number;
24
+ }>;
25
+ /**
26
+ * Context words that boost confidence for dictionary-based name detection.
27
+ */
28
+ export declare const NAME_CONTEXT_WORDS: string[];