// Centralized text-to-HTML escaping. Use everywhere user-provided strings flow // into an HTML context (emails, raw markup, etc). React's JSX rendering escapes // by default; this is for the non-JSX paths. const REPLACE: Record = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", }; export function escapeHtml(input: unknown): string { if (input === null || input === undefined) return ""; return String(input).replace(/[&<>"']/g, (c) => REPLACE[c] ?? c); } // Strip everything that smells like markup. Use for places where the value // should be plain text but might be rendered raw (mailer subject lines, etc.). export function stripTags(input: unknown): string { return String(input ?? "").replace(/<[^>]*>/g, ""); } // Quick allowlist URL check — refuses anything that isn't http/https. Used on // the redirect-after-submit setting and on webhook URLs. export function isSafeUrl(url: string): boolean { try { const u = new URL(url); return u.protocol === "http:" || u.protocol === "https:"; } catch { return false; } }