Repository navigation
Fix #24: Sanitize Node Metadata Inputs - #29
Conversation
📝 WalkthroughWalkthrough
ChangesMermaid Output Escaping
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 PHPStan (2.2.6)Composer install failed: the lock file is not up to date with the latest changes in composer.json. Run Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/diusazzad/laranexus/src/Support/RouteMapCollector.php`:
- Line 72: Update the htmlspecialchars() calls in RouteMapCollector, including
the escaping performed for names, routes, middleware, actions, models, and
views, to combine ENT_QUOTES with ENT_SUBSTITUTE. Preserve UTF-8 encoding and
ensure invalid byte sequences are substituted rather than removed from Mermaid
labels.
- Around line 72-89: Update the Mermaid label generation in RouteMapCollector’s
route and controller label paths, including the corresponding lines around the
method labels, so user-controlled names, URIs, actions, and middleware cannot
become executable or unintended HTML after Mermaid decodes entities. Either
apply an allowlisted sanitizer that preserves only explicitly supported
formatting or disable HTML labels and use a stricter Mermaid security mode;
ensure the dashboard output remains safe and add an end-to-end test covering
HTML-bearing route metadata.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8ee8b59f-c3ad-471b-a2b2-fcc5e142bc49
📒 Files selected for processing (1)
packages/diusazzad/laranexus/src/Support/RouteMapCollector.php
| foreach ($controllers as $name => $data) { | ||
| $ctrlId = "C_" . md5($name); | ||
| $mermaidLines[] = " subgraph {$ctrlId} [\"$name\"]"; | ||
| $safeName = htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate file =="
fd -a 'RouteMapCollector.php' . || true
echo "== file outline =="
ast-grep outline packages/diusazzad/laranexus/src/Support/RouteMapCollector.php --view expanded || true
echo "== relevant lines =="
cat -n packages/diusazzad/laranexus/src/Support/RouteMapCollector.php | sed -n '60,120p'
echo "== all htmlspecialchars usages in file =="
rg -n "htmlspecialchars" packages/diusazzad/laranexus/src/Support/RouteMapCollector.php
echo "== PHP availability/version =="
php -v 2>/dev/null || true
php -r 'echo implode("|", [
defined("ENT_QUOTES"),
defined("ENT_SUBSTITUTE"),
ENT_QUOTES,
ENT_SUBSTITUTE,
]);' 2>/dev/null || true
echo "== behavioral probe =="
tmpfile="$(mktemp)"
cat > "$tmpfile" <<'PHP'
<?php
if (!function_exists('htmlspecialchars')) {
echo "htmlspecialchars unavailable\n";
exit(0);
}
$inputs = [
"ok",
"-\x80",
"-\xFF",
"-\xC3",
];
$flags = [
"ENT_QUOTES only" => ENT_QUOTES,
"ENT_QUOTES | ENT_SUBSTITUTE" => ENT_QUOTES | ENT_SUBSTITUTE,
];
if (defined('ENT_SUBSTITUTE')) {
foreach ($inputs as $input) {
echo "input bytes: " . bin2hex($input) . PHP_EOL;
foreach ($flags as $label => $flag) {
if ($label === "ENT_QUOTES | ENT_SUBSTITUTE" && !defined('ENT_SUBSTITUTE')) {
continue;
}
$out = htmlspecialchars($input, $flag, 'UTF-8');
echo "$label: bytes=" . bin2hex($out) . " len=" . strlen($out) . PHP_EOL;
}
}
} else {
foreach ($inputs as $input) {
echo "input bytes: " . bin2hex($input) . PHP_EOL;
$out = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
echo "ENT_QUOTES only: bytes=" . bin2hex($out) . " len=" . strlen($out) . PHP_EOL;
}
}
PHP
php "$tmpfile"
rm -f "$tmpfile"Repository: diusazzad/LaraNexus
Length of output: 5340
Preserve ENT_SUBSTITUTE when escaping Mermaid labels.
The default flags are not preserved when only ENT_QUOTES is passed, so invalid UTF-8 in names, routes, middleware, actions, models, or views turns into an empty string and can silently erase labels. Add ENT_SUBSTITUTE to these htmlspecialchars() calls.
Suggested change
- htmlspecialchars($value, ENT_QUOTES, 'UTF-8')
+ htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $safeName = htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); | |
| $safeName = htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/diusazzad/laranexus/src/Support/RouteMapCollector.php` at line 72,
Update the htmlspecialchars() calls in RouteMapCollector, including the escaping
performed for names, routes, middleware, actions, models, and views, to combine
ENT_QUOTES with ENT_SUBSTITUTE. Preserve UTF-8 encoding and ensure invalid byte
sequences are substituted rather than removed from Mermaid labels.
Source: MCP tools
| $safeName = htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); | ||
| $mermaidLines[] = " subgraph {$ctrlId} [\"$safeName\"]"; | ||
|
|
||
| foreach ($data['routes'] as $r) { | ||
| $routeId = "R_" . md5($r['uri']); | ||
| $methodId = "M_" . md5($r['uri'] . $r['action']); | ||
|
|
||
| // Route Node with Middleware | ||
| $label = "/{$r['uri']}"; | ||
| $safeUri = htmlspecialchars($r['uri'], ENT_QUOTES, 'UTF-8'); | ||
| $label = "/{$safeUri}"; | ||
| if ($r['middleware']) { | ||
| $label .= " <br/><small>🔐 {$r['middleware']}</small>"; | ||
| $safeMiddleware = htmlspecialchars($r['middleware'], ENT_QUOTES, 'UTF-8'); | ||
| $label .= " <br/><small>🔐 {$safeMiddleware}</small>"; | ||
| } | ||
|
|
||
| $safeAction = htmlspecialchars($r['action'], ENT_QUOTES, 'UTF-8'); | ||
| $mermaidLines[] = " {$routeId}([\"$label\"]):::route"; | ||
| $mermaidLines[] = " {$methodId}(\"{$r['action']}\"):::method"; | ||
| $mermaidLines[] = " {$methodId}(\"{$safeAction}\"):::method"; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
version="${MERMAID_VERSION:-10.9.6}"
base="https://raw.githubusercontent.com/mermaid-js/mermaid/v${version}/packages/mermaid/src"
integration="$(curl -fsSL "$base/mermaid.ts")"
api="$(curl -fsSL "$base/mermaidAPI.ts")"
grep -Fq 'txt = element.innerHTML' <<<"$integration"
grep -Fq 'utils.entityDecode(txt)' <<<"$integration"
grep -Fq 'else if (!isLooseSecurityLevel)' <<<"$api"
echo "Verified Mermaid ${version} parsing and loose-mode sanitization behavior."Repository: diusazzad/LaraNexus
Length of output: 227
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== Locate relevant files =="
fd -a 'RouteMapCollector.php|*.blade.php|composer.lock|package.json|pnpm-lock.yaml|yarn.lock|package-lock.json' . \
| sed 's#^\./##' | head -200
echo
echo "== RouteMapCollector excerpt =="
file="$(fd 'RouteMapCollector.php' . | head -1)"
if [ "${file:-}" != "" ]; then
wc -l "$file"
sed -n '1,160p' "$file" | nl -ba
fi
echo
echo "== Mermaid config/usages through raw blade or securityLevel =="
rg -n --hidden --glob '!vendor/**' --glob '!node_modules/**' "securityLevel|DOMPurify|\.mermaid|innerHTML|RouteMapCollector|mermaid" . | head -300
echo
echo "== Composer/package Mermaid references =="
rg -n --hidden --glob '!vendor/**' --glob '!node_modules/**' "mermaid|viz" composer.json composer.lock package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null || trueRepository: diusazzad/LaraNexus
Length of output: 590
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== Locate relevant files =="
files=()
for p in packages/diusazzad/laranexus/src/Support/RouteMapCollector.php; do
[ -f "$p" ] && files+=("$p")
done
fd 'RouteMapCollector.php$' . packages || true
fd '\.blade\.php$' . packages || true
for f in composer.lock package.json pnpm-lock.yaml yarn.lock package-lock.json; do
[ -f "$f" ] && files+=("$f")
done
printf '%s\n' "${files[@]}"
file="$(fd 'RouteMapCollector.php$' . packages | head -1 || true)"
if [ -n "${file:-}" ]; then
echo
echo "== RouteMapCollector excerpt =="
wc -l "$file"
sed -n '1,170p' "$file" | nl -ba
fi
echo
echo "== Mermaid config/usages through raw blade or securityLevel =="
rg -n --hidden --glob '!vendor/**' --glob '!node_modules/**' "securityLevel|DOMPurify|\.mermaid|innerHTML|RouteMapCollector|mermaid" . packages || true
echo
echo "== Composer/package Mermaid references =="
for f in composer.json composer.lock package.json pnpm-lock.yaml yarn.lock package-lock.json; do
[ -f "$f" ] && { echo "--- $f"; rg -n 'mermaid|viz' "$f" || true; }
doneRepository: diusazzad/LaraNexus
Length of output: 864
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== RouteMapCollector excerpt =="
sed -n '1,170p' packages/diusazzad/laranexus/src/Support/RouteMapCollector.php
echo
echo "== Dashboard and relevant Blade excerpts =="
sed -n '1,240p' packages/diusazzad/laranexus/resources/views/dashboard.blade.php
rg -n --hidden --glob '!vendor/**' --glob '!node_modules/**' "RouteMapCollector|mermaid|securityLevel|innerHTML|`@raw`|dangerouslySetInnerHTML" packages/diusazzad/laranexus resources . | head -200
echo
echo "== Mermaid package references =="
for f in composer.json composer.lock package.json pnpm-lock.yaml yarn.lock package-lock.json; do
[ -f "$f" ] && { echo "--- $f"; rg -n 'mermaid|viz' "$f" || true; }
doneRepository: diusazzad/LaraNexus
Length of output: 25764
🌐 Web query:
Mermaid v10 parses innerHTML reads element.innerHTML entityDecode htmlLabels labels
💡 Result:
In Mermaid, the interaction between htmlLabels, innerHTML, and entity decoding has been a critical security consideration, particularly in versions 10 and 11. The htmlLabels configuration option determines whether Mermaid uses HTML elements (like <span> or <div>) to render node and edge labels instead of standard SVG text elements [1][2]. When htmlLabels is enabled, the library may process input that includes HTML tags, which can introduce security risks if the input is not strictly sanitized [1][3][4]. A notable security vulnerability (CVE-2025-54881) was identified in Mermaid versions 10.9.0-rc.1 through 11.9.0 [5][6][7]. In these versions, user-supplied input for sequence diagram labels containing KaTeX delimiters was passed to a function called calculateMathMLDimensions [8][7]. This function incorrectly assigned the raw, unsanitized input to an element's innerHTML property to measure its dimensions [8][6][7]. This behavior created an XSS (Cross-Site Scripting) sink, allowing attackers to inject arbitrary malicious HTML [8][5][7]. Key points regarding Mermaid's handling of these mechanisms include: - Security Levels: Mermaid provides a securityLevel configuration (strict, loose, antiscript, sandbox) to manage trust [1]. The strict (default) setting encodes HTML tags, while loose or antiscript may permit them, increasing the risk of XSS if the input source is untrusted [1][3]. - Sanitization: Even when htmlLabels is enabled, Mermaid employs internal sanitization (often using DOMPurify) to strip dangerous elements [4]. However, vulnerabilities arise when components—such as the aforementioned calculateMathMLDimensions—bypass these protections by directly setting innerHTML [8][4]. - Entity Decoding: Mermaid performs entity decoding (e.g., converting < back to <) to ensure labels render correctly [9]. Developers have worked to ensure that such decoding is handled safely (e.g., using a local function in createText.ts) to avoid affecting global rendering processes or creating security regressions [9]. To mitigate these risks, it is recommended to upgrade Mermaid to version 11.10.0 or higher [6][7]. Additionally, users handling untrusted diagram input should enforce a securityLevel of strict or sandbox and consider applying secondary sanitization on the rendered output if custom HTML rendering is required [1][4].
Citations:
- 1: https://github.com/mermaid-js/mermaid/blob/9745f325/packages/mermaid/src/docs/config/usage.md
- 2: https://pub.dev/documentation/mermaid/latest/mermaid/FlowChartConfig-class.html
- 3: Flowchart rendered differently depending on whether "htmlLabels" is enabled or not mermaid-js/mermaid#1572
- 4: https://osv.dev/vulnerability/GHSA-w95v-4h65-j455
- 5: https://nvd.nist.gov/vuln/detail/cve-2025-54881
- 6: https://security.snyk.io/vuln/SNYK-JS-MERMAID-12027649
- 7: https://osv.dev/vulnerability/GHSA-7rqq-prvp-x9jh
- 8: GHSA-7rqq-prvp-x9jh
- 9: fix: prevent escaping
<and&whenhtmlLabels: falsemermaid-js/mermaid#7436
🌐 Web query:
GHSA-7rqq-prvp-x9jh Mermaid v10.9.6 flowchart htmlLabels XSS innerHTML entityDecode
💡 Result:
The vulnerability identified as GHSA-7rqq-prvp-x9jh (also tracked as CVE-2025-54881) is a Cross-Site Scripting (XSS) flaw in the Mermaid.js library [1][2][3]. The vulnerability stems from improper sanitization of user-supplied input in sequence diagram labels [1][2]. Specifically, the library's calculateMathMLDimensions function—which is used when KaTeX support is enabled (the default configuration)—takes the raw label text and passes it directly to innerHTML when calculating element dimensions [1][4]. This allows attackers to inject malicious HTML and execute arbitrary scripts in the victim's browser [2][5]. This issue affects Mermaid versions 10.9.0-rc.1 through 11.9.0 [3][6]. It was patched in version 11.10.0 [1][2][5]. Regarding your query about flowchart htmlLabels and entityDecode: while GHSA-7rqq-prvp-x9jh specifically targets sequence diagram labels via the calculateMathMLDimensions function [1][2], Mermaid's handling of htmlLabels (often used in flowcharts) has historically presented security challenges, especially when combined with a loose securityLevel configuration [7][8]. In such setups, if Mermaid's output is injected into the DOM via innerHTML without secondary sanitization, it can lead to vulnerabilities like SSRF or NTLM hash theft, as the library's internal sanitizer may permit certain tags (such as <img>) that can trigger network requests [7][8]. Developers are advised to use securityLevel: "strict" and ensure any HTML rendered from Mermaid diagrams is passed through a robust sanitizer like DOMPurify before being inserted into the page [7][8].
Citations:
- 1: GHSA-7rqq-prvp-x9jh
- 2: GHSA-7rqq-prvp-x9jh
- 3: https://nvd.nist.gov/vuln/detail/CVE-2025-54881
- 4: https://osv.dev/vulnerability/GHSA-7rqq-prvp-x9jh
- 5: https://security.snyk.io/vuln/SNYK-JS-MERMAID-12027649
- 6: https://nvd.nist.gov/vuln/detail/cve-2025-54881
- 7: GHSA-w95v-4h65-j455
- 8: https://osv.dev/vulnerability/GHSA-w95v-4h65-j455
🌐 Web query:
site:github.com/mermaid-js/mermaid v10.9.6 packages/mermaid/src/mermaid.ts txt = element.innerHTML utils.entityDecode
💡 Result:
In the context of the Mermaid.js library (specifically versions like v10.9.6), the code snippet involving txt = element.innerHTML and utils.entityDecode(txt) is found in the main rendering logic, typically located within packages/mermaid/src/mermaid.ts [1][2]. This code handles the retrieval and processing of the diagram definition from the DOM [1]. Specifically: 1. txt = element.innerHTML: The library retrieves the raw HTML content of the target element (the div containing the Mermaid diagram definition) [1][2]. 2. utils.entityDecode(txt): Since HTML content often includes encoded entities (e.g., > instead of >), this utility function decodes those entities back into their original character representations [1][2]. 3. Dedent and Cleaning: The decoded text is passed through a dedent function to remove unnecessary indentation, which is crucial for correct YAML and diagram parsing, and then trimmed and cleaned (e.g., replacing
tags with a standardized format) [1][2]. This sequence ensures that the diagram definition provided by the user is correctly interpreted as valid text, regardless of how it was encoded within the HTML document [1][2].
Citations:
- 1: https://github.com/mermaid-js/mermaid/blob/8b52e537/packages/mermaid/src/mermaid.ts
- 2: https://github.com/mermaid-js/mermaid/blob/d23e36d07b8cdf332c34bde607fe8f1986a76bb0/packages/mermaid/src/mermaid.ts
Don’t rely on htmlspecialchars() for Mermaid inputs.
The dashboard renders $mermaidString through raw Blade output and runs Mermaid with securityLevel: 'loose' plus htmlLabels; encoded values are decoded by the Mermaid pipeline before parsing, so tags encoded via htmlspecialchars() can be interpreted as markup again. Either sanitize/deny HTML carefully for the allowed formatting or switch to a non-HTML label mode/stricter Mermaid mode, and cover this with an end-to-end test.
Also applies to: 103-111
🧰 Tools
🪛 ast-grep (0.45.0)
[error] 75-75: Do not use a weak hash algorithm
Context: md5($r['uri'])
Note: [CWE-328] Use of Weak Hash.
(weak-hash-algorithm)
[error] 76-76: Do not use a weak hash algorithm
Context: md5($r['uri'] . $r['action'])
Note: [CWE-328] Use of Weak Hash.
(weak-hash-algorithm)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/diusazzad/laranexus/src/Support/RouteMapCollector.php` around lines
72 - 89, Update the Mermaid label generation in RouteMapCollector’s route and
controller label paths, including the corresponding lines around the method
labels, so user-controlled names, URIs, actions, and middleware cannot become
executable or unintended HTML after Mermaid decodes entities. Either apply an
allowlisted sanitizer that preserves only explicitly supported formatting or
disable HTML labels and use a stricter Mermaid security mode; ensure the
dashboard output remains safe and add an end-to-end test covering HTML-bearing
route metadata.
Source: MCP tools
Summary by CodeRabbit