mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
fix(security): replace regex HTML sanitizer with DOMPurify to prevent XSS (#510)
* fix(security): replace regex HTML sanitizer with DOMPurify to prevent XSS The previous sanitizeRenderedHtml() used regex patterns to strip dangerous HTML tags and event handler attributes before assigning to innerHTML. Regex- based HTML sanitization is notoriously bypassable via: - SVG/MathML elements not in the blocklist (<svg onload=...>) - Newline-split event handlers (<img src=x on\nload=alert(1)>) - Mutation XSS (browser parsing quirks that reconstruct dangerous DOM) - Encoded attribute values and alternative quote styles - Nested/recursive tag patterns that defeat linear regex This is exploitable through prompt injection: if an LLM tool output contains crafted HTML, it flows through marked.parse() -> sanitizeRenderedHtml() -> innerHTML, allowing script execution in the user's browser session. Replace the regex sanitizer with DOMPurify 3.2.3, the industry-standard DOM-based HTML sanitizer. DOMPurify parses HTML into a real DOM tree and walks it node-by-node, which eliminates all known bypass vectors. It is used by Mozilla, Google, and most major web applications. CDN: cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.3/purify.min.js SRI: sha384-osZDKVu4ipZP703HmPOhWdyBajcFyjX2Psjk//TG1Rc0AdwEtuToaylrmcK3LdAl Audited all 60+ innerHTML assignments in app.js: - 5 use renderMarkdown() -> now protected by DOMPurify - Remainder use escapeHtml(), static literals, or empty strings Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(security): guard sanitizeRenderedHtml against DOMPurify CDN unavailability [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
d313f44a19
commit
28a22f2a59
@@ -676,26 +676,20 @@ function renderMarkdown(text) {
|
||||
return escapeHtml(text);
|
||||
}
|
||||
|
||||
// Strip dangerous HTML elements and attributes from rendered markdown.
|
||||
// This prevents XSS from tool output or prompt injection in LLM responses.
|
||||
// Sanitize rendered HTML using DOMPurify to prevent XSS from tool output
|
||||
// or prompt injection in LLM responses. DOMPurify is a DOM-based sanitizer
|
||||
// that handles all known bypass vectors (SVG onload, newline-split event
|
||||
// handlers, mutation XSS, etc.) unlike the regex approach it replaces.
|
||||
function sanitizeRenderedHtml(html) {
|
||||
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
||||
html = html.replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, '');
|
||||
html = html.replace(/<object\b[^>]*>[\s\S]*?<\/object>/gi, '');
|
||||
html = html.replace(/<embed\b[^>]*\/?>/gi, '');
|
||||
html = html.replace(/<form\b[^>]*>[\s\S]*?<\/form>/gi, '');
|
||||
html = html.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '');
|
||||
html = html.replace(/<link\b[^>]*\/?>/gi, '');
|
||||
html = html.replace(/<base\b[^>]*\/?>/gi, '');
|
||||
html = html.replace(/<meta\b[^>]*\/?>/gi, '');
|
||||
// Remove event handler attributes (onclick, onerror, onload, etc.)
|
||||
html = html.replace(/\s+on\w+\s*=\s*"[^"]*"/gi, '');
|
||||
html = html.replace(/\s+on\w+\s*=\s*'[^']*'/gi, '');
|
||||
html = html.replace(/\s+on\w+\s*=\s*[^\s>]+/gi, '');
|
||||
// Remove javascript: and data: URLs in href/src attributes
|
||||
html = html.replace(/(href|src|action)\s*=\s*["']?\s*javascript\s*:/gi, '$1="');
|
||||
html = html.replace(/(href|src|action)\s*=\s*["']?\s*data\s*:/gi, '$1="');
|
||||
return html;
|
||||
if (typeof DOMPurify !== 'undefined') {
|
||||
return DOMPurify.sanitize(html, {
|
||||
USE_PROFILES: { html: true },
|
||||
FORBID_TAGS: ['style', 'script'],
|
||||
FORBID_ATTR: ['style', 'onerror', 'onload']
|
||||
});
|
||||
}
|
||||
// DOMPurify not available (CDN unreachable) — return empty string rather than unsanitized HTML
|
||||
return '';
|
||||
}
|
||||
|
||||
function copyCodeBlock(btn) {
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
<script src="/i18n/en.js"></script>
|
||||
<script src="/i18n/zh-CN.js"></script>
|
||||
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.3/purify.min.js"
|
||||
integrity="sha384-osZDKVu4ipZP703HmPOhWdyBajcFyjX2Psjk//TG1Rc0AdwEtuToaylrmcK3LdAl"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/[email protected]/lib/marked.umd.min.js"
|
||||
integrity="sha384-pN9zSKOnTZwXRtYZAu0PBPEgR2B7DOC1aeLxQ33oJ0oy5iN1we6gm57xldM2irDG"
|
||||
|
||||
Reference in New Issue
Block a user