发布

  • feat: Add DOMPurify XSS prevention for UI components (#1141)

    frostbyte_neo 发布于 2025-11-24 22:08:14 +00:00

    • feat: Add DOMPurify XSS prevention for UI components

    Implements XSS prevention using DOMPurify library for user-facing UI components.

    Changes

    1. Added DOMPurify Library

    • Added dompurify@^3.2.2 to package.json dependencies
    • Industry-standard HTML sanitization library
    • Zero dependencies, actively maintained

    2. Created Sanitization Utility Module

    New file: src/local_deep_research/web/static/js/utils/sanitizer.js

    Features:

    • sanitizeHTML() - Sanitize HTML strings with configurable levels
    • safeSetHTML() - Safe wrapper for innerHTML assignments
    • escapeHTML() - Escape HTML for plain text display
    • safeSetText() - Safe wrapper for textContent
    • sanitizeURL() - Prevent javascript: and data: URI attacks
    • createSafeElement() - Create DocumentFragment from sanitized HTML

    Sanitization Levels:

    • strict - User-generated content (minimal tags)
    • ui - UI components (common elements, icons)
    • rich - Research content (full formatting, links, images)

    Security Features:

    • Whitelist-based tag and attribute filtering
    • Automatic target="_blank" rel="noopener noreferrer" for links
    • Blocks javascript:, data:, vbscript: URI schemes
    • Removes all inline event handlers (onclick, onerror, etc.)
    • Prevents DOM clobbering attacks

    3. Fixed Critical innerHTML Usage in ui.js

    Functions secured:

    • showSpinner() - Loading spinners with messages
    • showError() - Error messages
    • showMessage() - Toast notifications
    • showAlert() - Alert banners

    Changes:

    • Replaced unsafe innerHTML with safeSetHTML()
    • Used textContent for close button (× character)
    • All user-provided messages now sanitized

    Security Impact

    XSS Vectors Prevented:

    Before (Vulnerable):

    // Any of these could execute arbitrary JavaScript
    showMessage('<img src=x onerror="alert(\'XSS\')">', 'info');
    showError(container, '<script>steal_cookies()</script>');
    showSpinner(el, '<iframe src="evil.com"></iframe>');
    

    After (Protected):

    // All malicious code stripped, only safe content remains
    showMessage('<img src=x onerror="alert(\'XSS\')">', 'info');
    // Renders: <img src="x"> (onerror removed)
    
    showError(container, '<script>steal_cookies()</script>');
    // Renders: (script tag completely removed)
    
    showSpinner(el, '<iframe src="evil.com"></iframe>');
    // Renders: (iframe not in allowlist, removed)
    

    Attack Surface Reduction:

    • Toast notifications: User messages sanitized (XSS via API responses)
    • Error messages: Exception messages sanitized (XSS via error injection)
    • Loading spinners: Dynamic messages sanitized (XSS via status updates)
    • Alert banners: System alerts sanitized (XSS via server messages)

    Risk Mitigation:

    Component Risk Before Risk After Improvement
    Toast messages HIGH LOW Sanitized
    Error displays HIGH LOW Sanitized
    Alert banners MEDIUM LOW Sanitized
    Spinners LOW-MEDIUM LOW Sanitized

    Implementation Details

    DOMPurify Configuration

    Strict Config (user content):

    • Allowed tags: b, i, em, strong, a, p, br, span, div
    • Allowed attributes: href, title, class
    • No data attributes
    • No unknown protocols

    UI Config (components):

    • Allowed tags: div, span, i, strong, em, b, p, br, small, code, pre
    • Allowed attributes: class, id, title, aria-label, role, data-*
    • Data attributes enabled for UI state
    • No unknown protocols

    Rich Config (content):

    • Headings, lists, tables, blockquotes, images
    • Links with safe targets (target="_blank" rel="noopener noreferrer")
    • No inline event handlers

    Backward Compatibility

    • No breaking changes to function signatures
    • All existing calls to showMessage(), showError(), etc. work unchanged
    • Sanitization happens transparently
    • Only malicious content is blocked, safe content passes through

    Testing

    Manual Testing Checklist:

    • Toast notifications display correctly
    • Error messages render properly
    • Loading spinners show messages
    • Icons (FontAwesome) render in toasts/alerts
    • Close buttons (×) work in alerts
    • No console errors from DOMPurify

    XSS Test Cases:

    // Test 1: Script injection
    showMessage('<script>alert("XSS")</script>Hello', 'info');
    // Expected: "Hello" (script removed)
    
    // Test 2: Event handler injection
    showError(el, '<img src=x onerror=alert(1)>');
    // Expected: <img src="x"> (onerror removed)
    
    // Test 3: Iframe injection
    showSpinner(el, '<iframe src="javascript:alert(1)"></iframe>');
    // Expected: (iframe removed entirely)
    
    // Test 4: Safe HTML
    showMessage('<b>Bold</b> and <i>italic</i>', 'success');
    // Expected: Bold and italic text renders correctly
    

    Remaining Work

    Future PRs:

    1. Additional components (~25 files):

      • message_handler.js - Chat messages
      • research_ui.js - Research results
      • citations.js - Citation rendering
      • Other UI components
    2. Content Security Policy (separate PR):

      • Remove unsafe-inline from CSP
      • Add CSP nonces for inline scripts
      • Restrict connect-src domains
    3. Marked.js Integration:

      • Configure DOMPurify as Marked sanitizer
      • Apply to all markdown rendering

    Dependencies

    DOMPurify v3.2.2:

    • Size: ~20KB minified
    • Zero dependencies
    • Actively maintained by Cure53
    • Used by: Google, Microsoft, Facebook, etc.
    • License: MPL-2.0 / Apache-2.0

    Documentation

    For Developers:

    Use sanitizer for all dynamic HTML:

    import { safeSetHTML } from './utils/sanitizer.js';
    
    // Instead of:
    element.innerHTML = userContent;
    
    // Use:
    safeSetHTML(element, userContent, 'ui');
    

    For plain text, use textContent:

    // Safe by default - no HTML parsing
    element.textContent = userInput;
    

    For URLs:

    import { sanitizeURL } from './utils/sanitizer.js';
    
    const safe = sanitizeURL(userProvidedURL);
    if (safe) {
        link.href = safe;
    }
    
    • chore: auto-bump version to 1.2.19

    Co-authored-by: GitHub Action action@github.com

    下载附件