Incorrect sanitisation function leads to `XSS`
Description
Mermaid is a Javascript based diagramming and charting tool that uses Markdown-inspired text definitions and a renderer to create and modify complex diagrams. Prior to version 8.13.8, malicious diagrams can run javascript code at diagram readers' machines. Users should upgrade to version 8.13.8 to receive a patch. There are no known workarounds aside from upgrading.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Mermaid versions before 8.13.8 contain an XSS vulnerability allowing malicious diagrams to execute arbitrary JavaScript in viewers' browsers.
Vulnerability
Mermaid is a JavaScript diagramming tool. Prior to version 8.13.8, the securityLevel configuration could be set to 'loose', which disabled sanitization of diagram inputs, leading to a stored XSS vulnerability [1][4]. An attacker could craft a diagram containing embedded JavaScript code that would execute when the diagram is rendered.
Exploitation
An attacker creates a malicious diagram definition (e.g., using the state or flowchart diagram types) that includes JavaScript payloads. When a user views the diagram in an application using Mermaid (without proper server-side sanitization), the script executes in the context of the user's browser. No authentication or special privileges are required beyond the ability to provide diagram content.
Impact
Successful exploitation allows arbitrary JavaScript execution in the browser of any user viewing the malicious diagram. This can lead to session hijacking, data theft, or other client-side attacks. The attacker gains the same privileges as the victim user within the context of the application.
Mitigation
Users should upgrade to Mermaid version 8.13.8 or later, which enforces proper sanitization and defaults securityLevel to 'strict' [1][3][4]. No known workarounds exist; upgrading is the only reliable fix.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
mermaidnpm | < 8.13.8 | 8.13.8 |
Affected products
2- mermaid-js/mermaidv5Range: < 8.13.8
Patches
1066b7a0d0bdaMerge pull request from GHSA-p3rp-vmj9-gv6v
8 files changed · +272 −14
cypress/platform/xss16.html+106 −0 added@@ -0,0 +1,106 @@ +<html> + <head> + <link + href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" + rel="stylesheet" + /> + <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> + <link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet"> + <style> + body { + /* background: rgb(221, 208, 208); */ + /* background:#333; */ + font-family: 'Arial'; + /* font-size: 18px !important; */ + } + h1 { color: grey;} + .mermaid2 { + display: none; + } + .mermaid svg { + /* font-size: 18px !important; */ + } + .malware { + position: fixed; + bottom:0; + left:0; + right:0; + height: 150px; + background: red; + color: black; + display: flex; + display: flex; + justify-content: center; + align-items: center; + font-family: monospace; + font-size: 72px; + } + </style> + </head> + <body> + <div>Security check</div> + <div class="flex"> + <div id="diagram" class="mermaid"></div> + <div id="res" class=""></div> + <script src="./mermaid.js"></script> + <script> + mermaid.parseError = function (err, hash) { + // console.error('Mermaid error: ', err); + }; + mermaid.initialize({ + theme: 'forest', + arrowMarkerAbsolute: true, + // themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}', + logLevel: 0, + state: { + defaultRenderer: 'dagre-d3', + }, + flowchart: { + // defaultRenderer: 'dagre-wrapper', + nodeSpacing: 10, + curve: 'cardinal', + htmlLabels: true, + }, + htmlLabels: true, + // gantt: { axisFormat: '%m/%d/%Y' }, + sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false }, + // sequenceDiagram: { actorMargin: 300 } // deprecated + // fontFamily: '"times", sans-serif', + // fontFamily: 'courier', + fontSize: 18, + curve: 'basis', + securityLevel: 'loose', + startOnLoad: false, + secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'], + // themeVariables: {relationLabelColor: 'red'} + }); + function callback() { + alert('It worked'); +} + function xssAttack() { + const div = document.createElement('div'); + div.id = 'the-malware'; + div.className = 'malware'; + div.innerHTML = 'XSS Succeeded'; + document.getElementsByTagName('body')[0].appendChild(div); + throw new Error('XSS Succeded'); + } + + var diagram = `sequenceDiagram + participant Alice + links Alice: { "Click me!" : "javasjavascript:cript:alert('goose')" }`; + +// // var diagram = "stateDiagram-v2\n"; +// // diagram += "<img/src='1'/onerror" +// diagram += '//via.placeholder.com/64\' width=64 />"]'; +// console.log(diagram); +// document.querySelector('#diagram').innerHTML = diagram; +mermaid.render('diagram', diagram, (res) => { + console.log(res); + document.querySelector('#res').innerHTML = res; +}); + </script> + </body> +</html> +
cypress/platform/xss17.html+106 −0 added@@ -0,0 +1,106 @@ +<html> + <head> + <link + href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" + rel="stylesheet" + /> + <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> + <link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet"> + <style> + body { + /* background: rgb(221, 208, 208); */ + /* background:#333; */ + font-family: 'Arial'; + /* font-size: 18px !important; */ + } + h1 { color: grey;} + .mermaid2 { + display: none; + } + .mermaid svg { + /* font-size: 18px !important; */ + } + .malware { + position: fixed; + bottom:0; + left:0; + right:0; + height: 150px; + background: red; + color: black; + display: flex; + display: flex; + justify-content: center; + align-items: center; + font-family: monospace; + font-size: 72px; + } + </style> + </head> + <body> + <div>Security check</div> + <div class="flex"> + <div id="diagram" class="mermaid"></div> + <div id="res" class=""></div> + <script src="./mermaid.js"></script> + <script> + mermaid.parseError = function (err, hash) { + // console.error('Mermaid error: ', err); + }; + mermaid.initialize({ + theme: 'forest', + arrowMarkerAbsolute: true, + // themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}', + logLevel: 0, + state: { + defaultRenderer: 'dagre-d3', + }, + flowchart: { + // defaultRenderer: 'dagre-wrapper', + nodeSpacing: 10, + curve: 'cardinal', + htmlLabels: true, + }, + htmlLabels: true, + // gantt: { axisFormat: '%m/%d/%Y' }, + sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false }, + // sequenceDiagram: { actorMargin: 300 } // deprecated + // fontFamily: '"times", sans-serif', + // fontFamily: 'courier', + fontSize: 18, + curve: 'basis', + securityLevel: 'loose', + startOnLoad: false, + secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'], + // themeVariables: {relationLabelColor: 'red'} + }); + function callback() { + alert('It worked'); +} + function xssAttack() { + const div = document.createElement('div'); + div.id = 'the-malware'; + div.className = 'malware'; + div.innerHTML = 'XSS Succeeded'; + document.getElementsByTagName('body')[0].appendChild(div); + throw new Error('XSS Succeded'); + } + + var diagram = `sequenceDiagram + participant Alice + link Alice: Click Me!@javasjavascript:cript:alert("goose")`; + +// // var diagram = "stateDiagram-v2\n"; +// // diagram += "<img/src='1'/onerror" +// diagram += '//via.placeholder.com/64\' width=64 />"]'; +// console.log(diagram); +// document.querySelector('#diagram').innerHTML = diagram; +mermaid.render('diagram', diagram, (res) => { + console.log(res); + document.querySelector('#res').innerHTML = res; +}); + </script> + </body> +</html> +
docs/security.md+17 −0 added@@ -0,0 +1,17 @@ +# Security +The Mermaid team takes the security of Mermaid and the applications that use Mermaid seriously. This page describes how to report any vulnerabilities you may find, and lists best practices to minimize the risk of introducing a vulnerability. + +## Reporting vulnerabilities +To report a vulnerability, please e-mail security@mermaid.live with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue. + +We aim to reply within three working days, probably much sooner. + +You should expect a close collaboration as we work to resolve the issue you have reported. Please reach out to security@mermaid.live again if you do not receive prompt attention and regular updates. + +You may also reach out to the team via our public Slack chat channels; however, please make sure to e-mail security@mernaid.live when reporting an issue, and avoid revealing information about vulnerabilities in public as that could that could put users at risk. + +## Best practices + +Keep current with the latest Mermaid releases. We regularly update Mermaid, and these updates may fix security defects discovered in previous versions. Check the Mermaid release notes for security-related updates. + +Keep your application’s dependencies up to date. Make sure you upgrade your package dependencies to keep the dependencies up to date. Avoid pinning to specific versions for your dependencies and, if you do, make sure you check periodically to see if your dependencies have had security updates, and update the pin accordingly.
docs/_sidebar.md+6 −5 modified@@ -1,10 +1,10 @@ -- 📔 Introduction +- 📔 Introduction - [About Mermaid](README.md) - [Deployment](n00b-gettingStarted.md) - [Syntax and Configuration](n00b-syntaxReference.md) -- 📊 Diagram Syntax +- 📊 Diagram Syntax - [Flowchart](flowchart.md) - [Sequence diagram](sequenceDiagram.md) - [Class Diagram](classDiagram.md) @@ -16,7 +16,7 @@ - [Requirement Diagram](requirementDiagram.md) - [Other Examples](examples.md) -- ⚙️ Deployment and Configuration +- ⚙️ Deployment and Configuration - [Tutorials](Tutorials.md) - [API-Usage](usage.md) @@ -26,12 +26,13 @@ - [Mermaid CLI](mermaidCLI.md) - [Advanced usage](n00b-advanced.md) -- 📚 Misc +- 📚 Misc - [Use-Cases and Integrations](integrations.md) - [FAQ](faq.md) -- 🙌 Contributions and Community +- 🙌 Contributions and Community - [Overview for Beginners](n00b-overview.md) - [Development and Contribution ](development.md) - [Changelog](CHANGELOG.md) - [Adding Diagrams ](newDiagram.md) + - [Security ](security.md)
package.json+1 −1 modified@@ -1,6 +1,6 @@ { "name": "mermaid", - "version": "8.13.6", + "version": "8.13.8", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "main": "dist/mermaid.core.js", "module": "dist/mermaid.esm.min.mjs",
src/diagrams/common/common.spec.js+13 −1 modified@@ -1,4 +1,4 @@ -import { removeScript, removeEscapes } from './common'; +import { sanitizeText, removeScript, removeEscapes } from './common'; describe('when securityLevel is antiscript, all script must be removed', function () { it('should remove all script block, script inline.', function () { @@ -69,3 +69,15 @@ describe('remove escape code in text', function () { expect(result).toEqual('script:'); }); }); + +describe('Sanitize text', function () { + it('should remove script tag', function () { + const maliciousStr = 'javajavascript:script:alert(1)'; + const result = sanitizeText(maliciousStr, { + securityLevel: 'strict', + flowchart: { htmlLabels: true }, + }); + console.log('result', result); + expect(result).not.toContain('javascript:alert(1)'); + }); +});
src/diagrams/sequence/svgDraw.js+8 −6 modified@@ -1,5 +1,6 @@ import common from '../common/common'; import { addFunction } from '../../interactionDb'; +import { sanitizeUrl } from '@braintree/sanitize-url'; export const drawRect = function (elem, rectData) { const rectElem = elem.append('rect'); @@ -19,12 +20,12 @@ export const drawRect = function (elem, rectData) { return rectElem; }; -const sanitizeUrl = function (s) { - return s - .replace(/&/g, '&') - .replace(/</g, '<') - .replace(/javascript:/g, ''); -}; +// const sanitizeUrl = function (s) { +// return s +// .replace(/&/g, '&') +// .replace(/</g, '<') +// .replace(/javascript:/g, ''); +// }; const addPopupInteraction = (id, actorCnt) => { addFunction(() => { @@ -1055,4 +1056,5 @@ export default { popupMenu, popdownMenu, fixLifeLineHeights, + sanitizeUrl, };
src/diagrams/sequence/svgDraw.spec.js+15 −1 modified@@ -1,4 +1,4 @@ -const svgDraw = require('./svgDraw'); +const svgDraw = require('./svgDraw').default; const { MockD3 } = require('d3'); describe('svgDraw', function () { @@ -124,4 +124,18 @@ describe('svgDraw', function () { expect(rect.lower).toHaveBeenCalled(); }); }); + describe('sanitizeUrl', function () { + it('it should sanitize malicious urls', function () { + const maliciousStr = 'javascript:script:alert(1)'; + const result = svgDraw.sanitizeUrl(maliciousStr); + console.log('result', result); + expect(result).not.toContain('javascript:alert(1)'); + }); + it('it should not sanitize non dangerous urls', function () { + const maliciousStr = 'javajavascript:script:alert(1)'; + const result = svgDraw.sanitizeUrl(maliciousStr); + console.log('result', result); + expect(result).not.toContain('javascript:alert(1)'); + }); + }); });
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-p3rp-vmj9-gv6vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-43861ghsaADVISORY
- github.com/mermaid-js/mermaid/commit/066b7a0d0bda274d94a2f2d21e4323dab5776d83ghsax_refsource_MISCWEB
- github.com/mermaid-js/mermaid/releases/tag/8.13.8ghsax_refsource_MISCWEB
- github.com/mermaid-js/mermaid/security/advisories/GHSA-p3rp-vmj9-gv6vghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.