@socprime/logtotal-sanitizer 0.2.0-beta.2 → 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/CHANGELOG.md +39 -0
- package/README.md +3 -1
- package/{chunk-M4CEAFAP.js → chunk-5UDGYWP3.js} +288 -73
- package/chunk-5UDGYWP3.js.map +1 -0
- package/{chunk-EUNFMCKE.cjs → chunk-VELXTD3L.cjs} +432 -18
- package/chunk-VELXTD3L.cjs.map +1 -0
- package/{chunk-VMPXBCSL.js → chunk-X2QYRBY6.js} +432 -18
- package/chunk-X2QYRBY6.js.map +1 -0
- package/{chunk-XPVOBYVK.cjs → chunk-ZCWN2P2R.cjs} +312 -97
- package/chunk-ZCWN2P2R.cjs.map +1 -0
- package/cli.js +715 -86
- package/cli.js.map +1 -1
- package/{index-BYhIh1ru.d.cts → index-BYEGdnwh.d.cts} +8 -4
- package/{index-BYhIh1ru.d.ts → index-BYEGdnwh.d.ts} +8 -4
- package/index.cjs +25 -25
- package/index.d.cts +2 -2
- package/index.d.ts +2 -2
- package/index.js +2 -2
- package/node.cjs +27 -27
- package/node.d.cts +2 -2
- package/node.d.ts +2 -2
- package/node.js +3 -3
- package/package.json +1 -1
- package/rules.cjs +5 -5
- package/rules.d.cts +1 -1
- package/rules.d.ts +1 -1
- package/rules.js +1 -1
- package/chunk-EUNFMCKE.cjs.map +0 -1
- package/chunk-M4CEAFAP.js.map +0 -1
- package/chunk-VMPXBCSL.js.map +0 -1
- package/chunk-XPVOBYVK.cjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkVELXTD3L_cjs = require('./chunk-VELXTD3L.cjs');
|
|
4
4
|
|
|
5
5
|
// src/core/constants.ts
|
|
6
6
|
var DEFAULT_PREVIEW_BYTES = 256 * 1024;
|
|
@@ -200,11 +200,11 @@ function bytesToHex(bytes) {
|
|
|
200
200
|
var HEX_PATTERN = /^(?:[0-9a-fA-F]{2})+$/;
|
|
201
201
|
function generateKey(byteLength = DEFAULT_KEY_BYTES) {
|
|
202
202
|
if (!Number.isInteger(byteLength) || byteLength < 16) {
|
|
203
|
-
throw new
|
|
203
|
+
throw new chunkVELXTD3L_cjs.InvalidKeyError(`Key length must be an integer of at least 16 bytes, got ${byteLength}.`);
|
|
204
204
|
}
|
|
205
205
|
const source = globalThis.crypto;
|
|
206
206
|
if (typeof source?.getRandomValues !== "function") {
|
|
207
|
-
throw new
|
|
207
|
+
throw new chunkVELXTD3L_cjs.InvalidKeyError(
|
|
208
208
|
"No secure random source available. Pass an explicit `key` or provide a Web Crypto implementation on globalThis.crypto."
|
|
209
209
|
);
|
|
210
210
|
}
|
|
@@ -214,10 +214,10 @@ function generateKey(byteLength = DEFAULT_KEY_BYTES) {
|
|
|
214
214
|
}
|
|
215
215
|
function assertKey(key, encoding) {
|
|
216
216
|
if (key.length === 0) {
|
|
217
|
-
throw new
|
|
217
|
+
throw new chunkVELXTD3L_cjs.InvalidKeyError("Key must not be empty.");
|
|
218
218
|
}
|
|
219
219
|
if (encoding === "hex" && !HEX_PATTERN.test(key)) {
|
|
220
|
-
throw new
|
|
220
|
+
throw new chunkVELXTD3L_cjs.InvalidKeyError(
|
|
221
221
|
'A hex key must contain an even number of hexadecimal characters. Pass `keyEncoding: "utf8"` for a passphrase.'
|
|
222
222
|
);
|
|
223
223
|
}
|
|
@@ -237,7 +237,7 @@ function normalizePatterns(patterns) {
|
|
|
237
237
|
try {
|
|
238
238
|
new RegExp(source, "u");
|
|
239
239
|
} catch (cause) {
|
|
240
|
-
throw new
|
|
240
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError(
|
|
241
241
|
`alwaysRedact.patterns contains an invalid regular expression: ${String(cause)}`
|
|
242
242
|
);
|
|
243
243
|
}
|
|
@@ -278,7 +278,7 @@ function compileAnchored(patterns) {
|
|
|
278
278
|
try {
|
|
279
279
|
return new RegExp(`^(?:${combined})$`, "u");
|
|
280
280
|
} catch (cause) {
|
|
281
|
-
throw new
|
|
281
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError(
|
|
282
282
|
`neverRedact.patterns contains an invalid regular expression: ${String(cause)}`
|
|
283
283
|
);
|
|
284
284
|
}
|
|
@@ -365,13 +365,13 @@ function compileOne(rule, aggressive) {
|
|
|
365
365
|
try {
|
|
366
366
|
fragments.push({
|
|
367
367
|
regex: new RegExp(pattern, "gu"),
|
|
368
|
-
hasValueGroup:
|
|
368
|
+
hasValueGroup: chunkVELXTD3L_cjs.countCapturingGroups(pattern) === 1
|
|
369
369
|
});
|
|
370
370
|
} catch (cause) {
|
|
371
|
-
throw new
|
|
371
|
+
throw new chunkVELXTD3L_cjs.InvalidRuleError(`Failed to compile rule "${rule.id}": ${String(cause)}`);
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
|
-
const gateSource = patterns.map(
|
|
374
|
+
const gateSource = patterns.map(chunkVELXTD3L_cjs.deriveGateSource).join("|");
|
|
375
375
|
let gate = null;
|
|
376
376
|
if (!SKIP_REGEX_GATE.has(rule.id) && gateSource.length > 0 && gateSource !== source) {
|
|
377
377
|
try {
|
|
@@ -431,10 +431,10 @@ function createLineSplitter(options = {}) {
|
|
|
431
431
|
const maxLineChars = options.maxLineChars ?? DEFAULT_MAX_LINE_CHARS;
|
|
432
432
|
const overlapChars = options.overlapChars ?? DEFAULT_LINE_OVERLAP_CHARS;
|
|
433
433
|
if (maxLineChars < 1) {
|
|
434
|
-
throw new
|
|
434
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("lines.maxLineChars must be at least 1.");
|
|
435
435
|
}
|
|
436
436
|
if (overlapChars < 0 || overlapChars >= maxLineChars) {
|
|
437
|
-
throw new
|
|
437
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError(
|
|
438
438
|
`lines.overlapChars must be between 0 and lines.maxLineChars - 1 (${maxLineChars - 1}), got ${overlapChars}.`
|
|
439
439
|
);
|
|
440
440
|
}
|
|
@@ -461,6 +461,181 @@ function createLineSplitter(options = {}) {
|
|
|
461
461
|
return { push, flush };
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
+
// src/core/jsonKeyMatcher.ts
|
|
465
|
+
var RESOLVE_CACHE_LIMIT = 1e4;
|
|
466
|
+
function normalizeKey2(key) {
|
|
467
|
+
return key.toLowerCase().replace(/[-_.\s]/g, "");
|
|
468
|
+
}
|
|
469
|
+
function shouldPreserveJsonValue(value) {
|
|
470
|
+
return value.length === 0 || value === "-";
|
|
471
|
+
}
|
|
472
|
+
function buildTables(rules) {
|
|
473
|
+
const exact = /* @__PURE__ */ new Map();
|
|
474
|
+
const contains = [];
|
|
475
|
+
for (const rule of rules) {
|
|
476
|
+
for (const key of rule.jsonKeys ?? []) {
|
|
477
|
+
const normalized = normalizeKey2(key);
|
|
478
|
+
if (!exact.has(normalized)) {
|
|
479
|
+
exact.set(normalized, rule);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
for (const fragment of rule.jsonKeyContains ?? []) {
|
|
483
|
+
contains.push({ fragment: normalizeKey2(fragment), rule });
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return { exact, contains };
|
|
487
|
+
}
|
|
488
|
+
function lastSegment(key) {
|
|
489
|
+
const dot = key.lastIndexOf(".");
|
|
490
|
+
return dot === -1 ? key : key.slice(dot + 1);
|
|
491
|
+
}
|
|
492
|
+
function resolveUncached(tables, key, parent) {
|
|
493
|
+
const dot = key.lastIndexOf(".");
|
|
494
|
+
const leaf = normalizeKey2(dot === -1 ? key : key.slice(dot + 1));
|
|
495
|
+
const parentRaw = dot === -1 ? parent : key.slice(0, dot);
|
|
496
|
+
if (leaf.length === 0) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
if (parentRaw !== void 0) {
|
|
500
|
+
const scoped = tables.exact.get(normalizeKey2(lastSegment(parentRaw)) + leaf);
|
|
501
|
+
if (scoped) {
|
|
502
|
+
return scoped;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const exact = tables.exact.get(leaf);
|
|
506
|
+
if (exact) {
|
|
507
|
+
return exact;
|
|
508
|
+
}
|
|
509
|
+
for (const entry of tables.contains) {
|
|
510
|
+
if (leaf.includes(entry.fragment)) {
|
|
511
|
+
return entry.rule;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
function buildMatcher(rules) {
|
|
517
|
+
const tables = buildTables(rules);
|
|
518
|
+
const cache = /* @__PURE__ */ new Map();
|
|
519
|
+
return {
|
|
520
|
+
resolve(key, parent) {
|
|
521
|
+
const cacheKey = parent === void 0 ? key : `${parent}\0${key}`;
|
|
522
|
+
let rule = cache.get(cacheKey);
|
|
523
|
+
if (rule === void 0) {
|
|
524
|
+
rule = resolveUncached(tables, key, parent);
|
|
525
|
+
if (cache.size >= RESOLVE_CACHE_LIMIT) {
|
|
526
|
+
cache.clear();
|
|
527
|
+
}
|
|
528
|
+
cache.set(cacheKey, rule);
|
|
529
|
+
}
|
|
530
|
+
return rule ?? void 0;
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
var matcherCache = /* @__PURE__ */ new WeakMap();
|
|
535
|
+
function jsonKeyMatcher(rules) {
|
|
536
|
+
const cached = matcherCache.get(rules);
|
|
537
|
+
if (cached) {
|
|
538
|
+
return cached;
|
|
539
|
+
}
|
|
540
|
+
const matcher = buildMatcher(rules);
|
|
541
|
+
matcherCache.set(rules, matcher);
|
|
542
|
+
return matcher;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/core/redactKeyValuePairs.ts
|
|
546
|
+
var NUMBER = "(-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)(?![\\w.:+-])";
|
|
547
|
+
var DOUBLE_QUOTED = new RegExp(
|
|
548
|
+
`(?<![\\\\\\w])"((?:[^"\\\\\\r\\n]|\\\\.){1,128})"\\s*:\\s*(?:"((?:[^"\\\\]|\\\\.)*)"|${NUMBER})`,
|
|
549
|
+
"g"
|
|
550
|
+
);
|
|
551
|
+
var ESCAPED = new RegExp(
|
|
552
|
+
`\\\\"((?:[^"\\\\\\r\\n]|\\\\\\\\){1,128})\\\\"\\s*:\\s*(?:\\\\"((?:\\\\\\\\\\\\"|\\\\\\\\|\\\\[^"\\\\]|[^"\\\\])*)\\\\"|${NUMBER})`,
|
|
553
|
+
"g"
|
|
554
|
+
);
|
|
555
|
+
var SINGLE_QUOTED = new RegExp(
|
|
556
|
+
`(?<![\\\\\\w])'((?:[^'\\\\\\r\\n]|\\\\.){1,128})'\\s*:\\s*(?:'((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|${NUMBER})`,
|
|
557
|
+
"g"
|
|
558
|
+
);
|
|
559
|
+
function decodeJsonString(raw) {
|
|
560
|
+
if (!raw.includes("\\")) {
|
|
561
|
+
return raw;
|
|
562
|
+
}
|
|
563
|
+
try {
|
|
564
|
+
return JSON.parse(`"${raw}"`);
|
|
565
|
+
} catch {
|
|
566
|
+
return raw;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function decodeEscapedJsonString(raw) {
|
|
570
|
+
return decodeJsonString(decodeJsonString(raw));
|
|
571
|
+
}
|
|
572
|
+
function decodePythonString(raw) {
|
|
573
|
+
return raw.includes("\\") ? raw.replace(/\\(['"\\])/g, "$1") : raw;
|
|
574
|
+
}
|
|
575
|
+
function collect(line, regex, rules, decode, numberQuote, out) {
|
|
576
|
+
const matcher = jsonKeyMatcher(rules);
|
|
577
|
+
regex.lastIndex = 0;
|
|
578
|
+
for (let found = regex.exec(line); found !== null; found = regex.exec(line)) {
|
|
579
|
+
const rawKey = found[1];
|
|
580
|
+
let rawValue;
|
|
581
|
+
let quote = "";
|
|
582
|
+
for (let group = 2; group < found.length; group += 1) {
|
|
583
|
+
if (found[group] !== void 0) {
|
|
584
|
+
rawValue = found[group];
|
|
585
|
+
quote = group === found.length - 1 ? numberQuote : "";
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (rawValue === void 0) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const value = quote === "" ? decode(rawValue) : rawValue;
|
|
593
|
+
if (shouldPreserveJsonValue(value)) {
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
const rule = matcher.resolve(decode(rawKey));
|
|
597
|
+
if (!rule) {
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
const end = found.index + found[0].length - (quote === "" ? 1 : 0);
|
|
601
|
+
const valueEnd = quote === "" && regex === ESCAPED ? end - 1 : end;
|
|
602
|
+
out.push({
|
|
603
|
+
keyStart: found.index,
|
|
604
|
+
match: { start: valueEnd - rawValue.length, end: valueEnd, value, rule, quote }
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function findKeyValueMatches(line, rules) {
|
|
609
|
+
const doubleQuoted = line.includes('":') || line.includes('" :');
|
|
610
|
+
const singleQuoted = line.includes("':") || line.includes("' :");
|
|
611
|
+
if (!doubleQuoted && !singleQuoted) {
|
|
612
|
+
return [];
|
|
613
|
+
}
|
|
614
|
+
const candidates = [];
|
|
615
|
+
if (doubleQuoted) {
|
|
616
|
+
collect(line, DOUBLE_QUOTED, rules, decodeJsonString, '"', candidates);
|
|
617
|
+
if (line.includes('\\"')) {
|
|
618
|
+
collect(line, ESCAPED, rules, decodeEscapedJsonString, '\\"', candidates);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (singleQuoted) {
|
|
622
|
+
collect(line, SINGLE_QUOTED, rules, decodePythonString, "'", candidates);
|
|
623
|
+
}
|
|
624
|
+
if (candidates.length === 0) {
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
627
|
+
candidates.sort((left, right) => left.keyStart - right.keyStart);
|
|
628
|
+
const matches = [];
|
|
629
|
+
let floor = 0;
|
|
630
|
+
for (const { keyStart, match } of candidates) {
|
|
631
|
+
if (keyStart >= floor) {
|
|
632
|
+
matches.push(match);
|
|
633
|
+
floor = match.end;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return matches;
|
|
637
|
+
}
|
|
638
|
+
|
|
464
639
|
// src/core/redactLine.ts
|
|
465
640
|
function sliceContext(text, index, length, chars) {
|
|
466
641
|
return {
|
|
@@ -498,7 +673,7 @@ function openCursors(line, compiled) {
|
|
|
498
673
|
const cursors = [];
|
|
499
674
|
for (let ruleIndex = 0; ruleIndex < compiled.length; ruleIndex += 1) {
|
|
500
675
|
const rule = compiled[ruleIndex];
|
|
501
|
-
if (rule.gateChars !== null && !
|
|
676
|
+
if (rule.gateChars !== null && !chunkVELXTD3L_cjs.lineHasAnyChar(line, rule.gateChars)) {
|
|
502
677
|
continue;
|
|
503
678
|
}
|
|
504
679
|
if (rule.gate !== null) {
|
|
@@ -540,11 +715,21 @@ function pickNext(cursors) {
|
|
|
540
715
|
}
|
|
541
716
|
return best;
|
|
542
717
|
}
|
|
718
|
+
function blankOut(line, spans) {
|
|
719
|
+
let out = "";
|
|
720
|
+
let cursor = 0;
|
|
721
|
+
for (const span of spans) {
|
|
722
|
+
out += line.slice(cursor, span.start) + " ".repeat(span.end - span.start);
|
|
723
|
+
cursor = span.end;
|
|
724
|
+
}
|
|
725
|
+
return out + line.slice(cursor);
|
|
726
|
+
}
|
|
543
727
|
function redactLine(line, ctx, options = {}) {
|
|
544
728
|
const { compiled, rulesById, allow, contextChars } = ctx;
|
|
545
729
|
const withSegments = options.withSegments ?? false;
|
|
546
730
|
const withMatches = options.withMatches ?? false;
|
|
547
|
-
|
|
731
|
+
const keyed = ctx.json ? findKeyValueMatches(line, ctx.rules) : [];
|
|
732
|
+
if (compiled.length === 0 && keyed.length === 0) {
|
|
548
733
|
return {
|
|
549
734
|
output: line,
|
|
550
735
|
counts: {},
|
|
@@ -552,18 +737,65 @@ function redactLine(line, ctx, options = {}) {
|
|
|
552
737
|
segments: withSegments ? { before: [{ text: line, changed: false }], after: [{ text: line, changed: false }] } : void 0
|
|
553
738
|
};
|
|
554
739
|
}
|
|
555
|
-
const
|
|
740
|
+
const scan = keyed.length === 0 ? line : blankOut(line, keyed);
|
|
741
|
+
const cursors = openCursors(scan, compiled);
|
|
556
742
|
const counts = {};
|
|
557
743
|
const matches = [];
|
|
558
744
|
const before = [];
|
|
559
745
|
const after = [];
|
|
560
746
|
let output = "";
|
|
561
747
|
let cursor = 0;
|
|
748
|
+
let keyedIndex = 0;
|
|
562
749
|
for (; ; ) {
|
|
563
750
|
const next = pickNext(cursors);
|
|
751
|
+
const pending = keyed[keyedIndex];
|
|
752
|
+
if (pending !== void 0 && (next === null || pending.start <= next.start)) {
|
|
753
|
+
keyedIndex += 1;
|
|
754
|
+
if (!isAllowed(allow, pending.rule.id, pending.value)) {
|
|
755
|
+
const token = buildReplacement(ctx, pending.rule.id, pending.value);
|
|
756
|
+
const raw = line.slice(pending.start, pending.end);
|
|
757
|
+
if (withSegments) {
|
|
758
|
+
if (pending.start > cursor) {
|
|
759
|
+
const gap = line.slice(cursor, pending.start);
|
|
760
|
+
before.push({ text: gap, changed: false });
|
|
761
|
+
after.push({ text: gap, changed: false });
|
|
762
|
+
}
|
|
763
|
+
before.push({ text: raw, changed: true });
|
|
764
|
+
if (pending.quote) {
|
|
765
|
+
after.push({ text: pending.quote, changed: false });
|
|
766
|
+
}
|
|
767
|
+
after.push({ text: token, changed: true });
|
|
768
|
+
if (pending.quote) {
|
|
769
|
+
after.push({ text: pending.quote, changed: false });
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
output += line.slice(cursor, pending.start) + pending.quote + token + pending.quote;
|
|
773
|
+
cursor = pending.end;
|
|
774
|
+
counts[pending.rule.id] = (counts[pending.rule.id] ?? 0) + 1;
|
|
775
|
+
if (withMatches) {
|
|
776
|
+
matches.push({
|
|
777
|
+
ruleId: pending.rule.id,
|
|
778
|
+
original: pending.value,
|
|
779
|
+
replacement: token,
|
|
780
|
+
index: pending.start,
|
|
781
|
+
...contextChars > 0 ? sliceContext(line, pending.start, raw.length, contextChars) : {}
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
for (const candidate of cursors) {
|
|
786
|
+
if (candidate.active && candidate.start < pending.end) {
|
|
787
|
+
advance(candidate, scan, pending.end);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
564
792
|
if (next === null) {
|
|
565
793
|
break;
|
|
566
794
|
}
|
|
795
|
+
if (pending !== void 0 && next.start + next.original.length > pending.start) {
|
|
796
|
+
advance(next, scan, next.start + 1);
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
567
799
|
const { start, original, ruleId } = next;
|
|
568
800
|
const spanEnd = start + Math.max(original.length, 1);
|
|
569
801
|
const rule = rulesById.get(ruleId);
|
|
@@ -594,7 +826,7 @@ function redactLine(line, ctx, options = {}) {
|
|
|
594
826
|
}
|
|
595
827
|
for (const candidate of cursors) {
|
|
596
828
|
if (candidate.active && candidate.start < spanEnd) {
|
|
597
|
-
advance(candidate,
|
|
829
|
+
advance(candidate, scan, spanEnd);
|
|
598
830
|
}
|
|
599
831
|
}
|
|
600
832
|
}
|
|
@@ -618,51 +850,6 @@ function looksLikeJson(line) {
|
|
|
618
850
|
const trimmed = line.trim();
|
|
619
851
|
return trimmed.length > 0 && (trimmed.startsWith("{") || trimmed.startsWith("["));
|
|
620
852
|
}
|
|
621
|
-
function normalizeKey2(key) {
|
|
622
|
-
return key.toLowerCase().replace(/[-_]/g, "");
|
|
623
|
-
}
|
|
624
|
-
function shouldPreserveJsonValue(value) {
|
|
625
|
-
return value.length === 0 || value === "-";
|
|
626
|
-
}
|
|
627
|
-
function buildJsonKeyMatcher(rules) {
|
|
628
|
-
const exact = /* @__PURE__ */ new Map();
|
|
629
|
-
const contains = [];
|
|
630
|
-
for (const rule of rules) {
|
|
631
|
-
for (const key of rule.jsonKeys ?? []) {
|
|
632
|
-
const normalized = normalizeKey2(key);
|
|
633
|
-
if (!exact.has(normalized)) {
|
|
634
|
-
exact.set(normalized, rule);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
for (const fragment of rule.jsonKeyContains ?? []) {
|
|
638
|
-
contains.push({ fragment: normalizeKey2(fragment), rule });
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
return { exact, contains };
|
|
642
|
-
}
|
|
643
|
-
function resolveJsonKeyRule(matcher, key) {
|
|
644
|
-
const normalized = normalizeKey2(key);
|
|
645
|
-
const exact = matcher.exact.get(normalized);
|
|
646
|
-
if (exact) {
|
|
647
|
-
return exact;
|
|
648
|
-
}
|
|
649
|
-
for (const entry of matcher.contains) {
|
|
650
|
-
if (normalized.includes(entry.fragment)) {
|
|
651
|
-
return entry.rule;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
return void 0;
|
|
655
|
-
}
|
|
656
|
-
var jsonKeyMatcherCache = /* @__PURE__ */ new WeakMap();
|
|
657
|
-
function jsonKeyMatcher(rules) {
|
|
658
|
-
const cached = jsonKeyMatcherCache.get(rules);
|
|
659
|
-
if (cached) {
|
|
660
|
-
return cached;
|
|
661
|
-
}
|
|
662
|
-
const matcher = buildJsonKeyMatcher(rules);
|
|
663
|
-
jsonKeyMatcherCache.set(rules, matcher);
|
|
664
|
-
return matcher;
|
|
665
|
-
}
|
|
666
853
|
function mergeCounts(target, source) {
|
|
667
854
|
for (const [id, count] of Object.entries(source)) {
|
|
668
855
|
target[id] = (target[id] ?? 0) + count;
|
|
@@ -689,7 +876,27 @@ function highlightReplacements(text) {
|
|
|
689
876
|
function jsonStringContent(value) {
|
|
690
877
|
return JSON.stringify(value).slice(1, -1);
|
|
691
878
|
}
|
|
879
|
+
function locateJsonNumber(text, value, from = 0) {
|
|
880
|
+
const re = new RegExp(`:\\s*(${escapeRegExp(value)})(?![\\d.eE])`, "g");
|
|
881
|
+
re.lastIndex = from;
|
|
882
|
+
const match = re.exec(text);
|
|
883
|
+
if (match === null) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
return { index: match.index + match[0].length - value.length, length: value.length };
|
|
887
|
+
}
|
|
888
|
+
function locateQuotedJsonValue(text, value, from = 0) {
|
|
889
|
+
const encoded = jsonStringContent(value);
|
|
890
|
+
const quotedAt = text.indexOf(`"${encoded}"`, from);
|
|
891
|
+
if (quotedAt === -1) {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
return { index: quotedAt + 1, length: encoded.length };
|
|
895
|
+
}
|
|
692
896
|
function findOriginal(text, original, from) {
|
|
897
|
+
if (original.number === true) {
|
|
898
|
+
return locateJsonNumber(text, original.value, from);
|
|
899
|
+
}
|
|
693
900
|
const encoded = jsonStringContent(original.value);
|
|
694
901
|
if (original.wholeValue) {
|
|
695
902
|
const quoted = `"${encoded}"`;
|
|
@@ -772,31 +979,39 @@ function redactJsonLine(line, ctx, options = {}) {
|
|
|
772
979
|
}
|
|
773
980
|
return { contextBefore: "", contextAfter: "" };
|
|
774
981
|
};
|
|
775
|
-
const
|
|
982
|
+
const redactField = (rule, value, number) => {
|
|
983
|
+
if (isAllowed(ctx.allow, rule.id, value)) {
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
const replacement = buildReplacement(ctx, rule.id, value);
|
|
987
|
+
counts[rule.id] = (counts[rule.id] ?? 0) + 1;
|
|
988
|
+
if (withSegments) {
|
|
989
|
+
originals.push({ value, wholeValue: true, number });
|
|
990
|
+
}
|
|
991
|
+
if (withMatches) {
|
|
992
|
+
const located = number ? locateJsonNumber(content, value) : locateQuotedJsonValue(content, value);
|
|
993
|
+
matches.push({
|
|
994
|
+
ruleId: rule.id,
|
|
995
|
+
original: value,
|
|
996
|
+
replacement,
|
|
997
|
+
index: located?.index ?? 0,
|
|
998
|
+
...located && ctx.contextChars > 0 ? sliceContext(content, located.index, located.length, ctx.contextChars) : ctx.contextChars > 0 ? { contextBefore: "", contextAfter: "" } : {}
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
return replacement;
|
|
1002
|
+
};
|
|
1003
|
+
const redactValue = (value, key, parent) => {
|
|
1004
|
+
if (typeof value === "number") {
|
|
1005
|
+
const fieldRule = key === void 0 || !Number.isFinite(value) ? void 0 : keyMatcher.resolve(key, parent);
|
|
1006
|
+
return fieldRule ? redactField(fieldRule, String(value), true) ?? value : value;
|
|
1007
|
+
}
|
|
776
1008
|
if (typeof value === "string") {
|
|
777
1009
|
if (key !== void 0 && shouldPreserveJsonValue(value)) {
|
|
778
1010
|
return value;
|
|
779
1011
|
}
|
|
780
|
-
const fieldRule = key === void 0 ? void 0 :
|
|
1012
|
+
const fieldRule = key === void 0 ? void 0 : keyMatcher.resolve(key, parent);
|
|
781
1013
|
if (fieldRule) {
|
|
782
|
-
|
|
783
|
-
return value;
|
|
784
|
-
}
|
|
785
|
-
const replacement = buildReplacement(ctx, fieldRule.id, value);
|
|
786
|
-
counts[fieldRule.id] = (counts[fieldRule.id] ?? 0) + 1;
|
|
787
|
-
if (withSegments) {
|
|
788
|
-
originals.push({ value, wholeValue: true });
|
|
789
|
-
}
|
|
790
|
-
if (withMatches) {
|
|
791
|
-
matches.push({
|
|
792
|
-
ruleId: fieldRule.id,
|
|
793
|
-
original: value,
|
|
794
|
-
replacement,
|
|
795
|
-
index: Math.max(content.indexOf(jsonStringContent(value)), 0),
|
|
796
|
-
...contextFor(value)
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
return replacement;
|
|
1014
|
+
return redactField(fieldRule, value, false) ?? value;
|
|
800
1015
|
}
|
|
801
1016
|
const result = redactLine(value, ctx, { withMatches: collectOriginals });
|
|
802
1017
|
mergeCounts(counts, result.counts);
|
|
@@ -813,12 +1028,12 @@ function redactJsonLine(line, ctx, options = {}) {
|
|
|
813
1028
|
return result.output;
|
|
814
1029
|
}
|
|
815
1030
|
if (Array.isArray(value)) {
|
|
816
|
-
return value.map((item) => redactValue(item));
|
|
1031
|
+
return value.map((item) => redactValue(item, key, parent));
|
|
817
1032
|
}
|
|
818
1033
|
if (value !== null && typeof value === "object") {
|
|
819
1034
|
const out = {};
|
|
820
1035
|
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
821
|
-
out[entryKey] = redactValue(entryValue, entryKey);
|
|
1036
|
+
out[entryKey] = redactValue(entryValue, entryKey, key);
|
|
822
1037
|
}
|
|
823
1038
|
return out;
|
|
824
1039
|
}
|
|
@@ -846,10 +1061,10 @@ function createReportCollector(options = {}) {
|
|
|
846
1061
|
const maxPerRule = options.maxReplacementsPerRule;
|
|
847
1062
|
const limited = maxPerRule !== void 0;
|
|
848
1063
|
if (previewBytes < 0) {
|
|
849
|
-
throw new
|
|
1064
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("report.previewBytes must not be negative.");
|
|
850
1065
|
}
|
|
851
1066
|
if (limited && (maxPerRule < 0 || !Number.isInteger(maxPerRule))) {
|
|
852
|
-
throw new
|
|
1067
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("report.maxReplacementsPerRule must be a non-negative integer.");
|
|
853
1068
|
}
|
|
854
1069
|
const counts = {};
|
|
855
1070
|
const replacements = /* @__PURE__ */ new Map();
|
|
@@ -929,15 +1144,15 @@ function createReportCollector(options = {}) {
|
|
|
929
1144
|
// src/core/resolveRules.ts
|
|
930
1145
|
function toRule(selector) {
|
|
931
1146
|
if (typeof selector === "string") {
|
|
932
|
-
const builtin =
|
|
1147
|
+
const builtin = chunkVELXTD3L_cjs.getBuiltinRule(selector);
|
|
933
1148
|
if (!builtin) {
|
|
934
|
-
throw new
|
|
1149
|
+
throw new chunkVELXTD3L_cjs.UnknownRuleError(
|
|
935
1150
|
`Unknown rule "${selector}". Pass a rule object for custom rules, or one of the built-in identifiers.`
|
|
936
1151
|
);
|
|
937
1152
|
}
|
|
938
1153
|
return builtin;
|
|
939
1154
|
}
|
|
940
|
-
return
|
|
1155
|
+
return chunkVELXTD3L_cjs.validateRule(selector);
|
|
941
1156
|
}
|
|
942
1157
|
function resolveRules(selectors, extraRules = []) {
|
|
943
1158
|
const resolved = [];
|
|
@@ -975,7 +1190,7 @@ function createSanitizer(options = {}) {
|
|
|
975
1190
|
assertKey(key, keyEncoding);
|
|
976
1191
|
const contextChars = options.report?.contextChars ?? 0;
|
|
977
1192
|
if (contextChars < 0) {
|
|
978
|
-
throw new
|
|
1193
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("report.contextChars must not be negative.");
|
|
979
1194
|
}
|
|
980
1195
|
const selected = resolveWithAlwaysRedact(options);
|
|
981
1196
|
const ctx = createRuleContext({
|
|
@@ -1019,7 +1234,7 @@ function createSanitizer(options = {}) {
|
|
|
1019
1234
|
let charsRead = 0;
|
|
1020
1235
|
const writeLine = async (line) => {
|
|
1021
1236
|
if (signal?.aborted === true) {
|
|
1022
|
-
throw new
|
|
1237
|
+
throw new chunkVELXTD3L_cjs.SanitizationAbortedError();
|
|
1023
1238
|
}
|
|
1024
1239
|
await sink.write(processLine(line, ctx, collector));
|
|
1025
1240
|
};
|
|
@@ -1039,13 +1254,13 @@ function createSanitizer(options = {}) {
|
|
|
1039
1254
|
return { key, rules, sanitizeText: sanitizeText2, sanitizeStream: sanitizeStream2 };
|
|
1040
1255
|
}
|
|
1041
1256
|
function resolveWithAlwaysRedact(options) {
|
|
1042
|
-
const selected = resolveRules(options.rules ??
|
|
1257
|
+
const selected = resolveRules(options.rules ?? chunkVELXTD3L_cjs.builtinRuleIds, options.extraRules);
|
|
1043
1258
|
const alwaysRule = createAlwaysRedactRule(options.alwaysRedact);
|
|
1044
1259
|
if (!alwaysRule) {
|
|
1045
1260
|
return selected;
|
|
1046
1261
|
}
|
|
1047
1262
|
if (selected.some((rule) => rule.id === alwaysRule.id)) {
|
|
1048
|
-
throw new
|
|
1263
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError(
|
|
1049
1264
|
`alwaysRedact.ruleId "${alwaysRule.id}" collides with an active rule. Choose an identifier that is not in use.`
|
|
1050
1265
|
);
|
|
1051
1266
|
}
|
|
@@ -1238,15 +1453,15 @@ async function snapToNewline(blob, from, probeBytes, maxProbeBytes) {
|
|
|
1238
1453
|
}
|
|
1239
1454
|
async function planLineAlignedRanges(blob, partCount, options = {}) {
|
|
1240
1455
|
if (!Number.isInteger(partCount) || partCount < 1) {
|
|
1241
|
-
throw new
|
|
1456
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("partCount must be an integer >= 1.");
|
|
1242
1457
|
}
|
|
1243
1458
|
const probeBytes = options.probeBytes ?? DEFAULT_RANGE_PROBE_BYTES;
|
|
1244
1459
|
const maxProbeBytes = options.maxProbeBytes ?? DEFAULT_RANGE_MAX_PROBE_BYTES;
|
|
1245
1460
|
if (!Number.isInteger(probeBytes) || probeBytes < 1) {
|
|
1246
|
-
throw new
|
|
1461
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("probeBytes must be an integer >= 1.");
|
|
1247
1462
|
}
|
|
1248
1463
|
if (!Number.isInteger(maxProbeBytes) || maxProbeBytes < probeBytes) {
|
|
1249
|
-
throw new
|
|
1464
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("maxProbeBytes must be an integer >= probeBytes.");
|
|
1250
1465
|
}
|
|
1251
1466
|
if (blob.size <= 0) {
|
|
1252
1467
|
return [];
|
|
@@ -1308,5 +1523,5 @@ exports.sanitizeText = sanitizeText;
|
|
|
1308
1523
|
exports.toCallbackSink = toCallbackSink;
|
|
1309
1524
|
exports.toNullSink = toNullSink;
|
|
1310
1525
|
exports.toStringSink = toStringSink;
|
|
1311
|
-
//# sourceMappingURL=chunk-
|
|
1312
|
-
//# sourceMappingURL=chunk-
|
|
1526
|
+
//# sourceMappingURL=chunk-ZCWN2P2R.cjs.map
|
|
1527
|
+
//# sourceMappingURL=chunk-ZCWN2P2R.cjs.map
|