monacousa-portal/src/lib/server/sanitize.ts

62 lines
2.5 KiB
TypeScript

/**
* Centralized input sanitization utilities for preventing XSS attacks.
* Used at server-side input boundaries before storing or rendering user content.
*/
/**
* Escape HTML special characters to prevent XSS.
* Use for plain text fields that should never contain HTML.
*/
export function escapeHtml(str: string): string {
return str
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* Sanitize rich text by stripping dangerous HTML tags while preserving safe formatting.
* Allows: p, br, strong, em, ul, ol, li, h1-h6, a (with href only), blockquote, code, pre
* Strips: script, style, iframe, object, embed, form, input, on* attributes
*/
export function sanitizeRichText(html: string): string {
// Remove script tags and their contents
let cleaned = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
// Remove style tags and their contents
cleaned = cleaned.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '');
// Remove iframe, object, embed, form, input tags
cleaned = cleaned.replace(/<\/?(?:iframe|object|embed|form|input|textarea|select|button)\b[^>]*>/gi, '');
// Remove all on* event handlers (onclick, onerror, onload, etc.)
cleaned = cleaned.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi, '');
// Remove javascript: protocol from href/src attributes
cleaned = cleaned.replace(/(href|src|action)\s*=\s*(?:"javascript:[^"]*"|'javascript:[^']*')/gi, '$1=""');
// Remove data: protocol (except data:image for inline images)
cleaned = cleaned.replace(/(href|src|action)\s*=\s*(?:"data:(?!image)[^"]*"|'data:(?!image)[^']*')/gi, '$1=""');
return cleaned;
}
/**
* Sanitize a URL to prevent javascript: and data: protocol injection.
* Returns the URL if safe, or empty string if potentially malicious.
*/
export function sanitizeUrl(url: string): string {
const trimmed = url.trim();
// Block javascript: protocol (case-insensitive, handles whitespace/encoding tricks)
if (/^\s*javascript\s*:/i.test(trimmed)) return '';
// Block data: protocol (except data:image)
if (/^\s*data\s*:(?!image\/)/i.test(trimmed)) return '';
// Block vbscript: protocol
if (/^\s*vbscript\s*:/i.test(trimmed)) return '';
return trimmed;
}
/**
* Sanitize a plain text string for safe storage.
* Trims whitespace and limits length.
*/
export function sanitizeText(str: string, maxLength: number = 10000): string {
return str.trim().substring(0, maxLength);
}