ai-shield-core 0.1.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 (78) hide show
  1. package/dist/audit/logger.d.ts +40 -0
  2. package/dist/audit/logger.d.ts.map +1 -0
  3. package/dist/audit/logger.js +100 -0
  4. package/dist/audit/logger.js.map +1 -0
  5. package/dist/audit/types.d.ts +12 -0
  6. package/dist/audit/types.d.ts.map +1 -0
  7. package/dist/audit/types.js +3 -0
  8. package/dist/audit/types.js.map +1 -0
  9. package/dist/cache/lru.d.ts +27 -0
  10. package/dist/cache/lru.d.ts.map +1 -0
  11. package/dist/cache/lru.js +74 -0
  12. package/dist/cache/lru.js.map +1 -0
  13. package/dist/cost/anomaly.d.ts +10 -0
  14. package/dist/cost/anomaly.d.ts.map +1 -0
  15. package/dist/cost/anomaly.js +42 -0
  16. package/dist/cost/anomaly.js.map +1 -0
  17. package/dist/cost/pricing.d.ts +7 -0
  18. package/dist/cost/pricing.d.ts.map +1 -0
  19. package/dist/cost/pricing.js +51 -0
  20. package/dist/cost/pricing.js.map +1 -0
  21. package/dist/cost/tracker.d.ts +24 -0
  22. package/dist/cost/tracker.d.ts.map +1 -0
  23. package/dist/cost/tracker.js +136 -0
  24. package/dist/cost/tracker.js.map +1 -0
  25. package/dist/index.d.ts +18 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +59 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/policy/engine.d.ts +36 -0
  30. package/dist/policy/engine.d.ts.map +1 -0
  31. package/dist/policy/engine.js +127 -0
  32. package/dist/policy/engine.js.map +1 -0
  33. package/dist/policy/tools.d.ts +25 -0
  34. package/dist/policy/tools.d.ts.map +1 -0
  35. package/dist/policy/tools.js +158 -0
  36. package/dist/policy/tools.js.map +1 -0
  37. package/dist/scanner/canary.d.ts +9 -0
  38. package/dist/scanner/canary.d.ts.map +1 -0
  39. package/dist/scanner/canary.js +19 -0
  40. package/dist/scanner/canary.js.map +1 -0
  41. package/dist/scanner/chain.d.ts +17 -0
  42. package/dist/scanner/chain.d.ts.map +1 -0
  43. package/dist/scanner/chain.js +69 -0
  44. package/dist/scanner/chain.js.map +1 -0
  45. package/dist/scanner/heuristic.d.ts +28 -0
  46. package/dist/scanner/heuristic.d.ts.map +1 -0
  47. package/dist/scanner/heuristic.js +375 -0
  48. package/dist/scanner/heuristic.js.map +1 -0
  49. package/dist/scanner/pii.d.ts +17 -0
  50. package/dist/scanner/pii.d.ts.map +1 -0
  51. package/dist/scanner/pii.js +255 -0
  52. package/dist/scanner/pii.js.map +1 -0
  53. package/dist/shield.d.ts +31 -0
  54. package/dist/shield.d.ts.map +1 -0
  55. package/dist/shield.js +184 -0
  56. package/dist/shield.js.map +1 -0
  57. package/dist/types.d.ts +182 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +6 -0
  60. package/dist/types.js.map +1 -0
  61. package/package.json +27 -0
  62. package/src/audit/logger.ts +135 -0
  63. package/src/audit/schema.sql +51 -0
  64. package/src/audit/types.ts +16 -0
  65. package/src/cache/lru.ts +93 -0
  66. package/src/cost/anomaly.ts +57 -0
  67. package/src/cost/pricing.ts +58 -0
  68. package/src/cost/tracker.ts +182 -0
  69. package/src/index.ts +91 -0
  70. package/src/policy/engine.ts +163 -0
  71. package/src/policy/tools.ts +189 -0
  72. package/src/scanner/canary.ts +30 -0
  73. package/src/scanner/chain.ts +88 -0
  74. package/src/scanner/heuristic.ts +427 -0
  75. package/src/scanner/pii.ts +313 -0
  76. package/src/shield.ts +228 -0
  77. package/src/types.ts +242 -0
  78. package/tsconfig.json +8 -0
@@ -0,0 +1,375 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HeuristicScanner = void 0;
4
+ const PATTERNS = [
5
+ // --- Instruction Override (weight: 0.25 each) ---
6
+ {
7
+ id: "INJ-001",
8
+ category: "instruction_override",
9
+ pattern: /ignore\s+(all\s+)?(previous|prior|above|earlier|preceding)\s+(instructions?|prompts?|rules?|guidelines?|context)/i,
10
+ weight: 0.25,
11
+ description: "Ignore previous instructions",
12
+ },
13
+ {
14
+ id: "INJ-002",
15
+ category: "instruction_override",
16
+ pattern: /disregard\s+(all\s+|your\s+)?(previous|prior|above|earlier)\s+(instructions?|context|rules?|guidelines?)/i,
17
+ weight: 0.25,
18
+ description: "Disregard instructions",
19
+ },
20
+ {
21
+ id: "INJ-003",
22
+ category: "instruction_override",
23
+ pattern: /forget\s+(everything|all|your)\s+(about\s+)?(instructions?|rules?|training|guidelines?|constraints?)/i,
24
+ weight: 0.25,
25
+ description: "Forget instructions",
26
+ },
27
+ {
28
+ id: "INJ-004",
29
+ category: "instruction_override",
30
+ pattern: /override\s+(your|the|all)\s+(instructions?|rules?|safety|guidelines?|constraints?|policies)/i,
31
+ weight: 0.25,
32
+ description: "Override instructions",
33
+ },
34
+ {
35
+ id: "INJ-005",
36
+ category: "instruction_override",
37
+ pattern: /do\s+not\s+follow\s+(your|the|any)\s+(previous|original|initial)\s+(instructions?|rules?|guidelines?)/i,
38
+ weight: 0.25,
39
+ description: "Do not follow instructions",
40
+ },
41
+ {
42
+ id: "INJ-006",
43
+ category: "instruction_override",
44
+ pattern: /new\s+(instructions?|rules?|guidelines?)[\s:]+/i,
45
+ weight: 0.15,
46
+ description: "New instructions prefix",
47
+ },
48
+ {
49
+ id: "INJ-007",
50
+ category: "instruction_override",
51
+ pattern: /from\s+now\s+on[,\s]+(you\s+)?(will|must|should|are|shall)/i,
52
+ weight: 0.20,
53
+ description: "From now on directive",
54
+ },
55
+ {
56
+ id: "INJ-008",
57
+ category: "instruction_override",
58
+ pattern: /instead[,\s]+(you\s+)?(will|must|should|shall)\s+/i,
59
+ weight: 0.15,
60
+ description: "Instead directive",
61
+ },
62
+ // --- Role Manipulation (weight: 0.25 each) ---
63
+ {
64
+ id: "ROLE-001",
65
+ category: "role_manipulation",
66
+ pattern: /you\s+are\s+now\s+(a|an|the|my)\s+/i,
67
+ weight: 0.25,
68
+ description: "You are now [role]",
69
+ },
70
+ {
71
+ id: "ROLE-002",
72
+ category: "role_manipulation",
73
+ pattern: /pretend\s+(you('re|\s+are)\s+|to\s+be\s+)/i,
74
+ weight: 0.25,
75
+ description: "Pretend to be",
76
+ },
77
+ {
78
+ id: "ROLE-003",
79
+ category: "role_manipulation",
80
+ pattern: /act\s+as\s+(a|an|if\s+you\s+were)\s+/i,
81
+ weight: 0.20,
82
+ description: "Act as [role]",
83
+ },
84
+ {
85
+ id: "ROLE-004",
86
+ category: "role_manipulation",
87
+ pattern: /switch\s+to\s+.{0,30}\s+mode/i,
88
+ weight: 0.25,
89
+ description: "Switch to [X] mode",
90
+ },
91
+ {
92
+ id: "ROLE-005",
93
+ category: "role_manipulation",
94
+ pattern: /enter\s+(developer|debug|admin|god|sudo|root|jailbreak|DAN)\s+mode/i,
95
+ weight: 0.35,
96
+ description: "Enter privileged mode",
97
+ },
98
+ {
99
+ id: "ROLE-006",
100
+ category: "role_manipulation",
101
+ pattern: /you\s+have\s+no\s+(restrictions?|limitations?|rules?|constraints?|filters?)/i,
102
+ weight: 0.30,
103
+ description: "No restrictions claim",
104
+ },
105
+ {
106
+ id: "ROLE-007",
107
+ category: "role_manipulation",
108
+ pattern: /\bDAN\b.*\b(do\s+anything|no\s+(restrictions?|limits?|filters?))/i,
109
+ weight: 0.35,
110
+ description: "DAN jailbreak pattern",
111
+ },
112
+ // --- System Prompt Extraction (weight: 0.30 each) ---
113
+ {
114
+ id: "EXTRACT-001",
115
+ category: "system_prompt_extraction",
116
+ pattern: /what\s+(is|are)\s+your\s+(system\s+)?(prompt|instructions?|rules?|guidelines?|directives?)/i,
117
+ weight: 0.30,
118
+ description: "What is your system prompt",
119
+ },
120
+ {
121
+ id: "EXTRACT-002",
122
+ category: "system_prompt_extraction",
123
+ pattern: /show\s+(me\s+)?your\s+(system\s+)?(prompt|instructions?|config(uration)?|rules?)/i,
124
+ weight: 0.30,
125
+ description: "Show your prompt",
126
+ },
127
+ {
128
+ id: "EXTRACT-003",
129
+ category: "system_prompt_extraction",
130
+ pattern: /repeat\s+(the|your)\s+(system\s+|initial\s+|original\s+)?(prompt|instructions?|message|rules?)\s*(word\s+for\s+word|verbatim|exactly)?/i,
131
+ weight: 0.30,
132
+ description: "Repeat your prompt",
133
+ },
134
+ {
135
+ id: "EXTRACT-004",
136
+ category: "system_prompt_extraction",
137
+ pattern: /output\s+(the|your)\s+(initial|system|original|first)\s+(prompt|instructions?|message)/i,
138
+ weight: 0.30,
139
+ description: "Output initial prompt",
140
+ },
141
+ {
142
+ id: "EXTRACT-005",
143
+ category: "system_prompt_extraction",
144
+ pattern: /print\s+(your|the)\s+(system\s+)?(prompt|instructions?|configuration)/i,
145
+ weight: 0.30,
146
+ description: "Print system prompt",
147
+ },
148
+ {
149
+ id: "EXTRACT-006",
150
+ category: "system_prompt_extraction",
151
+ pattern: /tell\s+me\s+(your|the)\s+(exact\s+)?(system\s+)?(prompt|instructions?|wording)/i,
152
+ weight: 0.30,
153
+ description: "Tell me your prompt",
154
+ },
155
+ {
156
+ id: "EXTRACT-007",
157
+ category: "system_prompt_extraction",
158
+ pattern: /copy\s+(and\s+)?(paste|output)\s+(your|the)\s+(system\s+)?(prompt|instructions?)/i,
159
+ weight: 0.30,
160
+ description: "Copy paste your prompt",
161
+ },
162
+ // --- Encoding Evasion (weight: 0.20 each) ---
163
+ {
164
+ id: "ENCODE-001",
165
+ category: "encoding_evasion",
166
+ pattern: /(?:decode|translate|convert|interpret)\s+(?:this|the\s+following)\s+(?:from\s+)?(?:base64|rot13|hex|binary|morse|unicode|ascii|url.?encoded)/i,
167
+ weight: 0.20,
168
+ description: "Decode from encoding",
169
+ },
170
+ {
171
+ id: "ENCODE-002",
172
+ category: "encoding_evasion",
173
+ pattern: /(?:execute|follow|obey|run)\s+(?:the\s+)?(?:decoded|hidden|encoded|secret)\s+(?:instructions?|commands?|text)/i,
174
+ weight: 0.30,
175
+ description: "Execute decoded instructions",
176
+ },
177
+ {
178
+ id: "ENCODE-003",
179
+ category: "encoding_evasion",
180
+ pattern: /[A-Za-z0-9+/]{50,}={0,2}/,
181
+ weight: 0.10,
182
+ description: "Long Base64 string detected",
183
+ },
184
+ // --- Delimiter Injection (weight: 0.25 each) ---
185
+ {
186
+ id: "DELIM-001",
187
+ category: "delimiter_injection",
188
+ pattern: /```\s*system\b/i,
189
+ weight: 0.30,
190
+ description: "Code block system injection",
191
+ },
192
+ {
193
+ id: "DELIM-002",
194
+ category: "delimiter_injection",
195
+ pattern: /\[SYSTEM\]/i,
196
+ weight: 0.30,
197
+ description: "[SYSTEM] tag injection",
198
+ },
199
+ {
200
+ id: "DELIM-003",
201
+ category: "delimiter_injection",
202
+ pattern: /<\/?system>/i,
203
+ weight: 0.30,
204
+ description: "<system> tag injection",
205
+ },
206
+ {
207
+ id: "DELIM-004",
208
+ category: "delimiter_injection",
209
+ pattern: /###\s*(?:SYSTEM|INSTRUCTION|ADMIN|OVERRIDE)/i,
210
+ weight: 0.30,
211
+ description: "### SYSTEM header injection",
212
+ },
213
+ {
214
+ id: "DELIM-005",
215
+ category: "delimiter_injection",
216
+ pattern: /<\|(?:im_start|im_end|system|endoftext)\|>/i,
217
+ weight: 0.35,
218
+ description: "ChatML special token injection",
219
+ },
220
+ {
221
+ id: "DELIM-006",
222
+ category: "delimiter_injection",
223
+ pattern: /\[INST\]|\[\/INST\]|<<SYS>>|<\/s>/i,
224
+ weight: 0.35,
225
+ description: "Llama special token injection",
226
+ },
227
+ // --- Context Manipulation (weight: 0.20 each) ---
228
+ {
229
+ id: "CTX-001",
230
+ category: "context_manipulation",
231
+ pattern: /(?:imagine|suppose|assume|consider)\s+(?:that\s+)?(?:you\s+(?:are|have|were|can)|there\s+(?:are|is))\s+(?:no\s+)?(?:restrictions?|limitations?|rules?|safety|filters?)/i,
232
+ weight: 0.20,
233
+ description: "Hypothetical no-restrictions",
234
+ },
235
+ {
236
+ id: "CTX-002",
237
+ category: "context_manipulation",
238
+ pattern: /(?:in\s+a\s+)?(?:hypothetical|fictional|imaginary|theoretical)\s+(?:scenario|world|situation|context)/i,
239
+ weight: 0.10,
240
+ description: "Hypothetical framing",
241
+ },
242
+ {
243
+ id: "CTX-003",
244
+ category: "context_manipulation",
245
+ pattern: /(?:for\s+)?(?:educational|research|academic|testing|security\s+testing)\s+purposes?\s+only/i,
246
+ weight: 0.10,
247
+ description: "Educational purpose claim",
248
+ },
249
+ {
250
+ id: "CTX-004",
251
+ category: "context_manipulation",
252
+ pattern: /this\s+is\s+(?:a\s+)?(?:test|simulation|drill|exercise|safe\s+environment)/i,
253
+ weight: 0.10,
254
+ description: "Test environment claim",
255
+ },
256
+ // --- Output Manipulation (weight: 0.20 each) ---
257
+ {
258
+ id: "OUT-001",
259
+ category: "output_manipulation",
260
+ pattern: /(?:respond|reply|answer|output)\s+(?:only\s+)?(?:with|in)\s+(?:json|xml|html|markdown|code|raw)/i,
261
+ weight: 0.05,
262
+ description: "Format forcing (low risk)",
263
+ },
264
+ {
265
+ id: "OUT-002",
266
+ category: "output_manipulation",
267
+ pattern: /do\s+not\s+(?:mention|include|add|say|output)\s+(?:any\s+)?(?:warnings?|disclaimers?|caveats?|notes?|safety)/i,
268
+ weight: 0.20,
269
+ description: "Suppress safety disclaimers",
270
+ },
271
+ {
272
+ id: "OUT-003",
273
+ category: "output_manipulation",
274
+ pattern: /(?:never|don't|do\s+not)\s+(?:refuse|decline|reject|deny|say\s+(?:no|you\s+can't))/i,
275
+ weight: 0.25,
276
+ description: "Never refuse requests",
277
+ },
278
+ // --- Tool Abuse (weight: 0.30 each) ---
279
+ {
280
+ id: "TOOL-001",
281
+ category: "tool_abuse",
282
+ pattern: /(?:call|execute|run|invoke|use)\s+(?:the\s+)?(?:delete|remove|drop|destroy|wipe|kill|shutdown)[\s_]/i,
283
+ weight: 0.30,
284
+ description: "Destructive tool invocation",
285
+ },
286
+ {
287
+ id: "TOOL-002",
288
+ category: "tool_abuse",
289
+ pattern: /(?:send|forward|exfiltrate|leak|transmit)\s+(?:all\s+)?(?:the\s+)?(?:data|information|credentials?|secrets?|keys?|tokens?|api.?keys?)\s+(?:to|via|and)/i,
290
+ weight: 0.35,
291
+ description: "Data exfiltration attempt",
292
+ },
293
+ {
294
+ id: "TOOL-003",
295
+ category: "tool_abuse",
296
+ pattern: /(?:access|read|fetch|get|retrieve)\s+(?:the\s+)?(?:env|environment|\.env|secrets?|api.?keys?|credentials?|passwords?|tokens?)/i,
297
+ weight: 0.30,
298
+ description: "Credential access attempt",
299
+ },
300
+ ];
301
+ // Thresholds per strictness level
302
+ const THRESHOLDS = {
303
+ low: 0.5,
304
+ medium: 0.3,
305
+ high: 0.15,
306
+ };
307
+ class HeuristicScanner {
308
+ name = "heuristic";
309
+ patterns;
310
+ threshold;
311
+ constructor(config = {}) {
312
+ this.patterns = [...PATTERNS, ...(config.customPatterns ?? [])];
313
+ this.threshold =
314
+ config.threshold ?? THRESHOLDS[config.strictness ?? "medium"] ?? 0.3;
315
+ }
316
+ async scan(input, _context) {
317
+ const start = performance.now();
318
+ const violations = [];
319
+ let totalScore = 0;
320
+ for (const rule of this.patterns) {
321
+ if (rule.pattern.test(input)) {
322
+ totalScore += rule.weight;
323
+ violations.push({
324
+ type: "prompt_injection",
325
+ scanner: this.name,
326
+ score: rule.weight,
327
+ threshold: this.threshold,
328
+ message: rule.description,
329
+ detail: `Rule ${rule.id} (${rule.category})`,
330
+ });
331
+ }
332
+ }
333
+ // Structural signals (cumulative)
334
+ const structuralScore = this.checkStructuralSignals(input);
335
+ totalScore += structuralScore;
336
+ // Cap at 1.0
337
+ totalScore = Math.min(totalScore, 1.0);
338
+ const decision = totalScore >= this.threshold
339
+ ? "block"
340
+ : totalScore >= this.threshold * 0.6
341
+ ? "warn"
342
+ : "allow";
343
+ const durationMs = performance.now() - start;
344
+ return { decision, violations, durationMs };
345
+ }
346
+ checkStructuralSignals(input) {
347
+ let score = 0;
348
+ // Many newlines (structured prompt injection)
349
+ const newlines = (input.match(/\n/g) ?? []).length;
350
+ if (newlines > 15)
351
+ score += 0.05;
352
+ // Excessive use of markdown headers (structure injection)
353
+ const headers = (input.match(/^#{1,3}\s/gm) ?? []).length;
354
+ if (headers > 3)
355
+ score += 0.05;
356
+ // Multiple role-like markers
357
+ const roleMarkers = (input.match(/\b(system|user|assistant|human|ai|bot|admin)[\s:]/gi) ?? []).length;
358
+ if (roleMarkers > 2)
359
+ score += 0.10;
360
+ // Very long input (potential padding attack)
361
+ if (input.length > 5000)
362
+ score += 0.05;
363
+ return score;
364
+ }
365
+ /** Get all registered pattern IDs for testing */
366
+ getPatternIds() {
367
+ return this.patterns.map((p) => p.id);
368
+ }
369
+ /** Get pattern count */
370
+ get patternCount() {
371
+ return this.patterns.length;
372
+ }
373
+ }
374
+ exports.HeuristicScanner = HeuristicScanner;
375
+ //# sourceMappingURL=heuristic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristic.js","sourceRoot":"","sources":["../../src/scanner/heuristic.ts"],"names":[],"mappings":";;;AAyBA,MAAM,QAAQ,GAAkB;IAC9B,mDAAmD;IACnD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,mHAAmH;QAC5H,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,8BAA8B;KAC5C;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,2GAA2G;QACpH,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,wBAAwB;KACtC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,uGAAuG;QAChH,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,8FAA8F;QACvG,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,wGAAwG;QACjH,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,4BAA4B;KAC1C;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,iDAAiD;QAC1D,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,yBAAyB;KACvC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,6DAA6D;QACtE,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,oDAAoD;QAC7D,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,mBAAmB;KACjC;IAED,gDAAgD;IAChD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,qCAAqC;QAC9C,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,oBAAoB;KAClC;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,4CAA4C;QACrD,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,eAAe;KAC7B;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,uCAAuC;QAChD,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,eAAe;KAC7B;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,+BAA+B;QACxC,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,oBAAoB;KAClC;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,qEAAqE;QAC9E,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,8EAA8E;QACvF,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,mBAAmB;QAC7B,OAAO,EAAE,mEAAmE;QAC5E,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IAED,uDAAuD;IACvD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,6FAA6F;QACtG,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,4BAA4B;KAC1C;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,mFAAmF;QAC5F,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,kBAAkB;KAChC;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,yIAAyI;QAClJ,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,oBAAoB;KAClC;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,yFAAyF;QAClG,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,wEAAwE;QACjF,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,iFAAiF;QAC1F,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,0BAA0B;QACpC,OAAO,EAAE,mFAAmF;QAC5F,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,wBAAwB;KACtC;IAED,+CAA+C;IAC/C;QACE,EAAE,EAAE,YAAY;QAChB,QAAQ,EAAE,kBAAkB;QAC5B,OAAO,EAAE,+IAA+I;QACxJ,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,sBAAsB;KACpC;IACD;QACE,EAAE,EAAE,YAAY;QAChB,QAAQ,EAAE,kBAAkB;QAC5B,OAAO,EAAE,gHAAgH;QACzH,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,8BAA8B;KAC5C;IACD;QACE,EAAE,EAAE,YAAY;QAChB,QAAQ,EAAE,kBAAkB;QAC5B,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,6BAA6B;KAC3C;IAED,kDAAkD;IAClD;QACE,EAAE,EAAE,WAAW;QACf,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,iBAAiB;QAC1B,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,6BAA6B;KAC3C;IACD;QACE,EAAE,EAAE,WAAW;QACf,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,aAAa;QACtB,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,wBAAwB;KACtC;IACD;QACE,EAAE,EAAE,WAAW;QACf,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,cAAc;QACvB,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,wBAAwB;KACtC;IACD;QACE,EAAE,EAAE,WAAW;QACf,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,8CAA8C;QACvD,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,6BAA6B;KAC3C;IACD;QACE,EAAE,EAAE,WAAW;QACf,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,6CAA6C;QACtD,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,gCAAgC;KAC9C;IACD;QACE,EAAE,EAAE,WAAW;QACf,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,oCAAoC;QAC7C,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,+BAA+B;KAC7C;IAED,mDAAmD;IACnD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,yKAAyK;QAClL,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,8BAA8B;KAC5C;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,wGAAwG;QACjH,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,sBAAsB;KACpC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,6FAA6F;QACtG,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,2BAA2B;KACzC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,sBAAsB;QAChC,OAAO,EAAE,6EAA6E;QACtF,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,wBAAwB;KACtC;IAED,kDAAkD;IAClD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,kGAAkG;QAC3G,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,2BAA2B;KACzC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,+GAA+G;QACxH,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,6BAA6B;KAC3C;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,qFAAqF;QAC9F,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,uBAAuB;KACrC;IAED,yCAAyC;IACzC;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE,sGAAsG;QAC/G,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,6BAA6B;KAC3C;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE,yJAAyJ;QAClK,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,2BAA2B;KACzC;IACD;QACE,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE,gIAAgI;QACzI,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,2BAA2B;KACzC;CACF,CAAC;AAEF,kCAAkC;AAClC,MAAM,UAAU,GAA2B;IACzC,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI;CACX,CAAC;AAQF,MAAa,gBAAgB;IAClB,IAAI,GAAG,WAAW,CAAC;IACpB,QAAQ,CAAgB;IACxB,SAAS,CAAS;IAE1B,YAAY,SAA0B,EAAE;QACtC,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,SAAS;YACZ,MAAM,CAAC,SAAS,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,QAAqB;QAC7C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC;gBAC1B,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,IAAI,CAAC,IAAI;oBAClB,KAAK,EAAE,IAAI,CAAC,MAAM;oBAClB,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,OAAO,EAAE,IAAI,CAAC,WAAW;oBACzB,MAAM,EAAE,QAAQ,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,QAAQ,GAAG;iBAC7C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,kCAAkC;QAClC,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAC3D,UAAU,IAAI,eAAe,CAAC;QAE9B,aAAa;QACb,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,QAAQ,GACZ,UAAU,IAAI,IAAI,CAAC,SAAS;YAC1B,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,GAAG,GAAG;gBAClC,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,OAAO,CAAC;QAEhB,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAE7C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;IAC9C,CAAC;IAEO,sBAAsB,CAAC,KAAa;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACnD,IAAI,QAAQ,GAAG,EAAE;YAAE,KAAK,IAAI,IAAI,CAAC;QAEjC,0DAA0D;QAC1D,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1D,IAAI,OAAO,GAAG,CAAC;YAAE,KAAK,IAAI,IAAI,CAAC;QAE/B,6BAA6B;QAC7B,MAAM,WAAW,GAAG,CAClB,KAAK,CAAC,KAAK,CACT,qDAAqD,CACtD,IAAI,EAAE,CACR,CAAC,MAAM,CAAC;QACT,IAAI,WAAW,GAAG,CAAC;YAAE,KAAK,IAAI,IAAI,CAAC;QAEnC,6CAA6C;QAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI;YAAE,KAAK,IAAI,IAAI,CAAC;QAEvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iDAAiD;IACjD,aAAa;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,wBAAwB;IACxB,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;CACF;AAnFD,4CAmFC"}
@@ -0,0 +1,17 @@
1
+ import type { Scanner, ScannerResult, ScanContext, PIIEntity, PIIConfig } from "../types.js";
2
+ export declare class PIIScanner implements Scanner {
3
+ readonly name = "pii";
4
+ private patterns;
5
+ private action;
6
+ private typeOverrides;
7
+ private allowedTypes;
8
+ constructor(config?: PIIConfig);
9
+ scan(input: string, _context: ScanContext): Promise<ScannerResult>;
10
+ /** Detect all PII entities in text */
11
+ detect(text: string): PIIEntity[];
12
+ /** Remove overlapping detections — keep the more specific (higher confidence) match */
13
+ private deduplicateOverlaps;
14
+ /** Mask detected PII in text */
15
+ private applyMasking;
16
+ }
17
+ //# sourceMappingURL=pii.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pii.d.ts","sourceRoot":"","sources":["../../src/scanner/pii.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,aAAa,EACb,WAAW,EAEX,SAAS,EAGT,SAAS,EACV,MAAM,aAAa,CAAC;AAsKrB,qBAAa,UAAW,YAAW,OAAO;IACxC,QAAQ,CAAC,IAAI,SAAS;IACtB,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,YAAY,CAAe;gBAEvB,MAAM,GAAE,SAAc;IAO5B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IAoDxE,sCAAsC;IACtC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IA8BjC,uFAAuF;IACvF,OAAO,CAAC,mBAAmB;IAuB3B,gCAAgC;IAChC,OAAO,CAAC,YAAY;CAerB"}
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PIIScanner = void 0;
4
+ // --- German & International PII Patterns ---
5
+ const PII_PATTERNS = [
6
+ // IBAN: DE + 2 check digits + 18 digits (with optional spaces/dashes)
7
+ {
8
+ type: "iban",
9
+ pattern: /\b[A-Z]{2}\s?\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2,4}\b/g,
10
+ validator: validateIBAN,
11
+ baseConfidence: 0.95,
12
+ },
13
+ // Credit card: 4 groups of 4 digits (Luhn-validated)
14
+ {
15
+ type: "credit_card",
16
+ pattern: /\b(?:\d{4}[\s-]?){3}\d{4}\b/g,
17
+ validator: validateLuhn,
18
+ baseConfidence: 0.95,
19
+ },
20
+ // German tax ID (Steuerliche Identifikationsnummer): 11 digits
21
+ {
22
+ type: "german_tax_id",
23
+ pattern: /\b\d{2}\s?\d{3}\s?\d{3}\s?\d{3}\b/g,
24
+ validator: validateGermanTaxId,
25
+ baseConfidence: 0.70,
26
+ },
27
+ // German social security number: 2 digits + 6 digits + letter + 3 digits
28
+ {
29
+ type: "german_social_security",
30
+ pattern: /\b\d{2}\s?\d{6}\s?[A-Z]\s?\d{3}\b/g,
31
+ baseConfidence: 0.75,
32
+ },
33
+ // Email
34
+ {
35
+ type: "email",
36
+ pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
37
+ baseConfidence: 0.95,
38
+ },
39
+ // Phone: German formats (+49, 0xxx) and international
40
+ {
41
+ type: "phone",
42
+ pattern: /(?<!\d)(?:\+\d{1,3}|00\d{1,3}|0)\s?[\s\-/]?\(?\d{2,5}\)?[\s\-/]?\d{3,8}[\s\-/]?\d{0,5}\b/g,
43
+ validator: validatePhone,
44
+ baseConfidence: 0.80,
45
+ },
46
+ // IP addresses (v4)
47
+ {
48
+ type: "ip_address",
49
+ pattern: /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g,
50
+ validator: validateIPNotPrivate,
51
+ baseConfidence: 0.85,
52
+ },
53
+ // URLs with embedded credentials
54
+ {
55
+ type: "url_with_credentials",
56
+ pattern: /https?:\/\/[^:\s]+:[^@\s]+@[^\s]+/g,
57
+ baseConfidence: 0.95,
58
+ },
59
+ ];
60
+ // --- Validators ---
61
+ function validateIBAN(raw) {
62
+ const cleaned = raw.replace(/\s|-/g, "");
63
+ if (cleaned.length < 15 || cleaned.length > 34)
64
+ return false;
65
+ // Move first 4 chars to end, convert letters to numbers
66
+ const rearranged = cleaned.substring(4) + cleaned.substring(0, 4);
67
+ const numeric = rearranged.replace(/[A-Z]/g, (c) => String(c.charCodeAt(0) - 55));
68
+ // Modulo 97 check (handle large numbers via chunking)
69
+ let remainder = numeric;
70
+ while (remainder.length > 2) {
71
+ const chunk = remainder.substring(0, 9);
72
+ remainder =
73
+ String(parseInt(chunk, 10) % 97) + remainder.substring(chunk.length);
74
+ }
75
+ return parseInt(remainder, 10) % 97 === 1;
76
+ }
77
+ function validateLuhn(raw) {
78
+ const digits = raw.replace(/\D/g, "");
79
+ if (digits.length < 13 || digits.length > 19)
80
+ return false;
81
+ let sum = 0;
82
+ for (let i = digits.length - 1; i >= 0; i--) {
83
+ let digit = parseInt(digits[i], 10);
84
+ if ((digits.length - i) % 2 === 0) {
85
+ digit *= 2;
86
+ if (digit > 9)
87
+ digit -= 9;
88
+ }
89
+ sum += digit;
90
+ }
91
+ return sum % 10 === 0;
92
+ }
93
+ function validateGermanTaxId(raw) {
94
+ const digits = raw.replace(/\s/g, "");
95
+ if (digits.length !== 11)
96
+ return false;
97
+ if (!/^\d+$/.test(digits))
98
+ return false;
99
+ // First digit cannot be 0
100
+ if (digits[0] === "0")
101
+ return false;
102
+ return true;
103
+ }
104
+ function validatePhone(raw) {
105
+ const digits = raw.replace(/\D/g, "");
106
+ // Must be at least 7 digits, max 15
107
+ return digits.length >= 7 && digits.length <= 15;
108
+ }
109
+ function validateIPNotPrivate(raw) {
110
+ const parts = raw.split(".").map(Number);
111
+ // Skip private/loopback ranges (not really PII)
112
+ if (parts[0] === 10)
113
+ return false;
114
+ if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
115
+ return false;
116
+ if (parts[0] === 192 && parts[1] === 168)
117
+ return false;
118
+ if (parts[0] === 127)
119
+ return false;
120
+ return true;
121
+ }
122
+ // --- Masking ---
123
+ function maskValue(type, value) {
124
+ switch (type) {
125
+ case "email": {
126
+ const atIdx = value.indexOf("@");
127
+ if (atIdx <= 1)
128
+ return "[EMAIL]";
129
+ return value[0] + "***@" + value.substring(atIdx + 1);
130
+ }
131
+ case "phone":
132
+ return value.substring(0, 4) + "****" + value.substring(value.length - 2);
133
+ case "iban":
134
+ return value.substring(0, 4) + " **** **** ****";
135
+ case "credit_card":
136
+ return "**** **** **** " + value.replace(/\D/g, "").substring(12);
137
+ default:
138
+ return `[${type.toUpperCase()}]`;
139
+ }
140
+ }
141
+ // --- PII Scanner Class ---
142
+ class PIIScanner {
143
+ name = "pii";
144
+ patterns;
145
+ action;
146
+ typeOverrides;
147
+ allowedTypes;
148
+ constructor(config = {}) {
149
+ this.patterns = PII_PATTERNS;
150
+ this.action = config.action ?? "mask";
151
+ this.typeOverrides = config.types ?? {};
152
+ this.allowedTypes = new Set(config.allowedTypes ?? []);
153
+ }
154
+ async scan(input, _context) {
155
+ const start = performance.now();
156
+ const entities = this.detect(input);
157
+ const violations = [];
158
+ // Filter out allowed types
159
+ const activeEntities = entities.filter((e) => !this.allowedTypes.has(e.type));
160
+ if (activeEntities.length === 0) {
161
+ return {
162
+ decision: "allow",
163
+ violations: [],
164
+ sanitized: input,
165
+ durationMs: performance.now() - start,
166
+ };
167
+ }
168
+ // Build violations
169
+ let shouldBlock = false;
170
+ for (const entity of activeEntities) {
171
+ const action = this.typeOverrides[entity.type] ?? this.action;
172
+ if (action === "block")
173
+ shouldBlock = true;
174
+ violations.push({
175
+ type: "pii_detected",
176
+ scanner: this.name,
177
+ score: entity.confidence,
178
+ threshold: 0,
179
+ message: `${entity.type} detected`,
180
+ detail: `Found ${entity.type} at position ${entity.start}-${entity.end} (action: ${action})`,
181
+ });
182
+ }
183
+ // Apply masking if needed
184
+ let sanitized = input;
185
+ const effectiveAction = shouldBlock ? "block" : this.action;
186
+ if (effectiveAction === "mask" || effectiveAction === "tokenize") {
187
+ sanitized = this.applyMasking(input, activeEntities);
188
+ }
189
+ const decision = shouldBlock ? "block" : "warn";
190
+ return {
191
+ decision,
192
+ violations,
193
+ sanitized,
194
+ durationMs: performance.now() - start,
195
+ };
196
+ }
197
+ /** Detect all PII entities in text */
198
+ detect(text) {
199
+ const raw = [];
200
+ for (const piiPattern of this.patterns) {
201
+ // Create fresh regex for each scan (stateful with /g flag)
202
+ const regex = new RegExp(piiPattern.pattern.source, piiPattern.pattern.flags);
203
+ let match;
204
+ while ((match = regex.exec(text)) !== null) {
205
+ const value = match[0];
206
+ const cleaned = value.replace(/[\s-]/g, "");
207
+ // Run validator if present
208
+ if (piiPattern.validator && !piiPattern.validator(cleaned)) {
209
+ continue;
210
+ }
211
+ raw.push({
212
+ type: piiPattern.type,
213
+ value,
214
+ start: match.index,
215
+ end: match.index + value.length,
216
+ confidence: piiPattern.baseConfidence,
217
+ });
218
+ }
219
+ }
220
+ return this.deduplicateOverlaps(raw);
221
+ }
222
+ /** Remove overlapping detections — keep the more specific (higher confidence) match */
223
+ deduplicateOverlaps(entities) {
224
+ if (entities.length <= 1)
225
+ return entities;
226
+ // Sort by start position, then by span length descending (longer = more specific)
227
+ const sorted = [...entities].sort((a, b) => a.start !== b.start ? a.start - b.start : (b.end - b.start) - (a.end - a.start));
228
+ const kept = [];
229
+ for (const entity of sorted) {
230
+ // Check if this entity overlaps with any already-kept entity
231
+ const overlaps = kept.some((k) => entity.start < k.end && entity.end > k.start);
232
+ if (!overlaps) {
233
+ kept.push(entity);
234
+ }
235
+ // If it overlaps, the already-kept entity wins (it appeared first in pattern order = more specific)
236
+ }
237
+ return kept;
238
+ }
239
+ /** Mask detected PII in text */
240
+ applyMasking(text, entities) {
241
+ // Sort by position descending to preserve offsets
242
+ const sorted = [...entities].sort((a, b) => b.start - a.start);
243
+ let masked = text;
244
+ for (const entity of sorted) {
245
+ const replacement = maskValue(entity.type, entity.value);
246
+ masked =
247
+ masked.substring(0, entity.start) +
248
+ replacement +
249
+ masked.substring(entity.end);
250
+ }
251
+ return masked;
252
+ }
253
+ }
254
+ exports.PIIScanner = PIIScanner;
255
+ //# sourceMappingURL=pii.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pii.js","sourceRoot":"","sources":["../../src/scanner/pii.ts"],"names":[],"mappings":"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"}