62 lines
2.5 KiB
TypeScript
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, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|