markdown-sanitization-chain
Skill0 starsChanged 4 months agoMITView on GitHub
Render user-supplied markdown safely — marked.js → DOMPurify → Mermaid (order matters; skipping the sanitizer is XSS)
In this repository38 other files it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
- agent-creatorSkill
- agent-reviewSkill
- ai-memory-setupSkill
- alex-banner-generationSkill
- anti-hallucinationSkill
- brain-auditSkill
- browser-toolsSkill
- code-reviewSkill
- creative-writingSkill
- critical-thinkingSkill
- deep-reviewSkill
- doc-hygieneSkill
- docx-to-mdSkill
- git-workflowSkill
- greeting-checkinSkill
- html-to-mdSkill
- humanizerSkill
- instruction-creatorSkill
- instruction-reviewSkill
- lint-clean-markdownSkill
- markdown-mermaidSkill
- md-to-emlSkill
- md-to-htmlSkill
- md-to-txtSkill
- md-to-wordSkill
- meditationSkill
- planSkill
- problem-framing-auditSkill
- prompt-creatorSkill
- prompt-reviewSkill
- security-and-hardeningSkill
- skill-creatorSkill
- skill-reviewSkill
- spikeSkill
- status-reportingSkill
- systematic-debuggingSkill
- test-driven-developmentSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 33
Never skip the sanitizer even if content is "trusted." Trust gets revoked when the threat model changes.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Markdown Sanitization Chain
Battle-tested via production XSS incident. The order of markdown → sanitize → diagram render is non-negotiable when content comes from users.
When to Use
- An app renders markdown supplied by users (comments, docs UI, embedded editors)
- You're about to call
innerHTMLwith markdown-derived HTML - Mermaid or another diagram renderer runs in the browser
- Security review on a markdown-rendering surface
Why It Matters
Markdown renderers (marked.js, markdown-it) convert markdown to HTML but do not sanitize it. Diagram renderers (Mermaid, PlantUML) execute after sanitizers run, which can re-introduce attack vectors. Order matters critically.
The Rule
Always: marked.js → DOMPurify → Mermaid (post-render).
1. Parse markdown to HTML (marked.js)
2. Sanitize HTML (DOMPurify)
3. Insert sanitized HTML into DOM
4. Render diagrams on the now-sanitized DOM (Mermaid.run())Never skip the sanitizer even if content is "trusted." Trust gets revoked when the threat model changes; the chain stays.
Implementation
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import mermaid from 'mermaid';
async function renderMarkdown(content, container) {
// Step 1: parse markdown to HTML
const rawHtml = marked.parse(content);
// Step 2: sanitize BEFORE inserting into the DOM
const cleanHtml = DOMPurify.sanitize(rawHtml, {
ADD_TAGS: ['mermaid'], // allow mermaid tags through
});
// Step 3: insert sanitized HTML
container.innerHTML = cleanHtml;
// Step 4: render diagrams on sanitized DOM
await mermaid.run({ nodes: container.querySelectorAll('.mermaid') });
}Common Mistakes
| Mistake | Consequence |
| Skip DOMPurify ("it's internal content") | XSS from any content source |
| Sanitize after Mermaid renders | Mermaid-injected scripts execute |
Use innerHTML without sanitization anywhere | Classic XSS |
| Trust localStorage / URL params | User-controlled XSS payloads |
DOMPurify Configuration
const config = {
ADD_TAGS: ['mermaid'], // preserve diagram tags
ADD_ATTR: ['onclick'], // only if absolutely needed
FORBID_TAGS: ['style', 'script'], // explicit blocklist
FORBID_ATTR: ['onerror', 'onload'],
};Verification Checklist
- Markdown parser runs first
- DOMPurify runs before DOM insertion
- Diagram renderer runs after sanitization
- No raw
innerHTMLwithout sanitization anywhere in the surface - Tested with
<img src=x onerror=alert(1)>payload
Related
- markdown-mermaid — markdown + Mermaid style guide
- markdown-mermaid § Mode Fragility — silent render failures
- lint-clean-markdown — author-side hygiene
Would Revise If
Revisit this skill by 2026-08-26 (90 days) or sooner if any of the following fires: DOMPurify or marked.js publishes a breaking change that invalidates the documented chain order; a real XSS payload bypasses the chain in production use; or Mermaid changes its render-time HTML interface in a way that makes the post-sanitization step unsafe.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

