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.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 cryptoNode from 'crypto';
8
- import cryptoNode__default, { createHmac, createHash, createPrivateKey, createPublicKey, sign } from 'crypto';
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 = cryptoNode.randomBytes(32).toString("hex");
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 = cryptoNode.createHmac("sha256", masterKey).update(plaintext, "utf-8").digest("hex");
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 _hmacDigits(plaintext, n6, offset = 0) {
532
+ async function _hmacInt(plaintext) {
306
533
  const masterKey = await _getMasterKey();
307
- const digest = cryptoNode.createHmac("sha256", masterKey).update(plaintext, "utf-8").digest("hex");
308
- const result = [];
309
- for (let i6 = offset; i6 < digest.length; i6++) {
310
- const ch = digest[i6];
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
- while (result.length < n6) {
315
- result.push("0");
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 result.join("");
558
+ return crypto2.createHmac("sha256", masterKey).update(base, "utf-8").digest();
318
559
  }
319
- async function _pickFromArray(plaintext, array) {
320
- const digits = await _hmacDigits(plaintext, 8);
321
- const num = parseInt(digits, 10);
322
- return array[num % array.length];
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 = "ES_DNI";
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
- const f6 = await _pickFromArray(text, _FIRST_NAMES);
386
- const l6 = await _pickFromArray(text + "last", _LAST_NAMES);
387
- return `<PER:${f6}_${l6}>`;
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
- const c6 = await _pickFromArray(text, _CITIES);
391
- return `<LOC:${c6}>`;
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
- const c6 = await _pickFromArray(text, _LAST_NAMES);
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, _FIRST_NAMES, _LAST_NAMES, _CITIES;
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
- _FIRST_NAMES = ["Taylor", "Jordan", "Casey", "Morgan", "Riley", "Avery", "Rowan", "Quinn", "Charlie", "Peyton", "Blake", "Dakota", "Reese", "Skyler", "Finley", "Eden", "Harley", "Rory", "Emerson", "Remi"];
415
- _LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin"];
416
- _CITIES = ["London", "Paris", "Berlin", "Tokyo", "Rome", "Madrid", "Vienna", "Sydney", "Toronto", "Chicago", "Seattle", "Austin", "Boston", "Denver", "Dallas", "Miami", "Seoul", "Dubai", "Mumbai", "Cairo"];
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 = cryptoNode.randomBytes(32).toString("base64");
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 = cryptoNode.createHash("sha256").update(key).digest();
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 = cryptoNode.createHmac("sha256", masterKey).update(salt).digest();
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 = cryptoNode.randomBytes(GCM_IV_BYTES);
2914
- const cipher = cryptoNode.createCipheriv(GCM_ALGORITHM, this._aesKey, iv);
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 = cryptoNode.createDecipheriv(GCM_ALGORITHM, this._aesKey, iv);
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
- console.info(JSON.stringify(evt));
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 cryptoNode.createHmac("sha256", secret).update(bucketVal).digest("hex");
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 = cryptoNode__default.randomUUID.bind(cryptoNode__default);
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 cryptoNode.createHmac("sha256", secret).update(trimmed, "utf-8").digest("hex");
43294
+ return crypto2.createHmac("sha256", secret).update(trimmed, "utf-8").digest("hex");
42975
43295
  }
42976
- return cryptoNode.createHash("sha256").update(trimmed, "utf-8").digest("hex");
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.0";
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);