How the Mermaid securityLevel Configuration Impacts Diagram Rendering and XSS Protection

The securityLevel configuration in Mermaid controls how much HTML and JavaScript is allowed in diagram source text, with options ranging from 'strict' (full sanitization) to 'sandbox' (isolated iframe rendering), directly determining the XSS attack surface of your application.

The mermaid-js/mermaid library parses diagram definitions and injects the resulting SVG or HTML into web pages, making the securityLevel configuration critical for preventing cross-site scripting (XSS) attacks. This setting determines whether scripts execute, HTML tags render, or content remains isolated in sandboxed iframes. Understanding how each level impacts the rendering pipeline helps you balance visual flexibility against security requirements.

The Four securityLevel Options Explained

Mermaid’s security model is defined in src/config.type.ts and enforced throughout the rendering pipeline in src/mermaidAPI.ts. Each level triggers distinct sanitization behaviors in src/diagrams/common/common.ts and src/utils.ts.

Strict (Default) – Maximum Protection

When securityLevel is set to 'strict', Mermaid encodes all HTML tags and disables interactive features. In src/diagrams/common/common.ts, the sanitizeMore helper (lines 70-78) breaks markup into placeholders and escapes <, >, and = characters to neutralize any HTML injection attempts.

Additionally, src/mermaidAPI.ts runs DOMPurify sanitization on the generated SVG unless isLooseSecurityLevel is true. Click handlers defined via click directives in diagram syntax are completely disabled, ensuring no JavaScript execution occurs.

Antiscript – HTML Without JavaScript

The 'antiscript' level allows rich HTML markup such as <b>, <div>, or <span> tags while stripping all <script> elements. In sanitizeMore, when the level is detected as 'antiscript', the code calls removeScript which executes DOMPurify.sanitize(txt) specifically targeting script tags (lines 70-73).

Interactive features remain enabled, meaning click callbacks and hyperlinks function normally. However, the remaining HTML can still affect page styling or layout, requiring trust in the diagram source.

Loose – Full HTML and JavaScript Support

Setting securityLevel to 'loose' provides maximum flexibility but opens significant security vulnerabilities. In src/mermaidAPI.ts (lines 52-59), the DOMPurify step is entirely skipped when isLooseSecurityLevel evaluates to true.

Furthermore, src/utils.ts (lines 55-58) shows that the formatUrl helper bypasses URL sanitization for 'loose' mode, allowing javascript: protocol links and other potentially dangerous URLs. This enables embedding iframes, custom CSS, and inline event handlers, but exposes applications to full XSS attacks if malicious actors control the diagram text.

Sandbox – Isolated Iframe Rendering

The 'sandbox' option renders diagrams inside a sandboxed iframe to isolate potentially malicious content. In src/mermaidAPI.ts (lines 55-60), the code creates a sandboxedIframe when config.securityLevel === SECURITY_LVL_SANDBOX. The generated SVG is injected into this isolated document rather than the parent page.

Scripts execute within the iframe’s restricted context only, preventing access to the host page’s DOM, cookies, or localStorage. However, interactive features requiring parent context—such as click callbacks that trigger main page navigation or pop-ups—cease to function.

Technical Implementation in the Source Code

Configuration Definitions

The TypeScript type definition in src/config.type.ts declares the valid options:

securityLevel?: 'strict' | 'loose' | 'antiscript' | 'sandbox'

The YAML schema in src/schemas/config.schema.yaml documents each enum value’s semantics and default behavior, which feeds into the official documentation at src/docs/config/usage.md.

The Sanitization Pipeline

The core sanitization logic resides in src/diagrams/common/common.ts. The sanitizeMore function implements level-specific processing:

  1. For 'strict': Removes <script> tags, then replaces <, >, and = with placeholders to escape remaining HTML
  2. For 'antiscript': Strips scripts via DOMPurify but preserves other HTML markup
  3. For 'loose': Performs no additional escaping beyond baseline processing

URL Validation Bypass

In src/utils.ts, the formatUrl function demonstrates the security level’s impact on hyperlink handling:

// Line 55-58: Conditional bypass for 'loose' mode
if (securityLevel === 'loose') {
  return url;
}

Under 'strict', 'antiscript', or 'sandbox' modes, javascript: URLs and other dangerous protocols return undefined, effectively neutralizing them.

Level Attack Surface Risk with Malicious Input Recommended Use Case
Strict Minimal None—scripts removed, HTML escaped, clicks disabled Public-facing sites, untrusted user content
Antiscript Moderate Layout/style manipulation possible, but no script execution Internal documentation requiring rich formatting
Loose High Full XSS capability including script injection and cookie theft Trusted environments only—internal tools with vetted authors
Sandbox Low (isolated) Scripts run in iframe only; cannot access parent page Rendering untrusted diagrams where visual fidelity matters

Practical Configuration Examples

Global Initialization

Configure the security level when initializing Mermaid:

// Strict default—safest for public sites
mermaid.initialize({
  securityLevel: 'strict',
});

// Allow HTML formatting but block scripts
mermaid.initialize({
  securityLevel: 'antiscript',
});

// Maximum flexibility—use with caution
mermaid.initialize({
  securityLevel: 'loose',
});

// Isolated rendering for untrusted content
mermaid.initialize({
  securityLevel: 'sandbox',
});

Per-Render Override

Override the global setting for individual diagram renders:

mermaid.render(
  'diagram-id',
  'graph TD; A-->B; click B "https://example.com"',
  (svgCode) => {
    document.getElementById('output').innerHTML = svgCode;
  },
  null,
  { securityLevel: 'sandbox' } // Merges with global config via processAndSetConfigs
);

URL Handling Comparison

Demonstrating formatUrl behavior across levels:

// Returns undefined—sanitized
formatUrl('javascript:alert(1)', { securityLevel: 'strict' });

// Returns 'javascript:alert(1)'—dangerous
formatUrl('javascript:alert(1)', { securityLevel: 'loose' });

Summary

  • 'strict' (default) encodes all HTML and disables click handling, providing maximum XSS protection via DOMPurify and character escaping in src/diagrams/common/common.ts.
  • 'antiscript' permits HTML markup while stripping <script> tags, balancing formatting flexibility with script security.
  • 'loose' bypasses DOMPurify in src/mermaidAPI.ts and URL sanitization in src/utils.ts, enabling full HTML/JS support but requiring complete trust in diagram sources.
  • 'sandbox' isolates rendering in a sandboxed iframe created in src/mermaidAPI.ts, preventing parent page access while sacrificing some interactive features.

Frequently Asked Questions

What is the difference between 'strict' and 'antiscript' security levels?

The 'strict' level encodes all HTML characters (converting < and > to entities) and disables click interactions entirely, while 'antiscript' allows HTML tags like <b> or <div> to render but specifically removes <script> elements using DOMPurify. Choose 'strict' when you want zero HTML rendering capability, or 'antiscript' when you need formatting but must prevent script execution.

When should I use the 'sandbox' security level?

Use 'sandbox' when rendering diagrams from untrusted sources but where visual fidelity is important. According to the implementation in src/mermaidAPI.ts, this level creates an isolated iframe that prevents scripts from accessing the parent page’s DOM or cookies, though it disables click callbacks that interact with the main window.

Can I override securityLevel for individual diagrams?

Yes. While you can set a global default via mermaid.initialize(), the render function accepts an options parameter that merges with global configuration through processAndSetConfigs. Pass { securityLevel: 'sandbox' } as the fifth argument to mermaid.render() to override the global setting for that specific diagram.

Why does 'loose' mode pose an XSS risk?

In 'loose' mode, Mermaid skips the DOMPurify sanitization step in src/mermaidAPI.ts (lines 52-59) and disables URL validation in src/utils.ts (lines 55-58). This allows attackers to inject <script> tags, javascript: URLs, or inline event handlers into diagram text, enabling cookie theft, session hijacking, or arbitrary code execution in the user’s browser. Only use 'loose' when you completely control and trust the diagram source.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →