PageSourceSearch

https://andyyen.net/js/utils/sanitize.js

js andyyen.net collected 2026-10-04 08:03:50 UTC 2,698 bytes, 90 lines download raw bytes

1/**
2 * HTML Sanitization Utilities
3 * Wraps DOMPurify for consistent sanitization across the site
4 */
5
6const SanitizeUtils = {
7  /**
8   * Sanitize HTML string for safe innerHTML insertion
9   * @param {string} html - The HTML string to sanitize
10   * @param {Object} options - Optional DOMPurify configuration
11   * @returns {string} - Sanitized HTML
12   */
13  sanitizeHTML(html, options = {}) {
14    if (typeof DOMPurify === 'undefined') {
15      console.error('DOMPurify is not loaded. Returning empty string for safety.');
16      return '';
17    }
18
19    // Default safe configuration
20    const defaultConfig = {
21      ALLOWED_TAGS: [
22        'p', 'br', 'strong', 'em', 'u', 'a', 'span', 'div',
23        'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
24        'ul', 'ol', 'li',
25        'img', 'figure', 'figcaption',
26        'blockquote', 'code', 'pre',
27        'table', 'thead', 'tbody', 'tr', 'th', 'td',
28        'svg', 'path', 'circle', 'rect', 'line', 'polyline', 'polygon',
29        'button', 'audio'
30      ],
31      ALLOWED_ATTR: [
32        'href', 'target', 'rel', 'class', 'id', 'style',
33        'src', 'alt', 'title', 'width', 'height', 'loading', 'decoding',
34        'data-*', 'aria-*',
35        'viewBox', 'xmlns', 'd', 'fill', 'stroke', 'stroke-width',
36        'type', 'disabled', 'preload', 'controls'
37      ],
38      ALLOW_DATA_ATTR: true,
39      ALLOW_ARIA_ATTR: true
40    };
41
42    const config = { ...defaultConfig, ...options };
43    return DOMPurify.sanitize(html, config);
44  },
45
46  /**
47   * Sanitize HTML with iframe support (for video embeds)
48   * @param {string} html - The HTML string to sanitize
49   * @returns {string} - Sanitized HTML with iframes allowed
50   */
51  sanitizeWithIframes(html) {
52    return this.sanitizeHTML(html, {
53      ADD_TAGS: ['iframe'],
54      ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'src']
55    });
56  },
57
58  /**
59   * Sanitize simple text content (loading states, errors)
60   * @param {string} html - The HTML string to sanitize
61   * @returns {string} - Sanitized HTML (very restrictive)
62   */
63  sanitizeSimple(html) {
64    return this.sanitizeHTML(html, {
65      ALLOWED_TAGS: ['div', 'p', 'span', 'strong', 'em'],
66      ALLOWED_ATTR: ['class', 'style']
67    });
68  },
69
70  /**
71   * Escape HTML for use in attributes or plain text
72   * @param {string} text - Text to escape
73   * @returns {string} - Escaped text
74   */
75  escapeHTML(text) {
76    if (!text) return '';
77    const div = document.createElement('div');
78    div.textContent = text;
79    return div.innerHTML;
80  }
81};
82
83// Export for use in other scripts
84if (typeof window !== 'undefined') {
85  window.SanitizeUtils = SanitizeUtils;
86}
87
88if (typeof module !== 'undefined' && module.exports) {
89  module.exports = SanitizeUtils;
90}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.