import { AsyncBufferedLogWriter } from "./async-buffered-log-writer.js"; import { CONFIG_PATH, DEBUG_DIR, DEBUG_LOG_PATH, MULTI_AUTH_EXTENSION_ID, ensureMultiAuthDebugDirectory, loadMultiAuthConfig, } from "./config.js"; export interface MultiAuthDebugLoggerOptions { configPath?: string; debugDir?: string; logPath?: string; } /** * Matches JSON keys that likely contain secret material. * Covers: tokens, secrets, passwords, authorization headers, API keys, and OAuth access/refresh fields. */ const SENSITIVE_KEY_PATTERN = /(?:token|secret|password|authorization|key$|^(?:access|refresh)$)/i; function safeJsonStringify(value: unknown): string { const seen = new WeakSet(); return JSON.stringify(value, (key, currentValue) => { if (key !== "" && SENSITIVE_KEY_PATTERN.test(key)) { return "[REDACTED]"; } if (currentValue instanceof Error) { return { name: currentValue.name, message: currentValue.message, stack: currentValue.stack, }; } if (typeof currentValue === "bigint") { return currentValue.toString(); } if (typeof currentValue === "object" && currentValue !== null) { if (seen.has(currentValue)) { return "[Circular]"; } seen.add(currentValue); } return currentValue; }); } export class MultiAuthDebugLogger { private initialized = false; private readonly writer: AsyncBufferedLogWriter; constructor(private readonly options: MultiAuthDebugLoggerOptions = {}) { this.writer = new AsyncBufferedLogWriter({ enabled: false, logPath: this.options.logPath ?? DEBUG_LOG_PATH, ensureDirectory: () => ensureMultiAuthDebugDirectory(this.options.debugDir ?? DEBUG_DIR), createDroppedEntriesLine: (droppedEntries) => `${safeJsonStringify({ timestamp: new Date().toISOString(), level: "warn", extension: MULTI_AUTH_EXTENSION_ID, event: "debug_log_overflow", droppedEntries, })}\n`, }); } private initialize(): void { if (this.initialized) { return; } this.initialized = true; const configResult = loadMultiAuthConfig(this.options.configPath ?? CONFIG_PATH); this.writer.setEnabled(configResult.config.debug); } log(event: string, payload: Record = {}): void { try { this.initialize(); this.writer.writeLine( `${safeJsonStringify({ timestamp: new Date().toISOString(), level: "debug", extension: MULTI_AUTH_EXTENSION_ID, event, ...payload, })}\n`, ); } catch { // Debug log failures must never affect credential rotation. } } flush(): Promise { return this.writer.flush(); } dispose(): Promise { return this.writer.dispose(); } } export const multiAuthDebugLogger = new MultiAuthDebugLogger();