@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/CHANGELOG.md +31 -0
- package/README.md +3 -1
- package/{chunk-SGF4CYUV.js → chunk-5UDGYWP3.js} +280 -74
- 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-YPHS5NO7.cjs → chunk-ZCWN2P2R.cjs} +304 -98
- package/chunk-ZCWN2P2R.cjs.map +1 -0
- package/cli.js +707 -87
- 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-SGF4CYUV.js.map +0 -1
- package/chunk-VMPXBCSL.js.map +0 -1
- package/chunk-YPHS5NO7.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,6 +876,15 @@ 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
|
+
}
|
|
692
888
|
function locateQuotedJsonValue(text, value, from = 0) {
|
|
693
889
|
const encoded = jsonStringContent(value);
|
|
694
890
|
const quotedAt = text.indexOf(`"${encoded}"`, from);
|
|
@@ -698,6 +894,9 @@ function locateQuotedJsonValue(text, value, from = 0) {
|
|
|
698
894
|
return { index: quotedAt + 1, length: encoded.length };
|
|
699
895
|
}
|
|
700
896
|
function findOriginal(text, original, from) {
|
|
897
|
+
if (original.number === true) {
|
|
898
|
+
return locateJsonNumber(text, original.value, from);
|
|
899
|
+
}
|
|
701
900
|
const encoded = jsonStringContent(original.value);
|
|
702
901
|
if (original.wholeValue) {
|
|
703
902
|
const quoted = `"${encoded}"`;
|
|
@@ -780,32 +979,39 @@ function redactJsonLine(line, ctx, options = {}) {
|
|
|
780
979
|
}
|
|
781
980
|
return { contextBefore: "", contextAfter: "" };
|
|
782
981
|
};
|
|
783
|
-
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
|
+
}
|
|
784
1008
|
if (typeof value === "string") {
|
|
785
1009
|
if (key !== void 0 && shouldPreserveJsonValue(value)) {
|
|
786
1010
|
return value;
|
|
787
1011
|
}
|
|
788
|
-
const fieldRule = key === void 0 ? void 0 :
|
|
1012
|
+
const fieldRule = key === void 0 ? void 0 : keyMatcher.resolve(key, parent);
|
|
789
1013
|
if (fieldRule) {
|
|
790
|
-
|
|
791
|
-
return value;
|
|
792
|
-
}
|
|
793
|
-
const replacement = buildReplacement(ctx, fieldRule.id, value);
|
|
794
|
-
counts[fieldRule.id] = (counts[fieldRule.id] ?? 0) + 1;
|
|
795
|
-
if (withSegments) {
|
|
796
|
-
originals.push({ value, wholeValue: true });
|
|
797
|
-
}
|
|
798
|
-
if (withMatches) {
|
|
799
|
-
const located = locateQuotedJsonValue(content, value);
|
|
800
|
-
matches.push({
|
|
801
|
-
ruleId: fieldRule.id,
|
|
802
|
-
original: value,
|
|
803
|
-
replacement,
|
|
804
|
-
index: located?.index ?? 0,
|
|
805
|
-
...located && ctx.contextChars > 0 ? sliceContext(content, located.index, located.length, ctx.contextChars) : ctx.contextChars > 0 ? { contextBefore: "", contextAfter: "" } : {}
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
return replacement;
|
|
1014
|
+
return redactField(fieldRule, value, false) ?? value;
|
|
809
1015
|
}
|
|
810
1016
|
const result = redactLine(value, ctx, { withMatches: collectOriginals });
|
|
811
1017
|
mergeCounts(counts, result.counts);
|
|
@@ -822,12 +1028,12 @@ function redactJsonLine(line, ctx, options = {}) {
|
|
|
822
1028
|
return result.output;
|
|
823
1029
|
}
|
|
824
1030
|
if (Array.isArray(value)) {
|
|
825
|
-
return value.map((item) => redactValue(item));
|
|
1031
|
+
return value.map((item) => redactValue(item, key, parent));
|
|
826
1032
|
}
|
|
827
1033
|
if (value !== null && typeof value === "object") {
|
|
828
1034
|
const out = {};
|
|
829
1035
|
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
830
|
-
out[entryKey] = redactValue(entryValue, entryKey);
|
|
1036
|
+
out[entryKey] = redactValue(entryValue, entryKey, key);
|
|
831
1037
|
}
|
|
832
1038
|
return out;
|
|
833
1039
|
}
|
|
@@ -855,10 +1061,10 @@ function createReportCollector(options = {}) {
|
|
|
855
1061
|
const maxPerRule = options.maxReplacementsPerRule;
|
|
856
1062
|
const limited = maxPerRule !== void 0;
|
|
857
1063
|
if (previewBytes < 0) {
|
|
858
|
-
throw new
|
|
1064
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("report.previewBytes must not be negative.");
|
|
859
1065
|
}
|
|
860
1066
|
if (limited && (maxPerRule < 0 || !Number.isInteger(maxPerRule))) {
|
|
861
|
-
throw new
|
|
1067
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("report.maxReplacementsPerRule must be a non-negative integer.");
|
|
862
1068
|
}
|
|
863
1069
|
const counts = {};
|
|
864
1070
|
const replacements = /* @__PURE__ */ new Map();
|
|
@@ -938,15 +1144,15 @@ function createReportCollector(options = {}) {
|
|
|
938
1144
|
// src/core/resolveRules.ts
|
|
939
1145
|
function toRule(selector) {
|
|
940
1146
|
if (typeof selector === "string") {
|
|
941
|
-
const builtin =
|
|
1147
|
+
const builtin = chunkVELXTD3L_cjs.getBuiltinRule(selector);
|
|
942
1148
|
if (!builtin) {
|
|
943
|
-
throw new
|
|
1149
|
+
throw new chunkVELXTD3L_cjs.UnknownRuleError(
|
|
944
1150
|
`Unknown rule "${selector}". Pass a rule object for custom rules, or one of the built-in identifiers.`
|
|
945
1151
|
);
|
|
946
1152
|
}
|
|
947
1153
|
return builtin;
|
|
948
1154
|
}
|
|
949
|
-
return
|
|
1155
|
+
return chunkVELXTD3L_cjs.validateRule(selector);
|
|
950
1156
|
}
|
|
951
1157
|
function resolveRules(selectors, extraRules = []) {
|
|
952
1158
|
const resolved = [];
|
|
@@ -984,7 +1190,7 @@ function createSanitizer(options = {}) {
|
|
|
984
1190
|
assertKey(key, keyEncoding);
|
|
985
1191
|
const contextChars = options.report?.contextChars ?? 0;
|
|
986
1192
|
if (contextChars < 0) {
|
|
987
|
-
throw new
|
|
1193
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("report.contextChars must not be negative.");
|
|
988
1194
|
}
|
|
989
1195
|
const selected = resolveWithAlwaysRedact(options);
|
|
990
1196
|
const ctx = createRuleContext({
|
|
@@ -1028,7 +1234,7 @@ function createSanitizer(options = {}) {
|
|
|
1028
1234
|
let charsRead = 0;
|
|
1029
1235
|
const writeLine = async (line) => {
|
|
1030
1236
|
if (signal?.aborted === true) {
|
|
1031
|
-
throw new
|
|
1237
|
+
throw new chunkVELXTD3L_cjs.SanitizationAbortedError();
|
|
1032
1238
|
}
|
|
1033
1239
|
await sink.write(processLine(line, ctx, collector));
|
|
1034
1240
|
};
|
|
@@ -1048,13 +1254,13 @@ function createSanitizer(options = {}) {
|
|
|
1048
1254
|
return { key, rules, sanitizeText: sanitizeText2, sanitizeStream: sanitizeStream2 };
|
|
1049
1255
|
}
|
|
1050
1256
|
function resolveWithAlwaysRedact(options) {
|
|
1051
|
-
const selected = resolveRules(options.rules ??
|
|
1257
|
+
const selected = resolveRules(options.rules ?? chunkVELXTD3L_cjs.builtinRuleIds, options.extraRules);
|
|
1052
1258
|
const alwaysRule = createAlwaysRedactRule(options.alwaysRedact);
|
|
1053
1259
|
if (!alwaysRule) {
|
|
1054
1260
|
return selected;
|
|
1055
1261
|
}
|
|
1056
1262
|
if (selected.some((rule) => rule.id === alwaysRule.id)) {
|
|
1057
|
-
throw new
|
|
1263
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError(
|
|
1058
1264
|
`alwaysRedact.ruleId "${alwaysRule.id}" collides with an active rule. Choose an identifier that is not in use.`
|
|
1059
1265
|
);
|
|
1060
1266
|
}
|
|
@@ -1247,15 +1453,15 @@ async function snapToNewline(blob, from, probeBytes, maxProbeBytes) {
|
|
|
1247
1453
|
}
|
|
1248
1454
|
async function planLineAlignedRanges(blob, partCount, options = {}) {
|
|
1249
1455
|
if (!Number.isInteger(partCount) || partCount < 1) {
|
|
1250
|
-
throw new
|
|
1456
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("partCount must be an integer >= 1.");
|
|
1251
1457
|
}
|
|
1252
1458
|
const probeBytes = options.probeBytes ?? DEFAULT_RANGE_PROBE_BYTES;
|
|
1253
1459
|
const maxProbeBytes = options.maxProbeBytes ?? DEFAULT_RANGE_MAX_PROBE_BYTES;
|
|
1254
1460
|
if (!Number.isInteger(probeBytes) || probeBytes < 1) {
|
|
1255
|
-
throw new
|
|
1461
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("probeBytes must be an integer >= 1.");
|
|
1256
1462
|
}
|
|
1257
1463
|
if (!Number.isInteger(maxProbeBytes) || maxProbeBytes < probeBytes) {
|
|
1258
|
-
throw new
|
|
1464
|
+
throw new chunkVELXTD3L_cjs.InvalidOptionError("maxProbeBytes must be an integer >= probeBytes.");
|
|
1259
1465
|
}
|
|
1260
1466
|
if (blob.size <= 0) {
|
|
1261
1467
|
return [];
|
|
@@ -1317,5 +1523,5 @@ exports.sanitizeText = sanitizeText;
|
|
|
1317
1523
|
exports.toCallbackSink = toCallbackSink;
|
|
1318
1524
|
exports.toNullSink = toNullSink;
|
|
1319
1525
|
exports.toStringSink = toStringSink;
|
|
1320
|
-
//# sourceMappingURL=chunk-
|
|
1321
|
-
//# sourceMappingURL=chunk-
|
|
1526
|
+
//# sourceMappingURL=chunk-ZCWN2P2R.cjs.map
|
|
1527
|
+
//# sourceMappingURL=chunk-ZCWN2P2R.cjs.map
|