diff --git a/src/logging/redact-bounded.ts b/src/logging/redact-bounded.ts new file mode 100644 index 000000000..ff1f4c2ae --- /dev/null +++ b/src/logging/redact-bounded.ts @@ -0,0 +1,26 @@ +export const REDACT_REGEX_CHUNK_THRESHOLD = 32_768; +export const REDACT_REGEX_CHUNK_SIZE = 16_384; + +type BoundedRedactOptions = { + chunkThreshold?: number; + chunkSize?: number; +}; + +export function replacePatternBounded( + text: string, + pattern: RegExp, + replacer: Parameters[1], + options?: BoundedRedactOptions, +): string { + const chunkThreshold = options?.chunkThreshold ?? REDACT_REGEX_CHUNK_THRESHOLD; + const chunkSize = options?.chunkSize ?? REDACT_REGEX_CHUNK_SIZE; + if (chunkThreshold <= 0 || chunkSize <= 0 || text.length <= chunkThreshold) { + return text.replace(pattern, replacer); + } + + let output = ""; + for (let index = 0; index < text.length; index += chunkSize) { + output += text.slice(index, index + chunkSize).replace(pattern, replacer); + } + return output; +} diff --git a/src/logging/redact.ts b/src/logging/redact.ts index e0c38ccba..7e47ac0b6 100644 --- a/src/logging/redact.ts +++ b/src/logging/redact.ts @@ -1,6 +1,7 @@ import type { OpenClawConfig } from "../config/config.js"; import { compileSafeRegex } from "../security/safe-regex.js"; import { resolveNodeRequireFromMeta } from "./node-require.js"; +import { replacePatternBounded } from "./redact-bounded.js"; const requireConfig = resolveNodeRequireFromMeta(import.meta.url); @@ -10,8 +11,6 @@ const DEFAULT_REDACT_MODE: RedactSensitiveMode = "tools"; const DEFAULT_REDACT_MIN_LENGTH = 18; const DEFAULT_REDACT_KEEP_START = 6; const DEFAULT_REDACT_KEEP_END = 4; -const REDACT_REGEX_CHUNK_THRESHOLD = 32_768; -const REDACT_REGEX_CHUNK_SIZE = 16_384; const DEFAULT_REDACT_PATTERNS: string[] = [ // ENV-style assignments. @@ -96,26 +95,12 @@ function redactMatch(match: string, groups: string[]): string { return match.replace(token, masked); } -function replacePatternWithBounds(text: string, pattern: RegExp): string { - const apply = (value: string) => - value.replace(pattern, (...args: string[]) => - redactMatch(args[0], args.slice(1, args.length - 2)), - ); - if (text.length <= REDACT_REGEX_CHUNK_THRESHOLD) { - return apply(text); - } - - let output = ""; - for (let index = 0; index < text.length; index += REDACT_REGEX_CHUNK_SIZE) { - output += apply(text.slice(index, index + REDACT_REGEX_CHUNK_SIZE)); - } - return output; -} - function redactText(text: string, patterns: RegExp[]): string { let next = text; for (const pattern of patterns) { - next = replacePatternWithBounds(next, pattern); + next = replacePatternBounded(next, pattern, (...args: string[]) => + redactMatch(args[0], args.slice(1, args.length - 2)), + ); } return next; }