| type | skill |
| lifecycle | stable |
| inheritance | inheritable |
| name | markdown-sanitization-chain |
| description | Markdown sanitization order matters — marked.js then DOMPurify then Mermaid to prevent XSS |
| tier | standard |
| applyTo | **/*markdown*,**/*sanitization*,**/*chain* |
| currency | 2026-04-30T00:00:00.000Z |
| lastReviewed | 2026-04-30T00:00:00.000Z |
Markdown Sanitization Chain
Category: Security
Time Saved: 2-4 hours debugging XSS vulnerabilities
Battle-tested: Yes — production incident
The Problem
You're rendering user-supplied markdown with a diagram library. The app works great until someone submits markdown containing malicious scripts that bypass your rendering.
Why It Happens
Markdown renderers (marked.js, markdown-it) convert markdown to HTML but don't sanitize it. Diagram renderers (Mermaid, PlantUML) execute after sanitizers run, potentially introducing new attack vectors. The order of operations matters critically.
The Rule
Always: marked.js → DOMPurify → Mermaid (post-render)
1. Parse markdown to HTML (marked.js)
2. Sanitize HTML (DOMPurify)
3. Render diagrams on sanitized DOM (Mermaid.run())
Never skip the sanitizer even if content is "trusted."
Implementation
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import mermaid from 'mermaid';
async function renderMarkdown(content, container) {
const rawHtml = marked.parse(content);
const cleanHtml = DOMPurify.sanitize(rawHtml, {
ADD_TAGS: ['mermaid'],
});
container.innerHTML = cleanHtml;
await mermaid.run({ : container.() });
}