Repository: mermaid-js/mermaid
Pull request #6566: fix: escape backslashes before parentheses in URL replacement logic
Author: login: arpitjain099
Created: 2025-05-05T12:26:06Z | Merged: 2025-05-20T06:53:41Z

## :bookmark_tabs: Summary

Fixes unsafe or incomplete string escaping in URL manipulation logic by ensuring backslashes are escaped before other characters like parentheses.

Resolves #6565 

PS. This is the proof why this PR fixes the issues when running CodeQL on my forked repo.
<img width="720" alt="image" src="https://github.com/user-attachments/assets/af16e2c1-82fa-4785-a110-65c78807b2c4" />

---

## :straight_ruler: Design Decisions

- The bug was surfaced via CodeQL and relates to escaping parentheses in a string without first escaping existing backslashes.
- To ensure correctness and avoid malformed escape sequences, the fix applies `.replace(/\\/g, '\\\\')` before replacing other special characters.
- This avoids issues where characters like `\(` are incorrectly interpreted or break rendering/parsing.
- The change is minimal and scoped to the URL-escaping logic in `edges.js`.

**Example of corrected code:**

```js
url = url.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
```

This order of replacements ensures robust and predictable escaping.

---

### :clipboard: Tasks

Make sure you:

- [x] :book: have read the [contribution guidelines](https://mermaid.js.org/community/contributing.html)
- [ ] :computer: have added necessary unit/e2e tests.
- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://mermaid.js.org/community/contributing.html#update-documentation) is used for all new features.
- [x] :butterfly: If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. Changesets that add features should be `minor` and those that fix bugs should be `patch`. Please prefix changeset messages with `fix:`.


Linked issue #6565: Incomplete String Escaping in URL Handling (Potential Security Vulnerability)
### Description

Certain instances in the codebase use .replace() to escape special characters like ( and ) in URLs, but they fail to escape existing backslashes (\) first. This can lead to malformed escape sequences or introduce security vulnerabilities, especially in downstream parsing or rendering.

Example:
```
url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
```
This does not escape backslashes already present in the string. If a URL contains something like \(, the output becomes ambiguous or unsafe.

### Steps to reproduce

This issue is surfaced through static analysis (e.g., CodeQL), not through typical diagram rendering. However, to conceptually reproduce:

1. Use a diagram that includes a URL or label with a backslash and parentheses, e.g.:

```bash
   graph TD
     A[Click here] --> B((http://example.com/\(test\)))
```

2. Render the diagram using the Live Editor or embedded Mermaid.
3. Inspect the generated output or internal URL transformation logic.
4. Note that backslashes are not properly escaped before parentheses.
5. This results in potentially malformed output like \( instead of \\(, which may affect rendering or security depending on downstream usage.

### Screenshots

Not applicable, but CodeQL flags it with:
"Incomplete string escaping or encoding"

### Code Sample

```text
Problematic line:

url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');

Recommended fix:

url = url.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
```

### Setup

- Mermaid version:
- Browser and Version: [Chrome, Edge, Firefox]

### Suggested Solutions

- Escape backslashes first before other characters
- Consider abstracting the escaping logic into a utility function if this pattern appears frequently.

### Additional Context

_No response_

Diff:
diff --git a/.changeset/yellow-walls-fry.md b/.changeset/yellow-walls-fry.md
new file mode 100644
index 00000000000..75349fd195b
--- /dev/null
+++ b/.changeset/yellow-walls-fry.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Fix incomplete string escaping in URL manipulation logic when `arrowMarkerAbsolute: true` by ensuring all unsafe characters are escaped.
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index ad6b21e2989..b48a197a454 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -69,7 +69,9 @@ describe('Configuration', () => {
           .and('include', 'url(#');
       });
     });
-    it('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => {
+    // This has been broken for a long time, but something about the Cypress environment was
+    // rewriting the URL to be relative, causing the test to incorrectly pass.
+    it.skip('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => {
       renderGraph(
         `graph TD
         A[Christmas] -->|Get money| B(Go shopping)
@@ -112,7 +114,7 @@ describe('Configuration', () => {
           .first()
           .should('have.attr', 'marker-end')
           .should('exist')
-          .and('include', 'url(http://localhost');
+          .and('include', 'url(http\\:\\/\\/localhost');
       });
     });
     it('should not taint the initial configuration when using multiple directives', () => {
diff --git a/packages/mermaid/src/dagre-wrapper/edges.js b/packages/mermaid/src/dagre-wrapper/edges.js
index 5a97e5b63e4..f945caf94b1 100644
--- a/packages/mermaid/src/dagre-wrapper/edges.js
+++ b/packages/mermaid/src/dagre-wrapper/edges.js
@@ -4,7 +4,7 @@ import { createText } from '../rendering-util/createText.js';
 import { line, curveBasis, select } from 'd3';
 import { getConfig } from '../diagram-api/diagramAPI.js';
 import utils from '../utils.js';
-import { evaluate } from '../diagrams/common/common.js';
+import { evaluate, getUrl } from '../diagrams/common/common.js';
 import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js';
 import { getSubGraphTitleMargins } from '../utils/subGraphTitleMargins.js';
 import { addEdgeMarkers } from './edgeMarker.js';
@@ -440,14 +440,7 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
   let url = '';
   // // TODO: Can we load this config only from the rendered graph type?
   if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
-    url =
-      window.location.protocol +
-      '//' +
-      window.location.host +
-      window.location.pathname +
-      window.location.search;
-    url = url.replace(/\(/g, '\\(');
-    url = url.replace(/\)/g, '\\)');
+    url = getUrl(true);
   }
 
   addEdgeMarkers(svgPath, edge, url, id, diagramType);
diff --git a/packages/mermaid/src/diagrams/class/svgDraw.js b/packages/mermaid/src/diagrams/class/svgDraw.js
index 73cf97aeb71..ecea2b1de9c 100644
--- a/packages/mermaid/src/diagrams/class/svgDraw.js
+++ b/packages/mermaid/src/diagrams/class/svgDraw.js
@@ -1,7 +1,7 @@
 import { line, curveBasis } from 'd3';
 import utils from '../../utils.js';
 import { log } from '../../logger.js';
-import { parseGenericTypes } from '../common/common.js';
+import { parseGenericTypes, getUrl } from '../common/common.js';
 
 let edgeCount = 0;
 export const drawEdge = function (elem, path, relation, conf, diagObj) {
@@ -42,14 +42,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
     .attr('class', 'relation');
   let url = '';
   if (conf.arrowMarkerAbsolute) {
-    url =
-      window.location.protocol +
-      '//' +
-      window.location.host +
-      window.location.pathname +
-      window.location.search;
-    url = url.replace(/\(/g, '\\(');
-    url = url.replace(/\)/g, '\\)');
+    url = getUrl(true);
   }
 
   if (relation.relation.lineType == 1) {
diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts
index 14c88a0fc33..00c9b8313d3 100644
--- a/packages/mermaid/src/diagrams/common/common.ts
+++ b/packages/mermaid/src/diagrams/common/common.ts
@@ -149,7 +149,7 @@ const breakToPlaceholder = (s: string): string => {
  * @param useAbsolute - Whether to return the absolute URL or not
  * @returns The current URL
  */
-const getUrl = (useAbsolute: boolean): string => {
+export const getUrl = (useAbsolute: boolean): string => {
   let url = '';
   if (useAbsolute) {
     url =
@@ -158,8 +158,8 @@ const getUrl = (useAbsolute: boolean): string => {
       window.location.host +
       window.location.pathname +
       window.location.search;
-    url = url.replaceAll(/\(/g, '\\(');
-    url = url.replaceAll(/\)/g, '\\)');
+
+    url = CSS.escape(url);
   }
 
   return url;
diff --git a/packages/mermaid/src/diagrams/er/erRenderer.js b/packages/mermaid/src/diagrams/er/erRenderer.js
index 0327bfc9d53..a6f7745aa9e 100644
--- a/packages/mermaid/src/diagrams/er/erRenderer.js
+++ b/packages/mermaid/src/diagrams/er/erRenderer.js
@@ -6,7 +6,7 @@ import { log } from '../../logger.js';
 import utils from '../../utils.js';
 import erMarkers from './erMarkers.js';
 import { configureSvgSize } from '../../setupGraphViewbox.js';
-import { parseGenericTypes } from '../common/common.js';
+import { parseGenericTypes, getUrl } from '../common/common.js';
 import { v5 as uuid5 } from 'uuid';
 
 /** Regex used to remove chars from the entity name so the result can be used in an id */
@@ -451,14 +451,7 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
   // TODO: Understand this better
   let url = '';
   if (conf.arrowMarkerAbsolute) {
-    url =
-      window.location.protocol +
-      '//' +
-      window.location.host +
-      window.location.pathname +
-      window.location.search;
-    url = url.replace(/\(/g, '\\(');
-    url = url.replace(/\)/g, '\\)');
+    url = getUrl(true);
   }
 
   // Decide which start and end markers it needs. It may be possible to be more concise here
diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts
index 84bb15b155c..cfba92b793a 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts
+++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts
@@ -3,6 +3,7 @@ import { select } from 'd3';
 import svgDraw, { drawKatex, ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
 import { log } from '../../logger.js';
 import common, { calculateMathMLDimensions, hasKatex } from '../common/common.js';
+import { getUrl } from '../common/common.js';
 import * as svgDrawCommon from '../common/svgDrawCommon.js';
 import { getConfig } from '../../diagram-api/diagramAPI.js';
 import assignWithDepth from '../../assignWithDepth.js';
@@ -449,14 +450,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
 
   let url = '';
   if (conf.arrowMarkerAbsolute) {
-    url =
-      window.location.protocol +
-      '//' +
-      window.location.host +
-      window.location.pathname +
-      window.location.search;
-    url = url.replace(/\(/g, '\\(');
-    url = url.replace(/\)/g, '\\)');
+    url = getUrl(true);
   }
 
   line.attr('stroke-width', 2);
diff --git a/packages/mermaid/src/diagrams/state/shapes.js b/packages/mermaid/src/diagrams/state/shapes.js
index 419d2a76e67..f9b34506f43 100644
--- a/packages/mermaid/src/diagrams/state/shapes.js
+++ b/packages/mermaid/src/diagrams/state/shapes.js
@@ -1,7 +1,7 @@
 import { line, curveBasis } from 'd3';
 import { StateDB } from './stateDb.js';
 import utils from '../../utils.js';
-import common from '../common/common.js';
+import common, { getUrl } from '../common/common.js';
 import { getConfig } from '../../diagram-api/diagramAPI.js';
 import { log } from '../../logger.js';
 
@@ -444,14 +444,7 @@ export const drawEdge = function (elem, path, relation) {
     .attr('class', 'transition');
   let url = '';
   if (getConfig().state.arrowMarkerAbsolute) {
-    url =
-      window.location.protocol +
-      '//' +
-      window.location.host +
-      window.location.pathname +
-      window.location.search;
-    url = url.replace(/\(/g, '\\(');
-    url = url.replace(/\)/g, '\\)');
+    url = getUrl(true);
   }
 
   svgPath.attr(
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js
index ce4244ff2e1..a97668d5fae 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js
+++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js
@@ -1,5 +1,5 @@
 import { getConfig } from '../../diagram-api/diagramAPI.js';
-import { evaluate } from '../../diagrams/common/common.js';
+import { evaluate, getUrl } from '../../diagrams/common/common.js';
 import { log } from '../../logger.js';
 import { createText } from '../createText.js';
 import utils from '../../utils.js';
@@ -631,13 +631,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
 
   let url = '';
   if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
-    url =
-      window.location.protocol +
-      '//' +
-      window.location.host +
-      window.location.pathname +
-      window.location.search;
-    url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
+    url = getUrl(true);
   }
   log.info('arrowTypeStart', edge.arrowTypeStart);
   log.info('arrowTypeEnd', edge.arrowTypeEnd);


Reviews:
user: login: sidharthv96
body: LGTM, need an extra pair of eyes as it's security related. 

Also, can you add a util function and use that everywhere instead for the whole url handling. The code seems to be the same currently. 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-09T05:55:18Z
user: login: aloisklink
body: Awesome work on looking into fixing these warnings! I agree that it will probably cause some buggy behaviour if we don't fix this.

> Also, can you add a util function and use that everywhere instead for the whole url handling. The code seems to be the same currently.

We already have one, it's `getUrl()` in this `common.ts` file.

https://github.com/mermaid-js/mermaid/blob/0d047ccbcc4aaf33f802cd1a33cb28f1c66ffdd3/packages/mermaid/src/diagrams/common/common.ts#L146-L166

If you change all of this code to use that one function, then change it to fix this bug, that would be a better fix!


state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-09T13:58:02Z
user: login: arpitjain099
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-10T05:13:52Z
user: login: aloisklink
body: I hope you don't mind, one of the E2E tests was broken due to something that wasn't your fault, so I've fixed it in f20c6779824988506193d5afcbd500f16d76d16d and pushed it to your branch!

I think it's good now!

While we're waiting for @sidharthv96 to re-review this PR, would you like to add a changeset entry using `pnpm changeset` so your name shows up in the release notes? Maybe something like ```fix: escape additional characters in arrow-marker URL when `arrowMarkerAbsolute` is `true`.```.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-12T10:23:32Z
user: login: aloisklink
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T11:01:07Z
user: login: arpitjain099
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T11:15:17Z
user: login: aloisklink
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T11:34:38Z
user: login: aloisklink
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T11:34:48Z
user: login: aloisklink
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T11:35:06Z

Review comments:
diff_hunk: @@ -158,6 +158,7 @@ const getUrl = (useAbsolute: boolean): string => {
       window.location.host +
       window.location.pathname +
       window.location.search;
+    url = url.replace(/\\/g, '\\\\');
     url = url.replaceAll(/\(/g, '\\(');
     url = url.replaceAll(/\)/g, '\\)');
path: packages/mermaid/src/diagrams/common/common.ts
user: login: aloisklink
body: I think instead of using `url = url.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');`, we might want to use [`CSS.escape()`](https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape_static).

It looks like we're trying to create valid [`CSS <url-token>`](https://www.w3.org/TR/css-syntax-3/#typedef-url-token), as these are passed unquoted to the [SVG/CSS `url()` function](https://developer.mozilla.org/en-US/docs/Web/CSS/url_function). `CSS.escape` will probably overly escape characters, but it's probably cleaner and describes what we want to do better.

There's other characters that are valid in a URI like `'`, but aren't valid in a CSS `<url-token>` so they should be escaped too.
created_at: 2025-05-09T13:56:29Z
author_association: MEMBER
diff_hunk: @@ -158,6 +158,7 @@ const getUrl = (useAbsolute: boolean): string => {
       window.location.host +
       window.location.pathname +
       window.location.search;
+    url = url.replace(/\\/g, '\\\\');
     url = url.replaceAll(/\(/g, '\\(');
     url = url.replaceAll(/\)/g, '\\)');
path: packages/mermaid/src/diagrams/common/common.ts
user: login: arpitjain099
body: @aloisklink I have updated the function to use CSS.escape and replaced hardcoded occurrences with common function getUrl. Please review.
cc: @sidharthv96 
created_at: 2025-05-10T05:13:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'mermaid': major
path: .changeset/yellow-walls-fry.md
user: login: aloisklink
body: Since this is a bug-fix, this is a `patch` update!

`major` is reserved for breaking changes, which will require us to bump the Mermaid version to v12.

```suggestion
'mermaid': patch
```
created_at: 2025-05-12T10:43:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+---
+'mermaid': major
+---
+
+fix: Fixes unsafe or incomplete string escaping in URL manipulation logic by ensuring backslashes are escaped before other characters like parentheses.
path: .changeset/yellow-walls-fry.md
user: login: aloisklink
body: How's this?

```suggestion
fix: Fix incomplete string escaping in URL manipulation logic when `arrowMarkerAbsolute: true` by ensuring all unsafe characters are escaped.
```

- I've removed `unsafe` since this code is already escaped in the HTML. The bug you're fixing means that if you have a URL like: `http://localhost:9000/flowchart.html?hi=world(%20\)`, the arrow markers will now be correct.

  E.g. something like the following would still be a valid SVG, but the `url()` is invalid so the arrows won't be proper (and an additional `url(javascript:)` won't do anything so there shouldn't be any XSS attack possible):

  ```svg
  <path
    d="M196.238,47L196.238,51.167C196.238,55.333,196.238,63.667,196.238,71.333C196.238,79,196.238,86,196.238,89.5L196.238,93"
    id="L_A_B_0"
    class=" edge-thickness-thick edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
    style="stroke:gold; stroke-width:4px;fill:none;"
    marker-end="url(http://localhost:9000/flowchart.html?hi=world\(%20\\)#mermaid-1747046847808_flowchart-v2-pointEnd_gold)"></path>
  ```

- I also added ```when `arrowMarkerAbsolute: true` ``` since that's the only case where this bug appears.
- And I changed `ensuring backslashes are escaped` to `ensuring all unsafe characters are escaped` since your PR also fixes escaping `'`/`"`/ and whitespace.
created_at: 2025-05-12T10:59:53Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+---
+'mermaid': major
+---
+
+fix: Fixes unsafe or incomplete string escaping in URL manipulation logic by ensuring backslashes are escaped before other characters like parentheses.
path: .changeset/yellow-walls-fry.md
user: login: arpitjain099
body: @aloisklink Thank you for finding the issues with my commit. I have incorporated both of the feedback items. Please review again.
created_at: 2025-05-12T11:15:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -158,6 +158,7 @@ const getUrl = (useAbsolute: boolean): string => {
       window.location.host +
       window.location.pathname +
       window.location.search;
+    url = url.replace(/\\/g, '\\\\');
     url = url.replaceAll(/\(/g, '\\(');
     url = url.replaceAll(/\)/g, '\\)');
path: packages/mermaid/src/diagrams/common/common.ts
user: login: aloisklink
body: Fixed by https://github.com/mermaid-js/mermaid/pull/6566/commits/e79147dcfcf9d3282f6f4b85ac6a377e28104bd4
created_at: 2025-05-12T11:34:38Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+---
+'mermaid': major
path: .changeset/yellow-walls-fry.md
user: login: aloisklink
body: Fixed by https://github.com/mermaid-js/mermaid/pull/6566/commits/cadcddd40ce2cfb7e6db5555cc3941026ae7f6a6
created_at: 2025-05-12T11:34:48Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+---
+'mermaid': major
+---
+
+fix: Fixes unsafe or incomplete string escaping in URL manipulation logic by ensuring backslashes are escaped before other characters like parentheses.
path: .changeset/yellow-walls-fry.md
user: login: aloisklink
body: :rocket: Your changes in https://github.com/mermaid-js/mermaid/pull/6566/commits/942ae8dcbcb30d07307e52c45d890244c89de11e look good to me!

I think this is good to merge, but I'll wait until Wednesday to give @sidharthv96 a chance to re-review this!
created_at: 2025-05-12T11:35:06Z
author_association: MEMBER

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-05-05T12:26:10Z
body: ###  🦋  Changeset detected

Latest commit: d0f1dafd494633c1685abe1d158552b1ddf51631

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name    | Type  |
| ------- | ----- |
| mermaid | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/arpitjain099/mermaid/new/develop?filename=.changeset/gentle-eagles-doubt.md&value=---%0A%22mermaid%22%3A%20patch%0A---%0A%0Afix%3A%20escape%20backslashes%20before%20parentheses%20in%20URL%20replacement%20logic%0A)


author_association: NONE
user: login: netlify[bot]
created_at: 2025-05-05T12:26:24Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *mermaid-js* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | d0f1dafd494633c1685abe1d158552b1ddf51631 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/mermaid-js/deploys/68234c89b18a4b000862f3ef |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-6566--mermaid-js.netlify.app](https://deploy-preview-6566--mermaid-js.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTY1NjYtLW1lcm1haWQtanMubmV0bGlmeS5hcHAifQ.8OZS2kR2KjK2KOMM8GCOMRoamNOzcALwZ81QTrktC7Q)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [mermaid-js Preview](https://deploy-preview-6566--mermaid-js.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/mermaid-js/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: pkg-pr-new[bot]
created_at: 2025-05-05T12:27:14Z
body: 
[Open in StackBlitz](https://pkg.pr.new/template/b6e4f143-8aa1-406a-b249-0e1bf208346f)


<details><summary><b>mermaid</b></summary><p>

```
npm i https://pkg.pr.new/mermaid-js/mermaid@6566
```

</p></details>
      
    

<details><summary><b>@mermaid-js/mermaid-zenuml</b></summary><p>

```
npm i https://pkg.pr.new/mermaid-js/mermaid/@mermaid-js/mermaid-zenuml@6566
```

</p></details>
      
    

<details><summary><b>@mermaid-js/layout-elk</b></summary><p>

```
npm i https://pkg.pr.new/mermaid-js/mermaid/@mermaid-js/layout-elk@6566
```

</p></details>
      
    

<details><summary><b>@mermaid-js/parser</b></summary><p>

```
npm i https://pkg.pr.new/mermaid-js/mermaid/@mermaid-js/parser@6566
```

</p></details>
      
    

<details><summary><b>@mermaid-js/tiny</b></summary><p>

```
npm i https://pkg.pr.new/mermaid-js/mermaid/@mermaid-js/tiny@6566
```

</p></details>
      
    

_commit: <a href="https://github.com/mermaid-js/mermaid/runs/42137964141"><code>d0f1daf</code></a>_

author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-05T12:28:54Z
body: ## [Codecov](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) Report
Attention: Patch coverage is `7.14286%` with `13 lines` in your changes missing coverage. Please review.
> Project coverage is 3.88%. Comparing base [(`015afc5`)](https://app.codecov.io/gh/mermaid-js/mermaid/commit/015afc53d504f97eb0384f3c2dc9ee76ac6991a9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) to head [(`d0f1daf`)](https://app.codecov.io/gh/mermaid-js/mermaid/commit/d0f1dafd494633c1685abe1d158552b1ddf51631?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js).
> Report is 32 commits behind head on develop.

| [Files with missing lines](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) | Patch % | Lines |
|---|---|---|
| [packages/mermaid/src/dagre-wrapper/edges.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdagre-wrapper%2Fedges.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGFncmUtd3JhcHBlci9lZGdlcy5qcw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |
| [packages/mermaid/src/diagrams/class/svgDraw.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fclass%2FsvgDraw.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvY2xhc3Mvc3ZnRHJhdy5qcw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |
| [packages/mermaid/src/diagrams/er/erRenderer.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fer%2FerRenderer.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvZXIvZXJSZW5kZXJlci5qcw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |
| [.../mermaid/src/diagrams/sequence/sequenceRenderer.ts](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fsequence%2FsequenceRenderer.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvc2VxdWVuY2Uvc2VxdWVuY2VSZW5kZXJlci50cw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |
| [packages/mermaid/src/diagrams/state/shapes.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fstate%2Fshapes.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvc3RhdGUvc2hhcGVzLmpz) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |
| [...aid/src/rendering-util/rendering-elements/edges.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Frendering-util%2Frendering-elements%2Fedges.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvcmVuZGVyaW5nLXV0aWwvcmVuZGVyaW5nLWVsZW1lbnRzL2VkZ2VzLmpz) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |
| [packages/mermaid/src/diagrams/common/common.ts](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fcommon%2Fcommon.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvY29tbW9uL2NvbW1vbi50cw==) | 50.00% | [1 Missing :warning: ](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) |

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566/graphs/tree.svg?width=650&height=150&src=pr&token=BaET4V1BdM&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js)](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js)

```diff
@@           Coverage Diff           @@
##           develop   #6566   +/-   ##
=======================================
  Coverage     3.87%   3.88%           
=======================================
  Files          414     413    -1     
  Lines        43291   43239   -52     
  Branches       666     666           
=======================================
  Hits          1679    1679           
+ Misses       41612   41560   -52     
```

| [Flag](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) | Coverage Δ | |
|---|---|---|
| [unit](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) | `3.88% <7.14%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files with missing lines](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js) | Coverage Δ | |
|---|---|---|
| [packages/mermaid/src/diagrams/common/common.ts](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fcommon%2Fcommon.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvY29tbW9uL2NvbW1vbi50cw==) | `32.01% <50.00%> (+0.13%)` | :arrow_up: |
| [packages/mermaid/src/dagre-wrapper/edges.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdagre-wrapper%2Fedges.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGFncmUtd3JhcHBlci9lZGdlcy5qcw==) | `0.00% <0.00%> (ø)` | |
| [packages/mermaid/src/diagrams/class/svgDraw.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fclass%2FsvgDraw.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvY2xhc3Mvc3ZnRHJhdy5qcw==) | `0.00% <0.00%> (ø)` | |
| [packages/mermaid/src/diagrams/er/erRenderer.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fer%2FerRenderer.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvZXIvZXJSZW5kZXJlci5qcw==) | `0.00% <0.00%> (ø)` | |
| [.../mermaid/src/diagrams/sequence/sequenceRenderer.ts](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fsequence%2FsequenceRenderer.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvc2VxdWVuY2Uvc2VxdWVuY2VSZW5kZXJlci50cw==) | `0.07% <0.00%> (+<0.01%)` | :arrow_up: |
| [packages/mermaid/src/diagrams/state/shapes.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Fdiagrams%2Fstate%2Fshapes.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvZGlhZ3JhbXMvc3RhdGUvc2hhcGVzLmpz) | `0.00% <0.00%> (ø)` | |
| [...aid/src/rendering-util/rendering-elements/edges.js](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566?src=pr&el=tree&filepath=packages%2Fmermaid%2Fsrc%2Frendering-util%2Frendering-elements%2Fedges.js&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js#diff-cGFja2FnZXMvbWVybWFpZC9zcmMvcmVuZGVyaW5nLXV0aWwvcmVuZGVyaW5nLWVsZW1lbnRzL2VkZ2VzLmpz) | `0.00% <0.00%> (ø)` | |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/mermaid-js/mermaid/pull/6566/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mermaid-js)

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: arpitjain099
created_at: 2025-05-05T12:36:57Z
body: It is my first time contributing to this mermaid project. I have made multiple contributions to open source projects (please check my profile). This PR is to fix codeql related findings for mermaid project. Please review @sidharthv96 @knsv and/or other maintainers. Thank you
author_association: CONTRIBUTOR
user: login: argos-ci[bot]
created_at: 2025-05-05T12:38:35Z
body: **The latest updates on your projects.** Learn more about [Argos notifications ↗︎](https://argos-ci.com/docs/notifications)

| Build | Status | Details | Updated (UTC) |
| :---- | :----- | :------ | :------------ |
| **default** ([Inspect](https://app.argos-ci.com/mermaid/mermaid/builds/4155)) | ✅ Auto-approved build ([Review](https://app.argos-ci.com/mermaid/mermaid/builds/4155)) | 1 removed | May 13, 2025, 1:52 PM |
author_association: NONE
user: login: arpitjain099
created_at: 2025-05-09T13:59:12Z
body: Okay. Thank you. I am not very familiar with other part of the codebase but I'll try tomorrow. 
author_association: CONTRIBUTOR
user: login: arpitjain099
created_at: 2025-05-11T08:02:13Z
body: Please review this PR again @sidharthv96 @aloisklink  (when you have time). I have addressed your comments above. Thank you.
author_association: CONTRIBUTOR
user: login: arpitjain099
created_at: 2025-05-12T10:40:55Z
body: > I hope you don't mind, one of the E2E tests was broken due to something that wasn't your fault, so I've fixed it in [f20c677](https://github.com/mermaid-js/mermaid/commit/f20c6779824988506193d5afcbd500f16d76d16d) and pushed it to your branch!
> 
> I think it's good now!
> 
> While we're waiting for @sidharthv96 to re-review this PR, would you like to add a changeset entry using `pnpm changeset` so your name shows up in the release notes? Maybe something like `` fix: escape additional characters in arrow-marker URL when `arrowMarkerAbsolute` is `true`. ``.

Thank you @aloisklink 
I have pushed a [commit](https://github.com/mermaid-js/mermaid/pull/6566/commits/bca6ed67c3e0db910bf498fdd0fc0346c02d392b). Please review.
author_association: CONTRIBUTOR
user: login: arpitjain099
created_at: 2025-05-17T05:31:21Z
body: Bumping this PR up (apologies for the spam!) so it can be reviewed again by @sidharthv96 

cc: @aloisklink 

(I do understand managing such an active open source project must be very demanding)
author_association: CONTRIBUTOR
user: login: mermaid-bot[bot]
created_at: 2025-05-20T06:53:43Z
body: @arpitjain099, Thank you for the contribution!
You are now eligible for a year of Premium account on [MermaidChart](https://www.mermaidchart.com). 
[Sign up](https://www.mermaidchart.com/app/sign-up) with your GitHub account to activate.
author_association: NONE