diff --git a/src/redact.ts b/src/redact.ts index a668a72..4b4bd75 100644 --- a/src/redact.ts +++ b/src/redact.ts @@ -90,13 +90,23 @@ const getReplaceMeta = (args: unknown[]): ReplaceMeta | null => { return { offset, whole }; }; +// Ensure the regex is global so String.replace() visits *all* matches. +// (DeepRedact may provide non-global patterns; we still want to replace all.) +const asGlobal = (re: RegExp) => { + if (re.global) return re; + return new RegExp( + re.source, + re.flags.includes("g") ? re.flags : re.flags + "g", + ); +}; + const replaceAllMatchesWithContext = ( value: string, pattern: RegExp, replacement: string, allow?: ContextRule[], ) => - value.replace(pattern, (...args: unknown[]) => { + value.replace(asGlobal(pattern), (...args: unknown[]) => { const match = args[0]; if (typeof match !== "string") return replacement; @@ -118,7 +128,7 @@ const replaceBip39MnemonicMatchesWithContext = ( replacement: string, allow?: ContextRule[], ) => - value.replace(pattern, (...args: unknown[]) => { + value.replace(asGlobal(pattern), (...args: unknown[]) => { const match = args[0]; const phrase = args[1]; diff --git a/tests/redact.test.ts b/tests/redact.test.ts index 4d46530..2b0a158 100644 --- a/tests/redact.test.ts +++ b/tests/redact.test.ts @@ -41,4 +41,16 @@ describe("redact", () => { "Pushed event [REDACTED].[REDACTED] with key [REDACTED]", ); }); + + it("redacts only once with allowlist", () => { + const redact = createRedact({ + hex: { allow: [{ re: /\b(intent)\b/i }] }, + }); + const result = redact( + `This is an intent b3c1c51b70cd602cc9a5f76d3795b6eca27a89f884ba8977b604451333393530 but this is a private key: 9f4613930bc9d4ad3b2d838d79af0763538b2cee70083b281e2868f4632920b0`, + ); + expect(result).toBe( + `This is an intent b3c1c51b70cd602cc9a5f76d3795b6eca27a89f884ba8977b604451333393530 but this is a private key: [REDACTED]`, + ); + }); });