UNPKG

@ordojs/security

Version:

Security package for OrdoJS with XSS, CSRF, and injection protection

130 lines (112 loc) 3.49 kB
import createDOMPurify from 'dompurify'; import { JSDOM } from 'jsdom'; const window = new JSDOM('').window; const DOMPurify = createDOMPurify(window as any); /** * Configuration options for HTML sanitization */ export interface SanitizerOptions { /** * List of allowed HTML tags * @default ['a', 'b', 'br', 'code', 'div', 'em', 'i', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'ul'] */ allowedTags?: string[]; /** * List of allowed HTML attributes * @default ['href', 'target', 'class', 'id', 'style'] */ allowedAttributes?: string[] | Record<string, string[]>; /** * Whether to allow data attributes (data-*) * @default false */ allowDataAttributes?: boolean; /** * Whether to strip all HTML and return text only * @default false */ stripAllTags?: boolean; } /** * Default sanitizer options with safe defaults */ const DEFAULT_OPTIONS: SanitizerOptions = { allowedTags: ['a', 'b', 'br', 'code', 'div', 'em', 'i', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'ul'], allowedAttributes: ['href', 'target', 'class', 'id', 'style'], allowDataAttributes: false, stripAllTags: false, }; /** * HTML Sanitizer class for preventing XSS attacks * Uses DOMPurify under the hood with configurable options */ export class HtmlSanitizer { private options: SanitizerOptions; /** * Create a new HtmlSanitizer instance * @param options Sanitizer configuration options */ constructor(options: SanitizerOptions = {}) { this.options = { ...DEFAULT_OPTIONS, ...options }; } /** * Sanitize HTML content to prevent XSS attacks * @param html HTML content to sanitize * @returns Sanitized HTML */ sanitize(html: string): string { if (this.options.stripAllTags) { return DOMPurify.sanitize(html, { ALLOWED_TAGS: [] }) as unknown as string; } const config: any = { ALLOWED_TAGS: this.options.allowedTags, ALLOW_DATA_ATTR: this.options.allowDataAttributes, }; // Handle allowedAttributes which can be string[] or Record<string, string[]> if (Array.isArray(this.options.allowedAttributes)) { config.ALLOWED_ATTR = this.options.allowedAttributes; } else if (this.options.allowedAttributes) { config.ALLOWED_ATTR = this.options.allowedAttributes; } return DOMPurify.sanitize(html, config) as unknown as string; } /** * Update sanitizer options * @param options New sanitizer options */ updateOptions(options: Partial<SanitizerOptions>): void { this.options = { ...this.options, ...options }; } /** * Create a sanitizer with strict settings (minimal allowed tags) * @returns A new HtmlSanitizer instance with strict settings */ static createStrict(): HtmlSanitizer { return new HtmlSanitizer({ allowedTags: ['b', 'em', 'i', 'strong', 'span'], allowedAttributes: ['class'], allowDataAttributes: false, }); } /** * Create a sanitizer that strips all HTML tags * @returns A new HtmlSanitizer instance that strips all HTML */ static createTextOnly(): HtmlSanitizer { return new HtmlSanitizer({ stripAllTags: true, }); } } /** * Create a default HTML sanitizer instance */ export const defaultSanitizer = new HtmlSanitizer(); /** * Sanitize HTML content using the default sanitizer * @param html HTML content to sanitize * @returns Sanitized HTML */ export function sanitizeHtml(html: string): string { return defaultSanitizer.sanitize(html); }