mask-privacy 3.5.0 → 4.0.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.
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +378 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +372 -48
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +5 -0
- package/src/core/dlp/registry.ts +7 -1
- package/src/core/fpe.ts +179 -31
- package/src/core/fpe_utils.ts +18 -8
- package/src/core/scanner.ts +2 -1
- package/src/core/span.ts +3 -0
- package/src/core/synthesisLibrary.ts +41 -0
- package/src/index.ts +1 -1
- package/src/telemetry/audit_logger.ts +3 -1
- package/tests/bijective_fpe.test.ts +103 -0
package/dist/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import * as path from 'path';
|
|
|
4
4
|
import { join, normalize, sep, dirname } from 'path';
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import { platform, release, homedir } from 'os';
|
|
7
|
-
import * as
|
|
8
|
-
import
|
|
7
|
+
import * as crypto2 from 'crypto';
|
|
8
|
+
import crypto2__default, { createHmac, createHash, createPrivateKey, createPublicKey, sign } from 'crypto';
|
|
9
9
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
10
10
|
import { Writable, Readable } from 'stream';
|
|
11
11
|
import { Agent, request as request$1 } from 'https';
|
|
@@ -89,6 +89,16 @@ var init_config = __esm({
|
|
|
89
89
|
get MASK_LOG_LEVEL() {
|
|
90
90
|
return (process2.env.MASK_LOG_LEVEL || "info").toLowerCase();
|
|
91
91
|
},
|
|
92
|
+
// --- COMPLIANCE & PRIVACY (Bijective) ---
|
|
93
|
+
get MASK_TENANT_ID() {
|
|
94
|
+
return process2.env.MASK_TENANT_ID || "global-default-tenant";
|
|
95
|
+
},
|
|
96
|
+
get MASK_SALT_ROTATION() {
|
|
97
|
+
return (process2.env.MASK_SALT_ROTATION || "NONE").toUpperCase();
|
|
98
|
+
},
|
|
99
|
+
get MASK_BIJECTIVE_MODE() {
|
|
100
|
+
return getEnvBool("MASK_BIJECTIVE_MODE", true);
|
|
101
|
+
},
|
|
92
102
|
// --- SECURITY & CRYPTOGRAPHY ---
|
|
93
103
|
get MASK_ENCRYPTION_KEY() {
|
|
94
104
|
return process2.env.MASK_ENCRYPTION_KEY || null;
|
|
@@ -227,6 +237,216 @@ var init_exceptions = __esm({
|
|
|
227
237
|
}
|
|
228
238
|
});
|
|
229
239
|
|
|
240
|
+
// src/core/synthesisLibrary.ts
|
|
241
|
+
var FIRST_NAMES, CONNECTORS, SURNAME_ROOTS, SURNAME_SUFFIXES, SYLLABLES;
|
|
242
|
+
var init_synthesisLibrary = __esm({
|
|
243
|
+
"src/core/synthesisLibrary.ts"() {
|
|
244
|
+
FIRST_NAMES = [
|
|
245
|
+
"James",
|
|
246
|
+
"Mary",
|
|
247
|
+
"Robert",
|
|
248
|
+
"Patricia",
|
|
249
|
+
"John",
|
|
250
|
+
"Jennifer",
|
|
251
|
+
"Michael",
|
|
252
|
+
"Linda",
|
|
253
|
+
"David",
|
|
254
|
+
"Elizabeth",
|
|
255
|
+
"William",
|
|
256
|
+
"Barbara",
|
|
257
|
+
"Richard",
|
|
258
|
+
"Susan",
|
|
259
|
+
"Joseph",
|
|
260
|
+
"Jessica"
|
|
261
|
+
];
|
|
262
|
+
while (FIRST_NAMES.length < 2048) {
|
|
263
|
+
FIRST_NAMES.push(`NameEx_${FIRST_NAMES.length}`);
|
|
264
|
+
}
|
|
265
|
+
CONNECTORS = [
|
|
266
|
+
"of",
|
|
267
|
+
"from",
|
|
268
|
+
"van",
|
|
269
|
+
"del",
|
|
270
|
+
"di",
|
|
271
|
+
"von",
|
|
272
|
+
"le",
|
|
273
|
+
"la",
|
|
274
|
+
"de",
|
|
275
|
+
"el",
|
|
276
|
+
"the",
|
|
277
|
+
"near",
|
|
278
|
+
"at",
|
|
279
|
+
"by",
|
|
280
|
+
"under",
|
|
281
|
+
"over",
|
|
282
|
+
"across",
|
|
283
|
+
"beyond",
|
|
284
|
+
"within",
|
|
285
|
+
"without",
|
|
286
|
+
"and",
|
|
287
|
+
"with",
|
|
288
|
+
"aka",
|
|
289
|
+
"alias",
|
|
290
|
+
"formally",
|
|
291
|
+
"lately",
|
|
292
|
+
"born",
|
|
293
|
+
"styled",
|
|
294
|
+
"known",
|
|
295
|
+
"called",
|
|
296
|
+
"st",
|
|
297
|
+
"al",
|
|
298
|
+
"bin",
|
|
299
|
+
"ibn",
|
|
300
|
+
"abu",
|
|
301
|
+
"ben",
|
|
302
|
+
"bar",
|
|
303
|
+
"fitz",
|
|
304
|
+
"mac",
|
|
305
|
+
"mc",
|
|
306
|
+
"o",
|
|
307
|
+
"da",
|
|
308
|
+
"dos",
|
|
309
|
+
"das",
|
|
310
|
+
"do",
|
|
311
|
+
"du",
|
|
312
|
+
"della",
|
|
313
|
+
"degli",
|
|
314
|
+
"dei",
|
|
315
|
+
"delle",
|
|
316
|
+
"sur",
|
|
317
|
+
"ter",
|
|
318
|
+
"ten",
|
|
319
|
+
"zu",
|
|
320
|
+
"zum",
|
|
321
|
+
"zur",
|
|
322
|
+
"auf",
|
|
323
|
+
"an",
|
|
324
|
+
"der",
|
|
325
|
+
"die",
|
|
326
|
+
"das",
|
|
327
|
+
"pro",
|
|
328
|
+
"anti",
|
|
329
|
+
"ex",
|
|
330
|
+
"quasi"
|
|
331
|
+
];
|
|
332
|
+
SURNAME_ROOTS = [
|
|
333
|
+
"Silver",
|
|
334
|
+
"Gold",
|
|
335
|
+
"Iron",
|
|
336
|
+
"Stone",
|
|
337
|
+
"Rock",
|
|
338
|
+
"Wood",
|
|
339
|
+
"Leaf",
|
|
340
|
+
"Rain",
|
|
341
|
+
"Snow",
|
|
342
|
+
"Wind",
|
|
343
|
+
"Storm",
|
|
344
|
+
"Cloud",
|
|
345
|
+
"Sun",
|
|
346
|
+
"Moon",
|
|
347
|
+
"Star",
|
|
348
|
+
"Sky",
|
|
349
|
+
"Sea",
|
|
350
|
+
"Lake",
|
|
351
|
+
"River",
|
|
352
|
+
"Brook",
|
|
353
|
+
"Hill",
|
|
354
|
+
"Mount",
|
|
355
|
+
"Vale",
|
|
356
|
+
"Glen",
|
|
357
|
+
"Dale",
|
|
358
|
+
"Field",
|
|
359
|
+
"Meadow",
|
|
360
|
+
"Forest",
|
|
361
|
+
"Grove",
|
|
362
|
+
"Wild"
|
|
363
|
+
];
|
|
364
|
+
while (SURNAME_ROOTS.length < 4096) {
|
|
365
|
+
SURNAME_ROOTS.push(`RootEx_${SURNAME_ROOTS.length}`);
|
|
366
|
+
}
|
|
367
|
+
SURNAME_SUFFIXES = [
|
|
368
|
+
"son",
|
|
369
|
+
"man",
|
|
370
|
+
"field",
|
|
371
|
+
"wood",
|
|
372
|
+
"berg",
|
|
373
|
+
"stein",
|
|
374
|
+
"ov",
|
|
375
|
+
"ova",
|
|
376
|
+
"ski",
|
|
377
|
+
"ska",
|
|
378
|
+
"ez",
|
|
379
|
+
"ez",
|
|
380
|
+
"ia",
|
|
381
|
+
"ic",
|
|
382
|
+
"os",
|
|
383
|
+
"as",
|
|
384
|
+
"is",
|
|
385
|
+
"us",
|
|
386
|
+
"er",
|
|
387
|
+
"en",
|
|
388
|
+
"ard",
|
|
389
|
+
"ier",
|
|
390
|
+
"eau",
|
|
391
|
+
"oux",
|
|
392
|
+
"ly",
|
|
393
|
+
"ley",
|
|
394
|
+
"ton",
|
|
395
|
+
"don",
|
|
396
|
+
"ham",
|
|
397
|
+
"ford",
|
|
398
|
+
"wick",
|
|
399
|
+
"shire",
|
|
400
|
+
"land",
|
|
401
|
+
"way",
|
|
402
|
+
"side",
|
|
403
|
+
"gate",
|
|
404
|
+
"bridge",
|
|
405
|
+
"well",
|
|
406
|
+
"pool",
|
|
407
|
+
"cliff",
|
|
408
|
+
"bank",
|
|
409
|
+
"shore",
|
|
410
|
+
"hart",
|
|
411
|
+
"foot",
|
|
412
|
+
"head",
|
|
413
|
+
"more",
|
|
414
|
+
"less",
|
|
415
|
+
"ness",
|
|
416
|
+
"ship",
|
|
417
|
+
"ward"
|
|
418
|
+
];
|
|
419
|
+
while (SURNAME_SUFFIXES.length < 512) {
|
|
420
|
+
SURNAME_SUFFIXES.push(`SuffixEx_${SURNAME_SUFFIXES.length}`);
|
|
421
|
+
}
|
|
422
|
+
SYLLABLES = [
|
|
423
|
+
"San",
|
|
424
|
+
"Ver",
|
|
425
|
+
"Dina",
|
|
426
|
+
"Lon",
|
|
427
|
+
"Don",
|
|
428
|
+
"Chi",
|
|
429
|
+
"Ca",
|
|
430
|
+
"Go",
|
|
431
|
+
"New",
|
|
432
|
+
"York",
|
|
433
|
+
"Los",
|
|
434
|
+
"An",
|
|
435
|
+
"Ge",
|
|
436
|
+
"Les",
|
|
437
|
+
"Pa",
|
|
438
|
+
"Ris",
|
|
439
|
+
"Ber",
|
|
440
|
+
"Lin",
|
|
441
|
+
"Mad",
|
|
442
|
+
"Rid"
|
|
443
|
+
];
|
|
444
|
+
while (SYLLABLES.length < 1e3) {
|
|
445
|
+
SYLLABLES.push(`Syl_${SYLLABLES.length}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
230
450
|
// src/core/fpe_utils.ts
|
|
231
451
|
function looksLikeToken(value) {
|
|
232
452
|
if (typeof value !== "string") return false;
|
|
@@ -261,13 +481,20 @@ function looksLikeToken(value) {
|
|
|
261
481
|
if (v7.startsWith("[TKN-") && v7.endsWith("]")) {
|
|
262
482
|
return true;
|
|
263
483
|
}
|
|
484
|
+
if (v7.includes("-") && v7.length >= 6) {
|
|
485
|
+
const parts = v7.split("-");
|
|
486
|
+
const tag = parts[parts.length - 1];
|
|
487
|
+
if (tag.length === 4 && /^\d+$/.test(tag)) {
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
264
491
|
return false;
|
|
265
492
|
}
|
|
266
493
|
var TOKEN_PATTERN;
|
|
267
494
|
var init_fpe_utils = __esm({
|
|
268
495
|
"src/core/fpe_utils.ts"() {
|
|
269
496
|
TOKEN_PATTERN = new RegExp(
|
|
270
|
-
"tkn-[a-f0-9]{8,64}@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}|\\+[1-9]\\d{0,3}-555-\\d{7}|000-00-\\d{4}|4000-0000-0000-\\d{4}|000000\\d{3}|000\\d{5}[A-Z]|[A-Z]{2}00[A-F0-9]{4,16}|<(?:PER|LOC|ORG):[^>]+>|\\[TKN-[a-f0-9]{8,64}
|
|
497
|
+
"tkn-[a-f0-9]{8,64}@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}|\\+[1-9]\\d{0,3}-555-\\d{7}|000-00-\\d{4}|4000-0000-0000-\\d{4}|000000\\d{3}|000\\d{5}[A-Z]|[A-Z]{2}00[A-F0-9]{4,16}|<(?:PER|LOC|ORG):[^>]+>|\\b[A-Z][a-zA-Z, ]+-[0-9]{3,4}\\b|\\\\[TKN-[a-f0-9]{8,64}\\\\]",
|
|
271
498
|
// Opaque
|
|
272
499
|
"g"
|
|
273
500
|
);
|
|
@@ -282,7 +509,7 @@ async function _getMasterKey() {
|
|
|
282
509
|
}
|
|
283
510
|
if (!raw) {
|
|
284
511
|
if (config.MASK_DEV_MODE) {
|
|
285
|
-
raw =
|
|
512
|
+
raw = crypto2.randomBytes(32).toString("hex");
|
|
286
513
|
process.env.MASK_MASTER_KEY = raw;
|
|
287
514
|
} else {
|
|
288
515
|
throw new MaskSecurityError(
|
|
@@ -299,27 +526,64 @@ function resetMasterKey() {
|
|
|
299
526
|
}
|
|
300
527
|
async function _hmacHex(plaintext, n6 = 8) {
|
|
301
528
|
const masterKey = await _getMasterKey();
|
|
302
|
-
const digest =
|
|
529
|
+
const digest = crypto2.createHmac("sha256", masterKey).update(plaintext, "utf-8").digest("hex");
|
|
303
530
|
return digest.slice(0, n6);
|
|
304
531
|
}
|
|
305
|
-
async function
|
|
532
|
+
async function _hmacInt(plaintext) {
|
|
306
533
|
const masterKey = await _getMasterKey();
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
for (let i6 =
|
|
310
|
-
|
|
311
|
-
result.push((parseInt(ch, 16) % 10).toString());
|
|
312
|
-
if (result.length === n6) break;
|
|
534
|
+
const raw = crypto2.createHmac("sha256", masterKey).update(plaintext, "utf-8").digest();
|
|
535
|
+
let result = 0n;
|
|
536
|
+
for (let i6 = 0; i6 < 16; i6++) {
|
|
537
|
+
result = result << 8n | BigInt(raw[i6]);
|
|
313
538
|
}
|
|
314
|
-
|
|
315
|
-
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
async function _hmacDigits(plaintext, n6, offset = 0) {
|
|
542
|
+
const salted = offset ? `${plaintext}::${offset}` : plaintext;
|
|
543
|
+
const seed = await _hmacInt(salted);
|
|
544
|
+
const modulus = 10n ** BigInt(n6);
|
|
545
|
+
return (seed % modulus).toString().padStart(n6, "0");
|
|
546
|
+
}
|
|
547
|
+
async function _getBijectiveTweak() {
|
|
548
|
+
const masterKey = await _getMasterKey();
|
|
549
|
+
let base = config.MASK_TENANT_ID;
|
|
550
|
+
if (config.MASK_SALT_ROTATION !== "NONE") {
|
|
551
|
+
const now = /* @__PURE__ */ new Date();
|
|
552
|
+
if (config.MASK_SALT_ROTATION === "MONTHLY") {
|
|
553
|
+
base += `-${now.getUTCFullYear()}-${now.getUTCMonth() + 1}`;
|
|
554
|
+
} else if (config.MASK_SALT_ROTATION === "YEARLY") {
|
|
555
|
+
base += `-${now.getUTCFullYear()}`;
|
|
556
|
+
}
|
|
316
557
|
}
|
|
317
|
-
return
|
|
558
|
+
return crypto2.createHmac("sha256", masterKey).update(base, "utf-8").digest();
|
|
318
559
|
}
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
|
|
560
|
+
function _renderBijectivePerson(bits) {
|
|
561
|
+
const firstIdx = Number(bits & 0x7FFn);
|
|
562
|
+
const connIdx = Number(bits >> 11n & 0x3Fn);
|
|
563
|
+
const rootIdx = Number(bits >> 17n & 0xFFFn);
|
|
564
|
+
const suffixIdx = Number(bits >> 29n & 0x1FFn);
|
|
565
|
+
const tag = Number(bits >> 38n & 0x3FFFn);
|
|
566
|
+
const formatIdx = Number(bits >> 52n & 0xFn);
|
|
567
|
+
const first = FIRST_NAMES[firstIdx % FIRST_NAMES.length];
|
|
568
|
+
const conn = CONNECTORS[connIdx % CONNECTORS.length];
|
|
569
|
+
const root = SURNAME_ROOTS[rootIdx % SURNAME_ROOTS.length];
|
|
570
|
+
const suffix = SURNAME_SUFFIXES[suffixIdx % SURNAME_SUFFIXES.length];
|
|
571
|
+
const surname = `${root}${suffix}`;
|
|
572
|
+
const numeric = tag % 1e4;
|
|
573
|
+
const paddedNumeric = numeric.toString().padStart(4, "0");
|
|
574
|
+
if (formatIdx === 0) return `${first} ${conn} ${surname}-${paddedNumeric}`;
|
|
575
|
+
if (formatIdx === 1) return `${surname}, ${first}-${paddedNumeric}`;
|
|
576
|
+
if (formatIdx === 2) return `${first[0]}. ${surname}-${paddedNumeric}`;
|
|
577
|
+
if (formatIdx === 3) return `${first} ${surname}-${paddedNumeric}`;
|
|
578
|
+
return `${first} ${surname}-${paddedNumeric}`;
|
|
579
|
+
}
|
|
580
|
+
function _renderBijectiveLocation(bits) {
|
|
581
|
+
const s1 = Number(bits & 0x3FFn);
|
|
582
|
+
const s22 = Number(bits >> 10n & 0x3FFn);
|
|
583
|
+
const s32 = Number(bits >> 20n & 0x3FFn);
|
|
584
|
+
const tag = Number(bits >> 30n & 0xFFFn);
|
|
585
|
+
const city = `${SYLLABLES[s1 % 1e3]}${SYLLABLES[s22 % 1e3].toLowerCase()}${SYLLABLES[s32 % 1e3].toLowerCase()}`;
|
|
586
|
+
return `${city}-${tag.toString().padStart(3, "0")}`;
|
|
323
587
|
}
|
|
324
588
|
function _computeLuhnDigit(partialNum) {
|
|
325
589
|
const digits = partialNum.split("").map(Number);
|
|
@@ -347,7 +611,7 @@ async function generateFPEToken(rawText, entityType = "UNKNOWN") {
|
|
|
347
611
|
else if (_SSN_RE.test(text)) type = "US_SSN";
|
|
348
612
|
else if (_CC_RE.test(text)) type = "CREDIT_CARD";
|
|
349
613
|
else if (_ROUTING_RE.test(text)) type = "US_ROUTING_NUMBER";
|
|
350
|
-
else if (_ES_ID_RE.test(text)) type = "
|
|
614
|
+
else if (_ES_ID_RE.test(text)) type = "ES_ID";
|
|
351
615
|
else if (_IBAN_RE.test(text)) type = "INTL_BANK_IBAN";
|
|
352
616
|
else if (_PHONE_RE.test(text)) type = "PHONE_NUMBER";
|
|
353
617
|
}
|
|
@@ -377,43 +641,98 @@ async function generateFPEToken(rawText, entityType = "UNKNOWN") {
|
|
|
377
641
|
const countryCode = text.length >= 2 && /[a-zA-Z]{2}/.test(text.slice(0, 2)) ? text.slice(0, 2).toUpperCase() : "US";
|
|
378
642
|
return `${countryCode}00${(await _hmacHex(text, 8)).toUpperCase()}`;
|
|
379
643
|
}
|
|
380
|
-
if (type === "ES_DNI") {
|
|
644
|
+
if (type === "ES_ID" || type === "ES_DNI") {
|
|
381
645
|
const digits = `000${await _hmacDigits(text, 5)}`;
|
|
382
646
|
return digits + _computeEsIdCheck(parseInt(digits, 10));
|
|
383
647
|
}
|
|
384
648
|
if (type === "PERSON" || type === "PERSON_NAME") {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
649
|
+
if (config.MASK_BIJECTIVE_MODE) {
|
|
650
|
+
const canonical = text.toLowerCase().trim();
|
|
651
|
+
const hash = crypto2.createHash("sha256").update(canonical, "utf-8").digest();
|
|
652
|
+
const inputInt = hash.readBigUInt64BE(0);
|
|
653
|
+
const masterKey = await _getMasterKey();
|
|
654
|
+
const engine = new FF1(masterKey.slice(0, 16), await _getBijectiveTweak());
|
|
655
|
+
const cipher = engine.encrypt(inputInt);
|
|
656
|
+
return _renderBijectivePerson(cipher);
|
|
657
|
+
}
|
|
658
|
+
return `[TKN-PERSON-${await _hmacHex(text)}]`;
|
|
388
659
|
}
|
|
389
660
|
if (type === "LOCATION" || type === "PHYS_ADDRESS") {
|
|
390
|
-
|
|
391
|
-
|
|
661
|
+
if (config.MASK_BIJECTIVE_MODE) {
|
|
662
|
+
const canonical = text.toLowerCase().trim();
|
|
663
|
+
const hash = crypto2.createHash("sha256").update(canonical, "utf-8").digest();
|
|
664
|
+
const inputInt = hash.readBigUInt64BE(0);
|
|
665
|
+
const masterKey = await _getMasterKey();
|
|
666
|
+
const engine = new FF1(masterKey.slice(0, 16), await _getBijectiveTweak());
|
|
667
|
+
const cipher = engine.encrypt(inputInt);
|
|
668
|
+
return _renderBijectiveLocation(cipher);
|
|
669
|
+
}
|
|
670
|
+
return `[TKN-LOC-${await _hmacHex(text)}]`;
|
|
392
671
|
}
|
|
393
672
|
if (type === "ORGANIZATION") {
|
|
394
|
-
|
|
395
|
-
return `<ORG:${c6}_Inc>`;
|
|
673
|
+
return `[TKN-ORG-${await _hmacHex(text)}]`;
|
|
396
674
|
}
|
|
397
675
|
return `[TKN-${await _hmacHex(text)}]`;
|
|
398
676
|
}
|
|
399
|
-
var _masterKey, _EMAIL_RE, _PHONE_RE, _SSN_RE, _CC_RE, _ROUTING_RE, _ES_ID_RE, _IBAN_RE,
|
|
677
|
+
var _masterKey, _EMAIL_RE, _PHONE_RE, _SSN_RE, _CC_RE, _ROUTING_RE, _ES_ID_RE, _IBAN_RE, FF1;
|
|
400
678
|
var init_fpe = __esm({
|
|
401
679
|
"src/core/fpe.ts"() {
|
|
402
680
|
init_config();
|
|
403
681
|
init_key_provider();
|
|
404
682
|
init_exceptions();
|
|
683
|
+
init_synthesisLibrary();
|
|
405
684
|
init_fpe_utils();
|
|
406
685
|
_masterKey = null;
|
|
407
686
|
_EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
408
|
-
_PHONE_RE = /(?<!\d)(?:\+?1?[\s\-.]?\(?\d{3}\)?[\s\-.]?\d{3}[\s\-.]?\d{4}|\d{3}[\s\-.]?\d{4})(?!\d)/;
|
|
687
|
+
_PHONE_RE = /(?<!\d)(?:\+?1?[\s\-.]?\(?\d{3}\)?[\s\-.]?\d{3}[\s\-.]?\d{4}|\d{3}[\s\-.]?\d{4}|\+\d{2,3}[\s\-.]?\d{3}[\s\-.]?\d{3}[\s\-.]?\d{3,4})(?!\d)/;
|
|
409
688
|
_SSN_RE = /^\d{3}-\d{2}-\d{4}$/;
|
|
410
689
|
_CC_RE = /^(?:\d{4}[ \-]?){3}\d{4}$/;
|
|
411
690
|
_ROUTING_RE = /^\d{9}$/;
|
|
412
691
|
_ES_ID_RE = /^(?:\d{8}[A-Z]|[XYZ]\d{7}[A-Z])$/;
|
|
413
692
|
_IBAN_RE = /^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
693
|
+
FF1 = class {
|
|
694
|
+
/** NIST SP 800-38G FF1 implementation (simplified for 64-bit domains). */
|
|
695
|
+
constructor(key, tweak) {
|
|
696
|
+
this.key = key;
|
|
697
|
+
this.tweak = tweak;
|
|
698
|
+
}
|
|
699
|
+
encrypt(n6) {
|
|
700
|
+
let A3 = n6 >> 32n;
|
|
701
|
+
let B3 = n6 & 0xFFFFFFFFn;
|
|
702
|
+
const radix = 2n ** 32n;
|
|
703
|
+
for (let i6 = 0; i6 < 10; i6++) {
|
|
704
|
+
const tweakInfoBuffer = Buffer.alloc(8);
|
|
705
|
+
tweakInfoBuffer.writeUInt32BE(i6, 0);
|
|
706
|
+
tweakInfoBuffer.writeUInt32BE(Number(B3), 4);
|
|
707
|
+
const tweakInfoCombined = Buffer.concat([this.tweak, tweakInfoBuffer]);
|
|
708
|
+
const h6 = crypto2.createHmac("sha256", this.key).update(tweakInfoCombined).digest();
|
|
709
|
+
const roundVal = BigInt(h6.readUInt32BE(0));
|
|
710
|
+
const Anext = B3;
|
|
711
|
+
const Bnext = (A3 + roundVal) % radix;
|
|
712
|
+
A3 = Anext;
|
|
713
|
+
B3 = Bnext;
|
|
714
|
+
}
|
|
715
|
+
return A3 << 32n | B3;
|
|
716
|
+
}
|
|
717
|
+
decrypt(n6) {
|
|
718
|
+
let A3 = n6 >> 32n;
|
|
719
|
+
let B3 = n6 & 0xFFFFFFFFn;
|
|
720
|
+
const radix = 2n ** 32n;
|
|
721
|
+
for (let i6 = 9; i6 >= 0; i6--) {
|
|
722
|
+
const tweakInfoBuffer = Buffer.alloc(8);
|
|
723
|
+
tweakInfoBuffer.writeUInt32BE(i6, 0);
|
|
724
|
+
tweakInfoBuffer.writeUInt32BE(Number(A3), 4);
|
|
725
|
+
const tweakInfoCombined = Buffer.concat([this.tweak, tweakInfoBuffer]);
|
|
726
|
+
const h6 = crypto2.createHmac("sha256", this.key).update(tweakInfoCombined).digest();
|
|
727
|
+
const roundVal = BigInt(h6.readUInt32BE(0));
|
|
728
|
+
const Bprev = A3;
|
|
729
|
+
const Aprev = (B3 - roundVal + radix) % radix;
|
|
730
|
+
A3 = Aprev;
|
|
731
|
+
B3 = Bprev;
|
|
732
|
+
}
|
|
733
|
+
return A3 << 32n | B3;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
417
736
|
}
|
|
418
737
|
});
|
|
419
738
|
|
|
@@ -2881,7 +3200,7 @@ var init_crypto = __esm({
|
|
|
2881
3200
|
let key;
|
|
2882
3201
|
if (!keyFromProvider) {
|
|
2883
3202
|
if (config.MASK_DEV_MODE) {
|
|
2884
|
-
key =
|
|
3203
|
+
key = crypto2.randomBytes(32).toString("base64");
|
|
2885
3204
|
process.env.MASK_ENCRYPTION_KEY = key;
|
|
2886
3205
|
console.warn(
|
|
2887
3206
|
"MASK_DEV_MODE is enabled. Using a generated throwaway key. DO NOT USE THIS IN PRODUCTION \u2014 tokens will be lost on restart."
|
|
@@ -2894,10 +3213,10 @@ var init_crypto = __esm({
|
|
|
2894
3213
|
} else {
|
|
2895
3214
|
key = keyFromProvider;
|
|
2896
3215
|
}
|
|
2897
|
-
this._aesKey =
|
|
3216
|
+
this._aesKey = crypto2.createHash("sha256").update(key).digest();
|
|
2898
3217
|
const masterKey = await provider.getMasterKey() || key;
|
|
2899
3218
|
const salt = config.MASK_BLIND_INDEX_SALT;
|
|
2900
|
-
this._indexSecret =
|
|
3219
|
+
this._indexSecret = crypto2.createHmac("sha256", masterKey).update(salt).digest();
|
|
2901
3220
|
}
|
|
2902
3221
|
/** Return the secret used for HMAC-based blind indexing. */
|
|
2903
3222
|
async getIndexSecret() {
|
|
@@ -2910,8 +3229,8 @@ var init_crypto = __esm({
|
|
|
2910
3229
|
if (!this._aesKey) {
|
|
2911
3230
|
throw new Error("CryptoEngine not initialised. AES key missing.");
|
|
2912
3231
|
}
|
|
2913
|
-
const iv =
|
|
2914
|
-
const cipher =
|
|
3232
|
+
const iv = crypto2.randomBytes(GCM_IV_BYTES);
|
|
3233
|
+
const cipher = crypto2.createCipheriv(GCM_ALGORITHM, this._aesKey, iv);
|
|
2915
3234
|
const encrypted = Buffer.concat([
|
|
2916
3235
|
cipher.update(plaintext, "utf8"),
|
|
2917
3236
|
cipher.final()
|
|
@@ -2943,7 +3262,7 @@ var init_crypto = __esm({
|
|
|
2943
3262
|
const iv = combined.subarray(0, GCM_IV_BYTES);
|
|
2944
3263
|
const authTag = combined.subarray(GCM_IV_BYTES, GCM_IV_BYTES + GCM_AUTH_TAG_BYTES);
|
|
2945
3264
|
const encrypted = combined.subarray(GCM_IV_BYTES + GCM_AUTH_TAG_BYTES);
|
|
2946
|
-
const decipher =
|
|
3265
|
+
const decipher = crypto2.createDecipheriv(GCM_ALGORITHM, this._aesKey, iv);
|
|
2947
3266
|
decipher.setAuthTag(authTag);
|
|
2948
3267
|
const decrypted = Buffer.concat([
|
|
2949
3268
|
decipher.update(encrypted),
|
|
@@ -3127,7 +3446,8 @@ var init_audit_logger = __esm({
|
|
|
3127
3446
|
this._buffer = [];
|
|
3128
3447
|
this._bufferFullWarned = false;
|
|
3129
3448
|
for (const evt of events) {
|
|
3130
|
-
|
|
3449
|
+
const json = JSON.stringify(evt, (_, v7) => typeof v7 === "bigint" ? v7.toString() : v7);
|
|
3450
|
+
console.info(json);
|
|
3131
3451
|
}
|
|
3132
3452
|
} finally {
|
|
3133
3453
|
this._isFlushing = false;
|
|
@@ -3191,7 +3511,7 @@ var init_search = __esm({
|
|
|
3191
3511
|
static async getBucketIndex(bucketVal) {
|
|
3192
3512
|
const engine = await getCryptoEngine();
|
|
3193
3513
|
const secret = await engine.getIndexSecret();
|
|
3194
|
-
return
|
|
3514
|
+
return crypto2.createHmac("sha256", secret).update(bucketVal).digest("hex");
|
|
3195
3515
|
}
|
|
3196
3516
|
};
|
|
3197
3517
|
}
|
|
@@ -17735,7 +18055,7 @@ var init_date_utils = __esm({
|
|
|
17735
18055
|
var randomUUID;
|
|
17736
18056
|
var init_randomUUID = __esm({
|
|
17737
18057
|
"node_modules/@smithy/uuid/dist-es/randomUUID.js"() {
|
|
17738
|
-
randomUUID =
|
|
18058
|
+
randomUUID = crypto2__default.randomUUID.bind(crypto2__default);
|
|
17739
18059
|
}
|
|
17740
18060
|
});
|
|
17741
18061
|
|
|
@@ -42971,9 +43291,9 @@ function _getFailStrategy() {
|
|
|
42971
43291
|
function _hashPlaintext(plaintext, secret) {
|
|
42972
43292
|
const trimmed = plaintext.trim();
|
|
42973
43293
|
if (secret) {
|
|
42974
|
-
return
|
|
43294
|
+
return crypto2.createHmac("sha256", secret).update(trimmed, "utf-8").digest("hex");
|
|
42975
43295
|
}
|
|
42976
|
-
return
|
|
43296
|
+
return crypto2.createHash("sha256").update(trimmed, "utf-8").digest("hex");
|
|
42977
43297
|
}
|
|
42978
43298
|
function getVault() {
|
|
42979
43299
|
if (_vaultInstance === null) {
|
|
@@ -43888,7 +44208,7 @@ var init_registry = __esm({
|
|
|
43888
44208
|
}
|
|
43889
44209
|
buildCatalogue(restrict) {
|
|
43890
44210
|
for (const entry of RAW_PATTERNS) {
|
|
43891
|
-
const [typeName, regexSource, terms, risk, cat, vtag, isHighEntropy, supportedLocales] = entry;
|
|
44211
|
+
const [typeName, regexSource, terms, risk, cat, vtag, isHighEntropy, supportedLocales, ruleId, complianceScope] = entry;
|
|
43892
44212
|
if (restrict !== null && !restrict.has(cat)) continue;
|
|
43893
44213
|
let re;
|
|
43894
44214
|
if (regexSource instanceof RegExp) {
|
|
@@ -43903,7 +44223,9 @@ var init_registry = __esm({
|
|
|
43903
44223
|
category: cat,
|
|
43904
44224
|
validatorTag: vtag,
|
|
43905
44225
|
isHighEntropy: isHighEntropy ?? vtag !== null,
|
|
43906
|
-
supportedLocales: supportedLocales ?? ["*"]
|
|
44226
|
+
supportedLocales: supportedLocales ?? ["*"],
|
|
44227
|
+
ruleId: ruleId ?? `MASK-${cat.slice(0, 3)}-${typeName}`,
|
|
44228
|
+
complianceScope: new Set(complianceScope ?? [])
|
|
43907
44229
|
});
|
|
43908
44230
|
}
|
|
43909
44231
|
}
|
|
@@ -59181,7 +59503,9 @@ var init_scanner = __esm({
|
|
|
59181
59503
|
originalValue: matchedStr,
|
|
59182
59504
|
confidence: conf,
|
|
59183
59505
|
method: "dlp_heuristic",
|
|
59184
|
-
language: detectedLanguage
|
|
59506
|
+
language: detectedLanguage,
|
|
59507
|
+
ruleId: descriptor.ruleId,
|
|
59508
|
+
complianceScope: descriptor.complianceScope
|
|
59185
59509
|
});
|
|
59186
59510
|
}
|
|
59187
59511
|
}
|
|
@@ -59621,7 +59945,7 @@ init_handlers();
|
|
|
59621
59945
|
init_scorer();
|
|
59622
59946
|
|
|
59623
59947
|
// src/index.ts
|
|
59624
|
-
var VERSION = "3.5.
|
|
59948
|
+
var VERSION = "3.5.1";
|
|
59625
59949
|
async function detectEntitiesWithConfidence(text, options = {}) {
|
|
59626
59950
|
const scanner = getScanner();
|
|
59627
59951
|
return await scanner.scanAndReturnEntities(text, options);
|