/** * Input Sanitization Utilities * * Provides XSS protection for user-generated content. * All user input should be sanitized before rendering to prevent injection attacks. */ import DOMPurify from 'dompurify'; import { logger } from './logger'; /** * Sanitize HTML content to prevent XSS attacks * * @param html - Raw HTML string from user input * @returns Sanitized HTML safe for rendering */ export function sanitizeHTML(html: string): string { return DOMPurify.sanitize(html, { ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'a', 'ul', 'ol', 'li'], ALLOWED_ATTR: ['href', 'target', 'rel'], ALLOW_DATA_ATTR: false, }); } /** * Sanitize URL to prevent javascript: and data: protocol injection * * @param url - URL from user input * @returns Sanitized URL or '#' if invalid */ export function sanitizeURL(url: string): string { if (!url || typeof url !== 'string') { return '#'; } try { const parsed = new URL(url); // Only allow http, https, and mailto protocols const allowedProtocols = ['http:', 'https:', 'mailto:']; if (!allowedProtocols.includes(parsed.protocol)) { logger.warn('Blocked potentially dangerous URL protocol', { protocol: parsed.protocol }); return '#'; } return url; } catch { // Invalid URL format logger.warn('Invalid URL format', { url }); return '#'; } } /** * Sanitize plain text to prevent any HTML rendering * Escapes all HTML entities * * @param text - Plain text from user input * @returns Escaped text safe for rendering */ export function sanitizePlainText(text: string): string { if (!text || typeof text !== 'string') { return ''; } return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/\//g, '/'); } /** * Check if a string contains potentially dangerous content * Used for validation before sanitization * * @param input - User input to check * @returns true if input contains suspicious patterns */ export function containsSuspiciousContent(input: string): boolean { if (!input || typeof input !== 'string') { return false; } const suspiciousPatterns = [ /