VYPR
High severityNVD Advisory· Published Dec 30, 2021· Updated Aug 4, 2024

Incorrect sanitisation function leads to `XSS`

CVE-2021-43861

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.

PackageAffected versionsPatched versions
mermaidnpm
< 8.13.88.13.8

Affected products

2
  • ghsa-coords
    Range: < 8.13.8
  • mermaid-js/mermaidv5
    Range: < 8.13.8

Patches

1
066b7a0d0bda

Merge pull request from GHSA-p3rp-vmj9-gv6v

https://github.com/mermaid-js/mermaidKnut SveidqvistDec 29, 2021via ghsa
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, '&amp;')
    -    .replace(/</g, '&lt;')
    -    .replace(/javascript:/g, '');
    -};
    +// const sanitizeUrl = function (s) {
    +//   return s
    +//     .replace(/&/g, '&amp;')
    +//     .replace(/</g, '&lt;')
    +//     .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

News mentions

0

No linked articles in our index yet.