| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| export function escapeHtml(text, forAttribute = false) { |
| if (text === null || text === undefined) { |
| return ''; |
| } |
| |
| const str = String(text); |
| |
| const map = { |
| '&': '&', |
| '<': '<', |
| '>': '>', |
| '"': '"', |
| "'": ''' |
| }; |
| |
| let escaped = str.replace(/[&<>"']/g, m => map[m]); |
| |
| |
| if (forAttribute) { |
| escaped = escaped.replace(/"/g, '"').replace(/'/g, '''); |
| } |
| |
| return escaped; |
| } |
|
|
| |
| |
| |
| |
| |
| export function safeSetInnerHTML(element, html) { |
| if (!element || !(element instanceof HTMLElement)) { |
| console.warn('[Sanitizer] Invalid element provided to safeSetInnerHTML'); |
| return; |
| } |
| |
| |
| if (!html.includes('<') && !html.includes('>')) { |
| element.textContent = html; |
| return; |
| } |
| |
| |
| const temp = document.createElement('div'); |
| temp.innerHTML = html; |
| |
| |
| const walker = document.createTreeWalker( |
| temp, |
| NodeFilter.SHOW_TEXT, |
| null, |
| false |
| ); |
| |
| let node; |
| while (node = walker.nextNode()) { |
| if (node.textContent) { |
| node.textContent = node.textContent; |
| } |
| } |
| |
| |
| element.innerHTML = ''; |
| while (temp.firstChild) { |
| element.appendChild(temp.firstChild); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function sanitizeObject(obj, depth = 5) { |
| if (depth <= 0) { |
| return '[Max Depth Reached]'; |
| } |
| |
| if (obj === null || obj === undefined) { |
| return ''; |
| } |
| |
| if (typeof obj === 'string') { |
| return escapeHtml(obj); |
| } |
| |
| if (typeof obj === 'number' || typeof obj === 'boolean') { |
| return obj; |
| } |
| |
| if (Array.isArray(obj)) { |
| return obj.map(item => sanitizeObject(item, depth - 1)); |
| } |
| |
| if (typeof obj === 'object') { |
| const sanitized = {}; |
| for (const key in obj) { |
| if (Object.prototype.hasOwnProperty.call(obj, key)) { |
| sanitized[key] = sanitizeObject(obj[key], depth - 1); |
| } |
| } |
| return sanitized; |
| } |
| |
| return String(obj); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function safeFormatNumber(value, options = {}) { |
| if (value === null || value === undefined || isNaN(value)) { |
| return '—'; |
| } |
| |
| const num = Number(value); |
| if (isNaN(num)) { |
| return '—'; |
| } |
| |
| try { |
| return num.toLocaleString('en-US', { |
| minimumFractionDigits: options.minimumFractionDigits || 2, |
| maximumFractionDigits: options.maximumFractionDigits || 2, |
| ...options |
| }); |
| } catch (error) { |
| console.warn('[Sanitizer] Number formatting error:', error); |
| return String(num); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function safeFormatCurrency(value, currency = 'USD') { |
| if (value === null || value === undefined || isNaN(value)) { |
| return '—'; |
| } |
| |
| const num = Number(value); |
| if (isNaN(num)) { |
| return '—'; |
| } |
| |
| try { |
| return new Intl.NumberFormat('en-US', { |
| style: 'currency', |
| currency: currency, |
| minimumFractionDigits: 2, |
| maximumFractionDigits: 2 |
| }).format(num); |
| } catch (error) { |
| console.warn('[Sanitizer] Currency formatting error:', error); |
| return `$${num.toFixed(2)}`; |
| } |
| } |
|
|
|
|