export interface SanitizeOptions { repoRoot?: string; // e.g. /home/user/.disclaw home?: string; // os.homedir() pathMappings?: Array<{ host: string; virtual: string }>; } /** * Sanitizes agent output before it is sent to a Discord channel. * Replaces absolute paths and sensitive tokens so they are never leaked. * * Replacement order is intentional — longest/most-specific patterns first: * 1. repoRoot → * 2. home → ~ * 3. Discord bot token pattern → * 4. Anthropic API key pattern → */ /** Returns both backslash and forward-slash variants of a path (for Windows). */ function pathVariants(p: string): string[] { const fwd = p.replace(/\\/g, "/"); const bwd = p.replace(/\//g, "\\"); return fwd === bwd ? [p] : [p, fwd, bwd]; } function replaceAll(text: string, search: string, replacement: string): string { return text.split(search).join(replacement); } export function sanitizeForDiscord( text: string, opts: SanitizeOptions = {} ): string { let result = text; // 0. Replace path mappings first (most-specific, longest host paths first) // so they take precedence over generic repoRoot/home replacements. if (opts.pathMappings && opts.pathMappings.length > 0) { const sorted = [...opts.pathMappings].sort( (a, b) => b.host.length - a.host.length ); for (const mapping of sorted) { for (const variant of pathVariants(mapping.host)) { result = replaceAll(result, variant, mapping.virtual); } } } // 1. Replace repo root before home so that sub-paths are caught by the // more specific substitution first (repoRoot is usually inside home). if (opts.repoRoot) { for (const variant of pathVariants(opts.repoRoot)) { result = replaceAll(result, variant, ""); } } // 2. Replace home directory with ~ (both slash styles for Windows) if (opts.home) { for (const variant of pathVariants(opts.home)) { result = replaceAll(result, variant, "~"); } } // 3. Discord bot token — format: [MN]<23 base64url>.<6 base64url>.<27 base64url> result = result.replace( /[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27}/g, "" ); // 4. Anthropic API key — format: sk-ant-<20+ base64url chars> result = result.replace(/sk-ant-[A-Za-z0-9\-_]{20,}/g, ""); return result; }