export interface SanitizeOptions { repoRoot?: string; // e.g. /home/user/.disclaw home?: string; // os.homedir() } /** * 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; // 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; }