/** * Sanitizes a user-supplied URL to prevent phishing and script injection. * * Rules: * - Only allows https:// and http:// schemes * - Blocks javascript:, data:, vbscript:, and all other schemes * - Returns null for invalid / unsafe URLs (caller should hide the link) * * VPN/Tor users are unaffected — this validates the URL scheme & structure, * not the user's network origin. */ export function sanitizeUrl(url: string | undefined | null): string | null { if (!url) return null; const trimmed = url.trim(); if (!trimmed) return null; try { const parsed = new URL(trimmed); // Only allow safe schemes if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { return null; } return parsed.href; } catch { // Not a valid URL return null; } } /** * Extracts just the hostname for display (e.g. "Opens: example.com") */ export function urlDomain(url: string): string { try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return url; } } /** * Sanitizes a tel: or mailto: href. * Only allows the specific scheme, strips anything else. */ export function sanitizeTel(value: string | undefined | null): string | null { if (!value) return null; const trimmed = value.trim(); // Allow digits, +, -, spaces, parentheses only const digits = trimmed.replace(/[^0-9+\-\s().]/g, ''); return digits.length >= 5 ? `tel:${digits}` : null; } export function sanitizeEmail(value: string | undefined | null): string | null { if (!value) return null; const trimmed = value.trim(); // Basic email pattern if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) { return `mailto:${trimmed}`; } return null; }