CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-26226
5.30.07%

Mermaid's Siren Song: SVG Injection in beautiful-mermaid

Alon Barad
Alon Barad
Software Engineer

Feb 14, 2026·6 min read·6 visits

PoC Available

Executive Summary (TL;DR)

The `beautiful-mermaid` library, used to render Mermaid diagrams as SVGs, failed to sanitize user-supplied style attributes. Attackers can inject malicious payloads into `fill` or `stroke` properties, breaking out of the SVG XML structure to execute XSS. Fixed in version 0.1.3.

A critical flaw in the `beautiful-mermaid` library allows attackers to weaponize flowcharts and diagrams. By injecting malicious CSS strings into style definitions, adversaries can break out of SVG attributes and execute arbitrary JavaScript. This vulnerability transforms a harmless documentation tool into a vehicle for Cross-Site Scripting (XSS), affecting all versions prior to 0.1.3.

The Hook: When Flowcharts Fight Back

Mermaid diagrams are the darling of the developer documentation world. They turn cryptic text into beautiful flowcharts, sequence diagrams, and Gantt charts. The beautiful-mermaid library exists to take that text and render it into high-fidelity SVGs, perfect for embedding in websites or documentation portals. It promises "beautiful" rendering, but under the hood, it was doing something ugly: trusting user input blindly.

Imagine you are building a documentation platform. You allow users to submit Mermaid graphs to explain their microservices architecture. You render these server-side or client-side using beautiful-mermaid. It seems harmless enough—after all, it's just boxes and arrows, right? Wrong. In the security world, if you are building HTML or XML strings by mashing variables together without sanitization, you aren't building a renderer; you're building a vulnerability.

CVE-2026-26226 is the classic tale of convenience over security. The library treated style attributes like color, fill, and stroke as pure aesthetic choices, never expecting that someone might try to paint a box with a shade of " onload="alert(1). This oversight turns every diagram into a potential landmine for Cross-Site Scripting (XSS).

The Flaw: String Concatenation Doom

The root cause of this vulnerability is a fundamental misunderstanding of how to safely construct XML (and by extension, SVG) documents. There are two ways to build a DOM element: you can use a DOM API (like document.createElement), which naturally handles encoding, or you can build a massive string and hope for the best. beautiful-mermaid chose the latter.

In src/renderer.ts, the library parses the Mermaid syntax to extract node styles. If a user defines a specific style for a node—say, making it red—the library captures that string. The fatal error occurred when the library took those captured strings and interpolated them directly into a template literal to form the SVG output.

It didn't care what was in the string. It didn't look for quotes, angle brackets, or escape characters. It just pasted the value directly into the fill="..." attribute of the SVG string. This is the XML equivalent of SQL injection. The browser parser doesn't know that the attacker's double-quote meant "end of the attribute value." It just sees a closed attribute and a new, malicious instruction following immediately after.

The Code: The Smoking Gun

Let's look at the crime scene in src/renderer.ts. The code was responsible for taking style properties and applying them to the SVG elements. Here is the vulnerable logic before the patch:

// Vulnerable: Direct interpolation of user input
const fill = inlineStyle?.fill ?? 'var(--_node-fill)'
const stroke = inlineStyle?.stroke ?? 'var(--_node-stroke)'
const sw = inlineStyle?.['stroke-width'] ?? String(STROKE_WIDTHS.innerBox)
 
// Later, this is used in a template literal:
// return `<rect fill="${fill}" stroke="${stroke}" ... />`

If inlineStyle.fill is controlled by the user, they control the raw HTML output. The fix, introduced in version 0.1.3, is a textbook application of output encoding. The developers wrapped the tainted variables in an escapeXml function before interpolation:

// Fixed: Sanitizing the input before interpolation
const fill = escapeXml(inlineStyle?.fill ?? 'var(--_node-fill)')
const stroke = escapeXml(inlineStyle?.stroke ?? 'var(--_node-stroke)')
const sw = escapeXml(inlineStyle?.['stroke-width'] ?? String(STROKE_WIDTHS.innerBox))

This simple change ensures that a double quote " becomes &quot;, preventing the attacker from breaking out of the attribute context. It converts a weaponized string back into a harmless (albeit likely invalid) color value.

The Exploit: Breaking the Fourth Wall

Exploiting this is trivially easy once you understand the underlying string concatenation. We don't need complex memory corruption or race conditions; we just need to balance some quotes. The attack vector typically involves the style directive in Mermaid syntax, which allows applying CSS to specific nodes.

Scenario 1: The Event Handler Injection We want to inject onmouseover="alert(1)". The target code looks roughly like <rect fill="${USER_INPUT}" ... />. If we set our fill color to red" onmouseover="alert(1), the rendered SVG becomes:

<rect fill="red" onmouseover="alert(1)" ... />

The browser sees a red rectangle that executes JavaScript when you hover over it.

The Payload:

Scenario 2: The Element Injection If we want to be louder, we can close the rect tag entirely and start a new script tag. We set the fill to red"/><script>alert(origin)</script><rect fill="x.

The Payload:

This results in a valid SVG structure that includes an inline script block, executing immediately upon rendering in many contexts (though modern CSPs might block inline scripts, the event handler vector remains potent).

The Impact: Why Should You Care?

While the CVSS score is a moderate 5.3 (primarily because user interaction is usually required to view the diagram, and the scope is limited to the browser), the practical impact can be severe depending on the implementation. This is a classic stored XSS vector.

If a developer uses beautiful-mermaid to render user-generated content (like comments, wiki pages, or issue trackers) server-side and serves the resulting SVG, they are serving weaponized content. SVGs are powerful; they are essentially XML documents that can contain scripts, external object references, and more.

Successful exploitation means an attacker can:

  1. Steal Session Cookies: Hijacking the victim's account.
  2. Perform Actions on Behalf of the User: forcing the victim to delete data or change passwords.
  3. Deface the Application: Injecting fake login forms or misleading content.

Since this library is likely used in documentation tools or dashboards, the victims are often privileged users (developers, admins) viewing internal diagrams, making the target value high.

The Fix: Mitigation & Remediation

The remediation is straightforward: stop trusting strings. The maintainers of beautiful-mermaid released version 0.1.3 which includes the necessary escaping logic. If you are using this library, you need to upgrade immediately.

> [!TIP] > Action Item: Run npm list beautiful-mermaid or bun pm ls to check your version. If it is < 0.1.3, update it.

If you cannot update for some reason (perhaps you enjoy living on the edge), you must implement a strict Content Security Policy (CSP). A CSP that forbids unsafe-inline scripts and restricts object-src can mitigate the impact of the XSS, preventing the execution of the injected JavaScript even if the HTML is malformed.

However, the only true fix is the patch. Sanitization is hard; don't try to write your own regex to strip quotes. Use the vendor's fix which properly encodes XML entities.

Official Patches

lukilabsPull Request #8: fix potential xss vulnerability
lukilabsRelease v0.1.3 changelog

Fix Analysis (1)

Technical Appendix

CVSS Score
5.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N
EPSS Probability
0.07%
Top 78% most exploited

Affected Systems

beautiful-mermaid npm package

Affected Versions Detail

Product
Affected Versions
Fixed Version
beautiful-mermaid
lukilabs
< 0.1.30.1.3
AttributeDetail
CWECWE-79 (XSS)
CVSS v4.05.3 (Medium)
Attack VectorNetwork (Payload delivery)
Privileges RequiredNone
User InteractionPassive (Viewing diagram)
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-79
Cross-site Scripting

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Known Exploits & Detection

GitHubPoC payload included in the fix pull request showing attribute breakout.

Vulnerability Timeline

Fix commit merged
2026-01-29
Vulnerability publicly disclosed
2026-02-13
CVE-2026-26226 Published
2026-02-13

References & Sources

  • [1]VulnCheck Advisory
  • [2]ProjectDiscovery Neo Analysis

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.