const IBAN_IN_TEXT_PATTERN = /\b([A-Z]{2}\d{2}(?:[\s-]?[A-Z0-9]){11,30})(?=["'\]},.;:!?)]|$)/gi export function maskIban(iban: string): string { const normalized = iban.replace(/[\s-]+/g, "").toUpperCase() if (normalized.length <= 8) return normalized return `${normalized.slice(0, 4)} **** **** ${normalized.slice(-4)}` } export function sanitizeHistoryText(text: string): string { return text.replace(IBAN_IN_TEXT_PATTERN, (candidate) => maskIban(candidate)) } function sanitizeIbanField(value: any): any { if (typeof value === "string") return maskIban(value) if (Array.isArray(value)) return value.map(sanitizeIbanField) return sanitizeHistoryValue(value) } export function sanitizeHistoryValue(value: any): any { if (typeof value === "string") return sanitizeHistoryText(value) if (Array.isArray(value)) return value.map(sanitizeHistoryValue) if (!value || typeof value !== "object" || value instanceof Date) return value const prototype = Object.getPrototypeOf(value) if (prototype !== Object.prototype && prototype !== null) return value return Object.fromEntries( Object.entries(value).map(([key, nestedValue]) => [ key, key.toLowerCase().includes("iban") ? sanitizeIbanField(nestedValue) : sanitizeHistoryValue(nestedValue), ]) ) } export function sanitizeHistoryItem>(item: T): T { return { ...item, text: typeof item.text === "string" ? sanitizeHistoryText(item.text) : item.text, oldVal: sanitizeHistoryValue(item.oldVal), newVal: sanitizeHistoryValue(item.newVal), config: sanitizeHistoryValue(item.config), } }