[CRITICAL] Fix XSS vulnerability in Mermaid rendering #190

Closed
opened 2026-02-02 17:25:03 +00:00 by jason.woltje · 0 comments
Owner

Problem

MermaidViewer component uses securityLevel: "loose" and directly injects SVG via innerHTML, creating a direct XSS vector if diagrams are user-controlled.

Location

apps/web/components/MermaidViewer.tsx

  • Line 34: securityLevel: "loose"
  • Line 42: innerHTML injection
  • Line 53: dangerouslySetInnerHTML
mermaid.initialize({
  securityLevel: "loose",  // ❌ Allows script execution in SVG
  // ...
});

element.innerHTML = svg;  // ❌ Direct HTML injection

Attack Vector

If users can provide or modify Mermaid diagrams:

graph TD
  A[Normal Node] -->|<img src=x onerror=alert(document.cookie)>| B

Impact

  • CRITICAL: Full XSS if diagrams are user-controlled
  • Session hijacking via cookie theft
  • Account takeover
  • Malicious code execution in user browser
  • Blocks production deployment

Questions

  1. Are Mermaid diagrams user-controlled or admin-only?
  2. Is diagram content validated/sanitized server-side?

Acceptance Criteria

  • Change securityLevel to "strict"
  • Sanitize SVG output before injection
  • Use DOMPurify for HTML sanitization
  • Add CSP headers to prevent inline scripts
  • Validate diagram syntax server-side
  • Add tests for XSS payloads
  • Document safe diagram usage

Implementation

import DOMPurify from 'dompurify';

mermaid.initialize({
  securityLevel: "strict",  // NEW: Block script execution
  // ...
});

const { svg } = await mermaid.render('id', content);
const sanitized = DOMPurify.sanitize(svg, {
  USE_PROFILES: { svg: true, svgFilters: true },
  FORBID_TAGS: ['script', 'iframe'],
  FORBID_ATTR: ['onerror', 'onload']
});
element.innerHTML = sanitized;

Testing

  • Test XSS payloads are blocked
  • Test legitimate diagrams still render
  • Verify no console errors with strict mode

References

External security review findings (2026-02-02)

## Problem MermaidViewer component uses securityLevel: "loose" and directly injects SVG via innerHTML, creating a direct XSS vector if diagrams are user-controlled. ## Location apps/web/components/MermaidViewer.tsx - Line 34: securityLevel: "loose" - Line 42: innerHTML injection - Line 53: dangerouslySetInnerHTML ```typescript mermaid.initialize({ securityLevel: "loose", // ❌ Allows script execution in SVG // ... }); element.innerHTML = svg; // ❌ Direct HTML injection ``` ## Attack Vector If users can provide or modify Mermaid diagrams: ``` graph TD A[Normal Node] -->|<img src=x onerror=alert(document.cookie)>| B ``` ## Impact - **CRITICAL**: Full XSS if diagrams are user-controlled - Session hijacking via cookie theft - Account takeover - Malicious code execution in user browser - Blocks production deployment ## Questions 1. Are Mermaid diagrams user-controlled or admin-only? 2. Is diagram content validated/sanitized server-side? ## Acceptance Criteria - [ ] Change securityLevel to "strict" - [ ] Sanitize SVG output before injection - [ ] Use DOMPurify for HTML sanitization - [ ] Add CSP headers to prevent inline scripts - [ ] Validate diagram syntax server-side - [ ] Add tests for XSS payloads - [ ] Document safe diagram usage ## Implementation ```typescript import DOMPurify from 'dompurify'; mermaid.initialize({ securityLevel: "strict", // NEW: Block script execution // ... }); const { svg } = await mermaid.render('id', content); const sanitized = DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true, svgFilters: true }, FORBID_TAGS: ['script', 'iframe'], FORBID_ATTR: ['onerror', 'onload'] }); element.innerHTML = sanitized; ``` ## Testing - [ ] Test XSS payloads are blocked - [ ] Test legitimate diagrams still render - [ ] Verify no console errors with strict mode ## References External security review findings (2026-02-02)
jason.woltje added this to the M4.2-Infrastructure (0.0.4) milestone 2026-02-02 17:25:03 +00:00
jason.woltje added the securitywebp0 labels 2026-02-02 17:25:03 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaic/stack#190