import logger from "./logger"; // Fields that might contain sensitive data const SENSITIVE_FIELDS = [ "accessToken", "api_key", "apiKey", "auth", "authorization", "client_email", "client_id", "client_secret", "credential", "encrypted_credentials", "hash", "key", "password", "private_key", "refreshToken", "secret", "serviceAccountKey", "tenant_id", "token", ]; // Create a Set for efficient O(1) average time complexity lookups const SENSITIVE_FIELDS_SET = new Set(SENSITIVE_FIELDS); /** * Creates a replacer function for JSON.stringify that redacts sensitive fields. * This approach is more efficient as it leverages native JSON serialization. */ function createReplacer(): (key: string, value: unknown) => unknown { // Use a WeakSet to track circular references const seen = new WeakSet(); return function replacer(key: string, value: unknown): unknown { // Handle circular references if (value && typeof value === "object") { if (seen.has(value)) { return "[Circular]"; } seen.add(value); } // Redact sensitive fields if (SENSITIVE_FIELDS_SET.has(key)) { return "[REDACTED]"; } // Handle Error objects specially to preserve their structure if (value instanceof Error) { const errorObj: Record = { name: value.name, message: value.message, }; if (value.stack) { errorObj.stack = value.stack; } // Handle cause property safely if ("cause" in value && value.cause !== undefined) { errorObj.cause = value.cause; } return errorObj; } return value; }; } /** * Redacts sensitive data from an object by replacing sensitive fields with a placeholder. * Uses JSON.stringify's replacer function for efficient serialization and redaction. */ export function redactSensitiveData(data: unknown): unknown { if (!data || typeof data !== "object") return data; try { const replacer = createReplacer(); const jsonString = JSON.stringify(data, replacer); return JSON.parse(jsonString); } catch (error) { logger.error("Failed to redact sensitive data", error); return { error: "Redaction failed" }; } }