@socprime/logtotal-sanitizer 0.2.0-beta.3 → 0.2.1-beta.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/cli.js CHANGED
@@ -385,7 +385,40 @@ var geoLocationRule = {
385
385
  "postcode",
386
386
  "zip",
387
387
  "zipCode",
388
- "geohash"
388
+ "geohash",
389
+ "address",
390
+ "homeAddress",
391
+ "billingAddress",
392
+ "shippingAddress",
393
+ "mailingAddress",
394
+ "postalAddress",
395
+ "residentialAddress",
396
+ "street",
397
+ "streetName",
398
+ "houseNumber",
399
+ "addressLine",
400
+ "addressLine3",
401
+ "postCode",
402
+ "plz",
403
+ "gps",
404
+ "gpsCoordinates",
405
+ "geoPoint",
406
+ "geoLocation",
407
+ "latLng",
408
+ "latLon"
409
+ ],
410
+ jsonKeyContains: [
411
+ "streetaddress",
412
+ "homeaddress",
413
+ "billingaddress",
414
+ "shippingaddress",
415
+ "mailingaddress",
416
+ "postaladdress",
417
+ "addressline",
418
+ "latitude",
419
+ "longitude",
420
+ "postalcode",
421
+ "zipcode"
389
422
  ]
390
423
  };
391
424
 
@@ -493,7 +526,60 @@ var govIdentifiersRule = {
493
526
  "licenseNumber",
494
527
  "personalId",
495
528
  "dob",
496
- "dateOfBirth"
529
+ "dateOfBirth",
530
+ "passportNo",
531
+ "passportId",
532
+ "idNumber",
533
+ "idCardNumber",
534
+ "identityCardNumber",
535
+ "nationalIdNumber",
536
+ "nationalInsuranceNumber",
537
+ "sin",
538
+ "socialInsuranceNumber",
539
+ "tin",
540
+ "taxNumber",
541
+ "taxIdentificationNumber",
542
+ "ein",
543
+ "itin",
544
+ "ssnLast4",
545
+ "driversLicense",
546
+ "driverLicenseNumber",
547
+ "drivingLicence",
548
+ "dlNumber",
549
+ "personalCode",
550
+ "personalNumber",
551
+ "birthNumber",
552
+ "rodneCislo",
553
+ "pesel",
554
+ "oib",
555
+ "cnp",
556
+ "dni",
557
+ "nie",
558
+ "nif",
559
+ "nir",
560
+ "codiceFiscale",
561
+ "steuerId",
562
+ "bsn",
563
+ "cpf",
564
+ "curp",
565
+ "aadhaar",
566
+ "rnokpp",
567
+ "ipn",
568
+ "inn",
569
+ "snils",
570
+ "birthDate",
571
+ "placeOfBirth",
572
+ "mothersMaidenName",
573
+ "residencePermit",
574
+ "voterId"
575
+ ],
576
+ jsonKeyContains: [
577
+ "passport",
578
+ "taxid",
579
+ "nationalid",
580
+ "socialsecurity",
581
+ "driverlicen",
582
+ "drivinglicen"
497
583
  ]
498
584
  };
499
585
 
@@ -548,8 +634,42 @@ var healthInfoRule = {
548
634
  "insuranceNumber",
549
635
  "labResult",
550
636
  "observation",
551
- "vitalSigns"
552
- ]
637
+ "vitalSigns",
638
+ "diagnoses",
639
+ "diagnosisCode",
640
+ "icd",
641
+ "icdCode",
642
+ "medications",
643
+ "medicationName",
644
+ "drug",
645
+ "dosage",
646
+ "prescriptions",
647
+ "treatment",
648
+ "procedureCode",
649
+ "cptCode",
650
+ "symptom",
651
+ "symptoms",
652
+ "disease",
653
+ "illness",
654
+ "disability",
655
+ "medicalCondition",
656
+ "healthCondition",
657
+ "medicalHistory",
658
+ "medicalRecord",
659
+ "healthRecord",
660
+ "clinicalNotes",
661
+ "labResults",
662
+ "bloodGroup",
663
+ "vaccination",
664
+ "immunization",
665
+ "healthInsuranceNumber",
666
+ "insuranceId",
667
+ "npi",
668
+ "deaNumber",
669
+ "patientName",
670
+ "patientDob"
671
+ ],
672
+ jsonKeyContains: ["diagnos", "medication", "prescription", "allerg", "patient"]
553
673
  };
554
674
 
555
675
  // src/rules/definitions/homePaths.ts
@@ -1518,7 +1638,10 @@ var INTERNAL_SUFFIXES = [
1518
1638
  "lan",
1519
1639
  "home",
1520
1640
  "localdomain",
1521
- "arpa"
1641
+ "arpa",
1642
+ "test",
1643
+ "invalid",
1644
+ "localhost"
1522
1645
  ];
1523
1646
  var INTERNAL_COMPOUND_SUFFIXES = [
1524
1647
  "svc\\.cluster\\.local",
@@ -1625,9 +1748,34 @@ var hostnamesRule = {
1625
1748
  "nodeName",
1626
1749
  "dnsName",
1627
1750
  "machineName",
1628
- "targetHost"
1751
+ "targetHost",
1752
+ "remoteHost",
1753
+ "sourceHost",
1754
+ "destinationHost",
1755
+ "srcHost",
1756
+ "dstHost",
1757
+ "shost",
1758
+ "dhost",
1759
+ "clientHost",
1760
+ "hostFqdn",
1761
+ "workstation",
1762
+ "workstationName",
1763
+ "deviceName",
1764
+ "deviceHostname",
1765
+ "sni",
1766
+ "serverNameIndication",
1767
+ "tlsServerName",
1768
+ "virtualHost",
1769
+ "vhost",
1770
+ "httpHost",
1771
+ "xForwardedHost",
1772
+ "qname",
1773
+ "queryName",
1774
+ "dnsDomain",
1775
+ "smtpHost",
1776
+ "mailServer"
1629
1777
  ],
1630
- jsonKeyContains: ["computer", "domainname"]
1778
+ jsonKeyContains: ["computer", "domainname", "hostname", "fqdn", "workstation"]
1631
1779
  };
1632
1780
 
1633
1781
  // src/rules/definitions/ipAddresses.ts
@@ -1703,7 +1851,63 @@ var ipAddressesRule = {
1703
1851
  "destinationIp",
1704
1852
  "xForwardedFor",
1705
1853
  "mac",
1706
- "macAddress"
1854
+ "macAddress",
1855
+ "ipv4",
1856
+ "ipv6",
1857
+ "ipAddr",
1858
+ "clientAddress",
1859
+ "clientAddr",
1860
+ "serverAddress",
1861
+ "remoteIp",
1862
+ "remoteAddress",
1863
+ "peerIp",
1864
+ "peerAddress",
1865
+ "srcAddr",
1866
+ "dstAddr",
1867
+ "sourceAddress",
1868
+ "destinationAddress",
1869
+ "src",
1870
+ "dst",
1871
+ "srcIpAddr",
1872
+ "dstIpAddr",
1873
+ "localIp",
1874
+ "publicIp",
1875
+ "privateIp",
1876
+ "externalIp",
1877
+ "internalIp",
1878
+ "hostIp",
1879
+ "serverIp",
1880
+ "nasIpAddress",
1881
+ "callerIpAddress",
1882
+ "sourceIPAddress",
1883
+ "trueClientIp",
1884
+ "xRealIp",
1885
+ "cfConnectingIp",
1886
+ "forwardedFor",
1887
+ "natIp",
1888
+ "gatewayIp",
1889
+ "macAddr",
1890
+ "hwAddr",
1891
+ "clientMac",
1892
+ "srcMac",
1893
+ "dstMac",
1894
+ "smac",
1895
+ "dmac",
1896
+ "bssid"
1897
+ ],
1898
+ jsonKeyContains: [
1899
+ "ipaddr",
1900
+ "macaddr",
1901
+ "networkaddress",
1902
+ "remoteaddr",
1903
+ "clientaddr",
1904
+ "peeraddr",
1905
+ "clientip",
1906
+ "sourceip",
1907
+ "remoteip",
1908
+ "srcip",
1909
+ "dstip",
1910
+ "destinationip"
1707
1911
  ]
1708
1912
  };
1709
1913
 
@@ -2133,8 +2337,41 @@ var paymentInfoRule = {
2133
2337
  "routingNumber",
2134
2338
  "bic",
2135
2339
  "swift",
2136
- "expiryDate"
2137
- ]
2340
+ "expiryDate",
2341
+ "ccNumber",
2342
+ "cardNo",
2343
+ "cardNum",
2344
+ "creditCardNumber",
2345
+ "debitCardNumber",
2346
+ "primaryAccountNumber",
2347
+ "cvc2",
2348
+ "cvn",
2349
+ "csc",
2350
+ "securityCode",
2351
+ "cardSecurityCode",
2352
+ "cardCvv",
2353
+ "cardExpiry",
2354
+ "expDate",
2355
+ "expirationDate",
2356
+ "bankAccount",
2357
+ "bankAccountNumber",
2358
+ "acctNumber",
2359
+ "sortCode",
2360
+ "bsb",
2361
+ "swiftCode",
2362
+ "bicCode",
2363
+ "ibanNumber",
2364
+ "walletAddress",
2365
+ "btcAddress",
2366
+ "ethAddress",
2367
+ "cardHolder",
2368
+ "cardholderName",
2369
+ "nameOnCard",
2370
+ "track1",
2371
+ "track2",
2372
+ "trackData"
2373
+ ],
2374
+ jsonKeyContains: ["iban", "cardnumber", "cvv", "creditcard", "accountnumber"]
2138
2375
  };
2139
2376
 
2140
2377
  // src/rules/definitions/phoneNumbers.ts
@@ -2173,8 +2410,30 @@ var phoneNumbersRule = {
2173
2410
  "tel",
2174
2411
  "fax",
2175
2412
  "msisdn",
2176
- "contactNumber"
2177
- ]
2413
+ "contactNumber",
2414
+ "phoneNo",
2415
+ "phoneNum",
2416
+ "mobilePhone",
2417
+ "mobileNo",
2418
+ "cellPhone",
2419
+ "homePhone",
2420
+ "workPhone",
2421
+ "businessPhone",
2422
+ "telephoneNumber",
2423
+ "otherTelephone",
2424
+ "telNo",
2425
+ "faxNumber",
2426
+ "contactPhone",
2427
+ "primaryPhone",
2428
+ "callerId",
2429
+ "callerNumber",
2430
+ "callingNumber",
2431
+ "calledNumber",
2432
+ "ani",
2433
+ "dnis",
2434
+ "whatsapp"
2435
+ ],
2436
+ jsonKeyContains: ["phone", "msisdn", "mobilenumber", "faxnumber"]
2178
2437
  };
2179
2438
 
2180
2439
  // src/rules/definitions/secrets.ts
@@ -2268,7 +2527,66 @@ var secretsRule = {
2268
2527
  "xApiKey",
2269
2528
  "idToken",
2270
2529
  "awsSecretAccessKey",
2271
- "signature"
2530
+ "signature",
2531
+ "accessKey",
2532
+ "accessKeyId",
2533
+ "secretAccessKey",
2534
+ "awsSessionToken",
2535
+ "xAmzSecurityToken",
2536
+ "bearerToken",
2537
+ "authToken",
2538
+ "authKey",
2539
+ "apiToken",
2540
+ "appSecret",
2541
+ "consumerSecret",
2542
+ "oauthToken",
2543
+ "oauthTokenSecret",
2544
+ "masterKey",
2545
+ "encryptionKey",
2546
+ "sharedKey",
2547
+ "sharedSecret",
2548
+ "webhookSecret",
2549
+ "signingSecret",
2550
+ "signingKey",
2551
+ "connectionString",
2552
+ "connStr",
2553
+ "passphrase",
2554
+ "passwordHash",
2555
+ "pwdHash",
2556
+ "otp",
2557
+ "totp",
2558
+ "mfaCode",
2559
+ "oneTimePassword",
2560
+ "verificationCode",
2561
+ "recoveryCode",
2562
+ "sshKey",
2563
+ "subscriptionKey",
2564
+ "ocpApimSubscriptionKey",
2565
+ "xAuthToken",
2566
+ "xAccessToken",
2567
+ "proxyAuthorization",
2568
+ "sasToken",
2569
+ "vaultToken",
2570
+ "xVaultToken",
2571
+ "jwt",
2572
+ "samlResponse",
2573
+ "samlAssertion",
2574
+ "pinBlock"
2575
+ ],
2576
+ jsonKeyContains: [
2577
+ "password",
2578
+ "passwd",
2579
+ "secret",
2580
+ "apikey",
2581
+ "accesstoken",
2582
+ "refreshtoken",
2583
+ "idtoken",
2584
+ "authtoken",
2585
+ "bearertoken",
2586
+ "privatekey",
2587
+ "credential",
2588
+ "passphrase",
2589
+ "connectionstring"
2272
2590
  ]
2273
2591
  };
2274
2592
 
@@ -2299,7 +2617,41 @@ var sessionCookiesRule = {
2299
2617
  `(?:(?<![\\w-])[\\w-]*session[\\w-]*=${COOKIE_VALUE})`,
2300
2618
  `(?:(?<![\\w-])[\\w-]*token=${COOKIE_VALUE})`
2301
2619
  ],
2302
- jsonKeys: ["sessionId", "sid", "cookie", "cookies", "setCookie", "jsessionid", "csrfToken"]
2620
+ jsonKeys: [
2621
+ "sessionId",
2622
+ "sid",
2623
+ "cookie",
2624
+ "cookies",
2625
+ "setCookie",
2626
+ "jsessionid",
2627
+ "csrfToken",
2628
+ "sessionCookie",
2629
+ "sessionToken",
2630
+ "sessionKey",
2631
+ "session",
2632
+ "phpsessid",
2633
+ "aspNetSessionId",
2634
+ "connectSid",
2635
+ "laravelSession",
2636
+ "xsrfToken",
2637
+ "xCsrfToken",
2638
+ "xXsrfToken",
2639
+ "csrf",
2640
+ "rememberToken",
2641
+ "authCookie",
2642
+ "cookieHeader",
2643
+ "requestCookies",
2644
+ "responseCookies"
2645
+ ],
2646
+ jsonKeyContains: [
2647
+ "sessionid",
2648
+ "sessiontoken",
2649
+ "sessioncookie",
2650
+ "sessionkey",
2651
+ "cookie",
2652
+ "csrf",
2653
+ "xsrf"
2654
+ ]
2303
2655
  };
2304
2656
 
2305
2657
  // src/rules/definitions/users.ts
@@ -2419,9 +2771,71 @@ var usersRule = {
2419
2771
  "givenName",
2420
2772
  "surname",
2421
2773
  "userId",
2422
- "uid"
2774
+ "uid",
2775
+ "usr",
2776
+ "usrName",
2777
+ "usrEmail",
2778
+ "usrId",
2779
+ "loginName",
2780
+ "loginId",
2781
+ "logonName",
2782
+ "userSid",
2783
+ "requestedBy",
2784
+ "submittedBy",
2785
+ "approvedBy",
2786
+ "updatedBy",
2787
+ "deletedBy",
2788
+ "author",
2789
+ "committer",
2790
+ "reporter",
2791
+ "assignee",
2792
+ "customerName",
2793
+ "customerEmail",
2794
+ "contactEmail",
2795
+ "contactName",
2796
+ "recipient",
2797
+ "recipients",
2798
+ "sender",
2799
+ "replyTo",
2800
+ "mailFrom",
2801
+ "rcptTo",
2802
+ "envelopeFrom",
2803
+ "envelopeTo",
2804
+ "emails",
2805
+ "primaryEmail",
2806
+ "workEmail",
2807
+ "personalEmail",
2808
+ "middleName",
2809
+ "nickname",
2810
+ "maidenName",
2811
+ "familyName",
2812
+ "legalName",
2813
+ "realName",
2814
+ "gecos",
2815
+ "distinguishedName",
2816
+ "userDn",
2817
+ "userDisplayName",
2818
+ "employeeId",
2819
+ "employeeNumber",
2820
+ "staffId",
2821
+ "studentId",
2822
+ "impersonatedUser",
2823
+ "effectiveUser",
2824
+ "sudoUser",
2825
+ "ruser",
2826
+ "suser",
2827
+ "duser"
2423
2828
  ],
2424
- jsonKeyContains: ["owner", "username"]
2829
+ jsonKeyContains: [
2830
+ "owner",
2831
+ "username",
2832
+ "email",
2833
+ "principalname",
2834
+ "loginname",
2835
+ "firstname",
2836
+ "lastname",
2837
+ "fullname"
2838
+ ]
2425
2839
  };
2426
2840
 
2427
2841
  // src/rules/registry.ts
@@ -2757,6 +3171,181 @@ function createLineSplitter(options = {}) {
2757
3171
  return { push, flush };
2758
3172
  }
2759
3173
 
3174
+ // src/core/jsonKeyMatcher.ts
3175
+ var RESOLVE_CACHE_LIMIT = 1e4;
3176
+ function normalizeKey2(key) {
3177
+ return key.toLowerCase().replace(/[-_.\s]/g, "");
3178
+ }
3179
+ function shouldPreserveJsonValue(value) {
3180
+ return value.length === 0 || value === "-";
3181
+ }
3182
+ function buildTables(rules) {
3183
+ const exact = /* @__PURE__ */ new Map();
3184
+ const contains = [];
3185
+ for (const rule of rules) {
3186
+ for (const key of rule.jsonKeys ?? []) {
3187
+ const normalized = normalizeKey2(key);
3188
+ if (!exact.has(normalized)) {
3189
+ exact.set(normalized, rule);
3190
+ }
3191
+ }
3192
+ for (const fragment of rule.jsonKeyContains ?? []) {
3193
+ contains.push({ fragment: normalizeKey2(fragment), rule });
3194
+ }
3195
+ }
3196
+ return { exact, contains };
3197
+ }
3198
+ function lastSegment(key) {
3199
+ const dot = key.lastIndexOf(".");
3200
+ return dot === -1 ? key : key.slice(dot + 1);
3201
+ }
3202
+ function resolveUncached(tables, key, parent) {
3203
+ const dot = key.lastIndexOf(".");
3204
+ const leaf = normalizeKey2(dot === -1 ? key : key.slice(dot + 1));
3205
+ const parentRaw = dot === -1 ? parent : key.slice(0, dot);
3206
+ if (leaf.length === 0) {
3207
+ return null;
3208
+ }
3209
+ if (parentRaw !== void 0) {
3210
+ const scoped = tables.exact.get(normalizeKey2(lastSegment(parentRaw)) + leaf);
3211
+ if (scoped) {
3212
+ return scoped;
3213
+ }
3214
+ }
3215
+ const exact = tables.exact.get(leaf);
3216
+ if (exact) {
3217
+ return exact;
3218
+ }
3219
+ for (const entry of tables.contains) {
3220
+ if (leaf.includes(entry.fragment)) {
3221
+ return entry.rule;
3222
+ }
3223
+ }
3224
+ return null;
3225
+ }
3226
+ function buildMatcher(rules) {
3227
+ const tables = buildTables(rules);
3228
+ const cache = /* @__PURE__ */ new Map();
3229
+ return {
3230
+ resolve(key, parent) {
3231
+ const cacheKey = parent === void 0 ? key : `${parent}\0${key}`;
3232
+ let rule = cache.get(cacheKey);
3233
+ if (rule === void 0) {
3234
+ rule = resolveUncached(tables, key, parent);
3235
+ if (cache.size >= RESOLVE_CACHE_LIMIT) {
3236
+ cache.clear();
3237
+ }
3238
+ cache.set(cacheKey, rule);
3239
+ }
3240
+ return rule ?? void 0;
3241
+ }
3242
+ };
3243
+ }
3244
+ var matcherCache = /* @__PURE__ */ new WeakMap();
3245
+ function jsonKeyMatcher(rules) {
3246
+ const cached = matcherCache.get(rules);
3247
+ if (cached) {
3248
+ return cached;
3249
+ }
3250
+ const matcher = buildMatcher(rules);
3251
+ matcherCache.set(rules, matcher);
3252
+ return matcher;
3253
+ }
3254
+
3255
+ // src/core/redactKeyValuePairs.ts
3256
+ var NUMBER = "(-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)(?![\\w.:+-])";
3257
+ var DOUBLE_QUOTED = new RegExp(
3258
+ `(?<![\\\\\\w])"((?:[^"\\\\\\r\\n]|\\\\.){1,128})"\\s*:\\s*(?:"((?:[^"\\\\]|\\\\.)*)"|${NUMBER})`,
3259
+ "g"
3260
+ );
3261
+ var ESCAPED = new RegExp(
3262
+ `\\\\"((?:[^"\\\\\\r\\n]|\\\\\\\\){1,128})\\\\"\\s*:\\s*(?:\\\\"((?:\\\\\\\\\\\\"|\\\\\\\\|\\\\[^"\\\\]|[^"\\\\])*)\\\\"|${NUMBER})`,
3263
+ "g"
3264
+ );
3265
+ var SINGLE_QUOTED = new RegExp(
3266
+ `(?<![\\\\\\w])'((?:[^'\\\\\\r\\n]|\\\\.){1,128})'\\s*:\\s*(?:'((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|${NUMBER})`,
3267
+ "g"
3268
+ );
3269
+ function decodeJsonString(raw) {
3270
+ if (!raw.includes("\\")) {
3271
+ return raw;
3272
+ }
3273
+ try {
3274
+ return JSON.parse(`"${raw}"`);
3275
+ } catch {
3276
+ return raw;
3277
+ }
3278
+ }
3279
+ function decodeEscapedJsonString(raw) {
3280
+ return decodeJsonString(decodeJsonString(raw));
3281
+ }
3282
+ function decodePythonString(raw) {
3283
+ return raw.includes("\\") ? raw.replace(/\\(['"\\])/g, "$1") : raw;
3284
+ }
3285
+ function collect(line, regex, rules, decode, numberQuote, out) {
3286
+ const matcher = jsonKeyMatcher(rules);
3287
+ regex.lastIndex = 0;
3288
+ for (let found = regex.exec(line); found !== null; found = regex.exec(line)) {
3289
+ const rawKey = found[1];
3290
+ let rawValue;
3291
+ let quote = "";
3292
+ for (let group = 2; group < found.length; group += 1) {
3293
+ if (found[group] !== void 0) {
3294
+ rawValue = found[group];
3295
+ quote = group === found.length - 1 ? numberQuote : "";
3296
+ break;
3297
+ }
3298
+ }
3299
+ if (rawValue === void 0) {
3300
+ continue;
3301
+ }
3302
+ const value = quote === "" ? decode(rawValue) : rawValue;
3303
+ if (shouldPreserveJsonValue(value)) {
3304
+ continue;
3305
+ }
3306
+ const rule = matcher.resolve(decode(rawKey));
3307
+ if (!rule) {
3308
+ continue;
3309
+ }
3310
+ const end = found.index + found[0].length - (quote === "" ? 1 : 0);
3311
+ const valueEnd = quote === "" && regex === ESCAPED ? end - 1 : end;
3312
+ out.push({
3313
+ keyStart: found.index,
3314
+ match: { start: valueEnd - rawValue.length, end: valueEnd, value, rule, quote }
3315
+ });
3316
+ }
3317
+ }
3318
+ function findKeyValueMatches(line, rules) {
3319
+ const doubleQuoted = line.includes('":') || line.includes('" :');
3320
+ const singleQuoted = line.includes("':") || line.includes("' :");
3321
+ if (!doubleQuoted && !singleQuoted) {
3322
+ return [];
3323
+ }
3324
+ const candidates = [];
3325
+ if (doubleQuoted) {
3326
+ collect(line, DOUBLE_QUOTED, rules, decodeJsonString, '"', candidates);
3327
+ if (line.includes('\\"')) {
3328
+ collect(line, ESCAPED, rules, decodeEscapedJsonString, '\\"', candidates);
3329
+ }
3330
+ }
3331
+ if (singleQuoted) {
3332
+ collect(line, SINGLE_QUOTED, rules, decodePythonString, "'", candidates);
3333
+ }
3334
+ if (candidates.length === 0) {
3335
+ return [];
3336
+ }
3337
+ candidates.sort((left, right) => left.keyStart - right.keyStart);
3338
+ const matches = [];
3339
+ let floor = 0;
3340
+ for (const { keyStart, match } of candidates) {
3341
+ if (keyStart >= floor) {
3342
+ matches.push(match);
3343
+ floor = match.end;
3344
+ }
3345
+ }
3346
+ return matches;
3347
+ }
3348
+
2760
3349
  // src/core/redactLine.ts
2761
3350
  function sliceContext(text, index, length, chars) {
2762
3351
  return {
@@ -2836,11 +3425,21 @@ function pickNext(cursors) {
2836
3425
  }
2837
3426
  return best;
2838
3427
  }
3428
+ function blankOut(line, spans) {
3429
+ let out = "";
3430
+ let cursor = 0;
3431
+ for (const span of spans) {
3432
+ out += line.slice(cursor, span.start) + " ".repeat(span.end - span.start);
3433
+ cursor = span.end;
3434
+ }
3435
+ return out + line.slice(cursor);
3436
+ }
2839
3437
  function redactLine(line, ctx, options = {}) {
2840
3438
  const { compiled, rulesById, allow, contextChars } = ctx;
2841
3439
  const withSegments = options.withSegments ?? false;
2842
3440
  const withMatches = options.withMatches ?? false;
2843
- if (compiled.length === 0) {
3441
+ const keyed = ctx.json ? findKeyValueMatches(line, ctx.rules) : [];
3442
+ if (compiled.length === 0 && keyed.length === 0) {
2844
3443
  return {
2845
3444
  output: line,
2846
3445
  counts: {},
@@ -2848,18 +3447,65 @@ function redactLine(line, ctx, options = {}) {
2848
3447
  segments: withSegments ? { before: [{ text: line, changed: false }], after: [{ text: line, changed: false }] } : void 0
2849
3448
  };
2850
3449
  }
2851
- const cursors = openCursors(line, compiled);
3450
+ const scan = keyed.length === 0 ? line : blankOut(line, keyed);
3451
+ const cursors = openCursors(scan, compiled);
2852
3452
  const counts = {};
2853
3453
  const matches = [];
2854
3454
  const before = [];
2855
3455
  const after = [];
2856
3456
  let output = "";
2857
3457
  let cursor = 0;
3458
+ let keyedIndex = 0;
2858
3459
  for (; ; ) {
2859
3460
  const next = pickNext(cursors);
3461
+ const pending = keyed[keyedIndex];
3462
+ if (pending !== void 0 && (next === null || pending.start <= next.start)) {
3463
+ keyedIndex += 1;
3464
+ if (!isAllowed(allow, pending.rule.id, pending.value)) {
3465
+ const token = buildReplacement(ctx, pending.rule.id, pending.value);
3466
+ const raw = line.slice(pending.start, pending.end);
3467
+ if (withSegments) {
3468
+ if (pending.start > cursor) {
3469
+ const gap = line.slice(cursor, pending.start);
3470
+ before.push({ text: gap, changed: false });
3471
+ after.push({ text: gap, changed: false });
3472
+ }
3473
+ before.push({ text: raw, changed: true });
3474
+ if (pending.quote) {
3475
+ after.push({ text: pending.quote, changed: false });
3476
+ }
3477
+ after.push({ text: token, changed: true });
3478
+ if (pending.quote) {
3479
+ after.push({ text: pending.quote, changed: false });
3480
+ }
3481
+ }
3482
+ output += line.slice(cursor, pending.start) + pending.quote + token + pending.quote;
3483
+ cursor = pending.end;
3484
+ counts[pending.rule.id] = (counts[pending.rule.id] ?? 0) + 1;
3485
+ if (withMatches) {
3486
+ matches.push({
3487
+ ruleId: pending.rule.id,
3488
+ original: pending.value,
3489
+ replacement: token,
3490
+ index: pending.start,
3491
+ ...contextChars > 0 ? sliceContext(line, pending.start, raw.length, contextChars) : {}
3492
+ });
3493
+ }
3494
+ }
3495
+ for (const candidate of cursors) {
3496
+ if (candidate.active && candidate.start < pending.end) {
3497
+ advance(candidate, scan, pending.end);
3498
+ }
3499
+ }
3500
+ continue;
3501
+ }
2860
3502
  if (next === null) {
2861
3503
  break;
2862
3504
  }
3505
+ if (pending !== void 0 && next.start + next.original.length > pending.start) {
3506
+ advance(next, scan, next.start + 1);
3507
+ continue;
3508
+ }
2863
3509
  const { start, original, ruleId } = next;
2864
3510
  const spanEnd = start + Math.max(original.length, 1);
2865
3511
  const rule = rulesById.get(ruleId);
@@ -2890,7 +3536,7 @@ function redactLine(line, ctx, options = {}) {
2890
3536
  }
2891
3537
  for (const candidate of cursors) {
2892
3538
  if (candidate.active && candidate.start < spanEnd) {
2893
- advance(candidate, line, spanEnd);
3539
+ advance(candidate, scan, spanEnd);
2894
3540
  }
2895
3541
  }
2896
3542
  }
@@ -2914,51 +3560,6 @@ function looksLikeJson(line) {
2914
3560
  const trimmed = line.trim();
2915
3561
  return trimmed.length > 0 && (trimmed.startsWith("{") || trimmed.startsWith("["));
2916
3562
  }
2917
- function normalizeKey2(key) {
2918
- return key.toLowerCase().replace(/[-_]/g, "");
2919
- }
2920
- function shouldPreserveJsonValue(value) {
2921
- return value.length === 0 || value === "-";
2922
- }
2923
- function buildJsonKeyMatcher(rules) {
2924
- const exact = /* @__PURE__ */ new Map();
2925
- const contains = [];
2926
- for (const rule of rules) {
2927
- for (const key of rule.jsonKeys ?? []) {
2928
- const normalized = normalizeKey2(key);
2929
- if (!exact.has(normalized)) {
2930
- exact.set(normalized, rule);
2931
- }
2932
- }
2933
- for (const fragment of rule.jsonKeyContains ?? []) {
2934
- contains.push({ fragment: normalizeKey2(fragment), rule });
2935
- }
2936
- }
2937
- return { exact, contains };
2938
- }
2939
- function resolveJsonKeyRule(matcher, key) {
2940
- const normalized = normalizeKey2(key);
2941
- const exact = matcher.exact.get(normalized);
2942
- if (exact) {
2943
- return exact;
2944
- }
2945
- for (const entry of matcher.contains) {
2946
- if (normalized.includes(entry.fragment)) {
2947
- return entry.rule;
2948
- }
2949
- }
2950
- return void 0;
2951
- }
2952
- var jsonKeyMatcherCache = /* @__PURE__ */ new WeakMap();
2953
- function jsonKeyMatcher(rules) {
2954
- const cached = jsonKeyMatcherCache.get(rules);
2955
- if (cached) {
2956
- return cached;
2957
- }
2958
- const matcher = buildJsonKeyMatcher(rules);
2959
- jsonKeyMatcherCache.set(rules, matcher);
2960
- return matcher;
2961
- }
2962
3563
  function mergeCounts(target, source) {
2963
3564
  for (const [id, count] of Object.entries(source)) {
2964
3565
  target[id] = (target[id] ?? 0) + count;
@@ -2985,6 +3586,15 @@ function highlightReplacements(text) {
2985
3586
  function jsonStringContent(value) {
2986
3587
  return JSON.stringify(value).slice(1, -1);
2987
3588
  }
3589
+ function locateJsonNumber(text, value, from = 0) {
3590
+ const re = new RegExp(`:\\s*(${escapeRegExp(value)})(?![\\d.eE])`, "g");
3591
+ re.lastIndex = from;
3592
+ const match = re.exec(text);
3593
+ if (match === null) {
3594
+ return null;
3595
+ }
3596
+ return { index: match.index + match[0].length - value.length, length: value.length };
3597
+ }
2988
3598
  function locateQuotedJsonValue(text, value, from = 0) {
2989
3599
  const encoded = jsonStringContent(value);
2990
3600
  const quotedAt = text.indexOf(`"${encoded}"`, from);
@@ -2994,6 +3604,9 @@ function locateQuotedJsonValue(text, value, from = 0) {
2994
3604
  return { index: quotedAt + 1, length: encoded.length };
2995
3605
  }
2996
3606
  function findOriginal(text, original, from) {
3607
+ if (original.number === true) {
3608
+ return locateJsonNumber(text, original.value, from);
3609
+ }
2997
3610
  const encoded = jsonStringContent(original.value);
2998
3611
  if (original.wholeValue) {
2999
3612
  const quoted = `"${encoded}"`;
@@ -3076,32 +3689,39 @@ function redactJsonLine(line, ctx, options = {}) {
3076
3689
  }
3077
3690
  return { contextBefore: "", contextAfter: "" };
3078
3691
  };
3079
- const redactValue = (value, key) => {
3692
+ const redactField = (rule, value, number) => {
3693
+ if (isAllowed(ctx.allow, rule.id, value)) {
3694
+ return null;
3695
+ }
3696
+ const replacement = buildReplacement(ctx, rule.id, value);
3697
+ counts[rule.id] = (counts[rule.id] ?? 0) + 1;
3698
+ if (withSegments) {
3699
+ originals.push({ value, wholeValue: true, number });
3700
+ }
3701
+ if (withMatches) {
3702
+ const located = number ? locateJsonNumber(content, value) : locateQuotedJsonValue(content, value);
3703
+ matches.push({
3704
+ ruleId: rule.id,
3705
+ original: value,
3706
+ replacement,
3707
+ index: located?.index ?? 0,
3708
+ ...located && ctx.contextChars > 0 ? sliceContext(content, located.index, located.length, ctx.contextChars) : ctx.contextChars > 0 ? { contextBefore: "", contextAfter: "" } : {}
3709
+ });
3710
+ }
3711
+ return replacement;
3712
+ };
3713
+ const redactValue = (value, key, parent) => {
3714
+ if (typeof value === "number") {
3715
+ const fieldRule = key === void 0 || !Number.isFinite(value) ? void 0 : keyMatcher.resolve(key, parent);
3716
+ return fieldRule ? redactField(fieldRule, String(value), true) ?? value : value;
3717
+ }
3080
3718
  if (typeof value === "string") {
3081
3719
  if (key !== void 0 && shouldPreserveJsonValue(value)) {
3082
3720
  return value;
3083
3721
  }
3084
- const fieldRule = key === void 0 ? void 0 : resolveJsonKeyRule(keyMatcher, key);
3722
+ const fieldRule = key === void 0 ? void 0 : keyMatcher.resolve(key, parent);
3085
3723
  if (fieldRule) {
3086
- if (isAllowed(ctx.allow, fieldRule.id, value)) {
3087
- return value;
3088
- }
3089
- const replacement = buildReplacement(ctx, fieldRule.id, value);
3090
- counts[fieldRule.id] = (counts[fieldRule.id] ?? 0) + 1;
3091
- if (withSegments) {
3092
- originals.push({ value, wholeValue: true });
3093
- }
3094
- if (withMatches) {
3095
- const located = locateQuotedJsonValue(content, value);
3096
- matches.push({
3097
- ruleId: fieldRule.id,
3098
- original: value,
3099
- replacement,
3100
- index: located?.index ?? 0,
3101
- ...located && ctx.contextChars > 0 ? sliceContext(content, located.index, located.length, ctx.contextChars) : ctx.contextChars > 0 ? { contextBefore: "", contextAfter: "" } : {}
3102
- });
3103
- }
3104
- return replacement;
3724
+ return redactField(fieldRule, value, false) ?? value;
3105
3725
  }
3106
3726
  const result = redactLine(value, ctx, { withMatches: collectOriginals });
3107
3727
  mergeCounts(counts, result.counts);
@@ -3118,12 +3738,12 @@ function redactJsonLine(line, ctx, options = {}) {
3118
3738
  return result.output;
3119
3739
  }
3120
3740
  if (Array.isArray(value)) {
3121
- return value.map((item) => redactValue(item));
3741
+ return value.map((item) => redactValue(item, key, parent));
3122
3742
  }
3123
3743
  if (value !== null && typeof value === "object") {
3124
3744
  const out = {};
3125
3745
  for (const [entryKey, entryValue] of Object.entries(value)) {
3126
- out[entryKey] = redactValue(entryValue, entryKey);
3746
+ out[entryKey] = redactValue(entryValue, entryKey, key);
3127
3747
  }
3128
3748
  return out;
3129
3749
  }