-
feat: Add DOMPurify XSS prevention for UI components (#1141)
发布于
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.2to 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.jsFeatures:
sanitizeHTML()- Sanitize HTML strings with configurable levelssafeSetHTML()- Safe wrapper for innerHTML assignmentsescapeHTML()- Escape HTML for plain text displaysafeSetText()- Safe wrapper for textContentsanitizeURL()- Prevent javascript: and data: URI attackscreateSafeElement()- 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 messagesshowError()- Error messagesshowMessage()- Toast notificationsshowAlert()- Alert banners
Changes:
- Replaced unsafe
innerHTMLwithsafeSetHTML() - Used
textContentfor 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 correctlyRemaining Work
Future PRs:
-
Additional components (~25 files):
- message_handler.js - Chat messages
- research_ui.js - Research results
- citations.js - Citation rendering
- Other UI components
-
Content Security Policy (separate PR):
- Remove
unsafe-inlinefrom CSP - Add CSP nonces for inline scripts
- Restrict connect-src domains
- Remove
-
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
下载附件