Feb 8, 2026·5 min read·22 visits
Gogs was using an outdated version of Mermaid.js (v8.14.0) to render diagrams in Markdown. Due to a lack of input sanitization in the library and a missing security configuration ('sandbox') in Gogs, attackers can inject arbitrary JavaScript via malicious diagrams. Fixed in Gogs by updating Mermaid to v11.12.1.
A classic tale of neglected dependencies and implicit trust. Gogs, the 'painless' self-hosted Git service, fell victim to a high-severity Stored Cross-Site Scripting (XSS) vulnerability. By bundling an ancient, vulnerable version of the Mermaid.js diagramming library and initializing it without a sandbox, Gogs turned every issue tracker and wiki page into a potential minefield. This report dives into the mechanics of the Mermaid bypass and why client-side rendering of user-supplied DSLs is a dangerous game.
Gogs prides itself on being a "painless" self-hosted Git service. It's written in Go, it's fast, and it's easy to deploy. Like many modern developer tools, it supports Markdown rendering for issues, pull requests, and wikis. And because developers love boxes and arrows, it bundles Mermaid.js to turn code blocks into pretty flowcharts and architecture diagrams.
But here's the rub: Rendering diagrams from user-supplied text is complex. It involves parsing a Domain Specific Language (DSL) and converting it into complex HTML and SVG structures. If the parser gets confused, or if the renderer gets lazy about sanitizing the output, you don't just get a broken diagram—you get Remote Code Execution in the browser context.
This vulnerability isn't just about a bug in Gogs code; it's a supply chain failure. Gogs was shipping with Mermaid version 8.14.0. In JavaScript years, that is practically the Paleolithic era. This outdated library, combined with an insecure default configuration, created a perfect storm for Stored XSS.
The vulnerability stems from two distinct failures that, when combined, open the door to exploitation.
First: The Dependency Rot.
Gogs was bundling Mermaid versions 8.14.0 and 11.9.0. These versions contain known vulnerabilities (tracked loosely under various CVEs like CVE-2025-54880 and others affecting Mermaid) where the parser fails to properly sanitize node labels. Specifically, the architecture-beta diagram type—a newer feature—was particularly bad at handling special characters inside service descriptions.
Second: The Configuration Gap.
Even a vulnerable library can be defanged if you configure it correctly. Mermaid offers a securityLevel setting. If you set this to "sandbox", Mermaid renders the diagram inside a sandboxed <iframe>, isolating it from the main application's DOM. Gogs did not use this setting. Instead, it initialized Mermaid directly in the global scope. This meant that if an attacker could trick Mermaid into generating an HTML tag, that tag would be rendered live on the page, with full access to the user's cookies, session tokens, and the Gogs DOM.
Let's look at the "smoking gun" in templates/base/footer.tmpl. This is where Gogs loads the frontend assets. The fix commit 71a72a72ad1c8cea7940c9d7e4cbdfbc0fc3d401 shows exactly what changed.
The Vulnerable Implementation:
<!-- Loading a relic from the past -->
<script src="{{AppSubURL}}/plugins/mermaid-8.14.0/mermaid.min.js"></script>
<script>
// Initializing without a safety net
mermaid.init({startOnLoad: true, noteMargin: 10}, ".language-mermaid");
</script>The Fix:
<!-- Bumping to the modern era -->
<script src="{{AppSubURL}}/plugins/mermaid-11.12.1/mermaid.min.js"></script>Notice something missing? While they updated the library to 11.12.1 (which patches the specific parser bypass), they still didn't enable securityLevel: 'sandbox'. They fixed the specific exploit vector, but the architectural weakness remains. It's like patching a hole in the hull with duct tape but leaving the torpedo bay doors open.
To exploit this, we don't need complex memory corruption or buffer overflows. We just need to write a "diagram". The vulnerability lies in how the architecture-beta diagram type handles labels. It expects a format like service name(icon)[description].
In the vulnerable version, the content inside [] was blindly concatenated into the HTML output. An attacker can craft a malicious Markdown payload and post it to a public issue or a repository wiki.
The Payload:
# Project Architecture Proposal
Please review this diagram:
```mermaid
architecture-beta
service benign_service(server)[<img src=x onerror="alert('XSS on ' + document.domain)">]
**The Execution Flow:**
1. The victim (perhaps an admin reviewing a PR) loads the page.
2. Gogs serves the page containing the malicious Markdown.
3. The browser runs the `mermaid.init()` script.
4. Mermaid parses the `architecture-beta` block.
5. It encounters our `<img>` tag inside the description bracket.
6. Because sanitization failed, it appends `<img src=x onerror=...>` directly to the DOM.
7. The browser tries to load image source `x`, fails, and triggers `onerror`.
8. **BOOM.** The attacker's JavaScript executes.
It's easy to dismiss XSS as "just a popup," but on a platform like Gogs, it is catastrophic. This is Stored XSS, meaning the payload persists in the database and is served to anyone who views the affected page.
Scenario 1: Account Takeover
If an administrator views the malicious issue, the script can steal their session cookie (assuming HttpOnly isn't strictly enforced or bypassable via other means) or force their browser to perform actions on their behalf. The attacker is now the admin.
Scenario 2: Wormable Repository Infection The script could be designed to be wormable. When a victim views the diagram, the script could use the Gogs API to edit other repositories the victim has access to, injecting the same malicious diagram into their READMEs. Within hours, the entire Gogs instance could be infected.
Scenario 3: Backdoor Injection An attacker could modify the source code of repositories silently. The next time a developer pulls the code, they pull a backdoor into their production environment.
The immediate fix provided by the Gogs maintainers is to upgrade the bundled Mermaid library.
Step 1: Apply the Patch
If you are running a source install, checkout the latest master or specifically commit 71a72a72. If you are using binaries, update to the latest release immediately.
Step 2: Check Your Content
If your instance is public-facing, you may want to scan your database for suspicious usages of mermaid blocks containing HTML tags like <img, <script, or onerror attributes.
Step 3: Defense in Depth (Developer Note) For anyone building similar systems: Do not trust client-side rendering libraries. If you must use Mermaid, use the sandbox mode.
mermaid.initialize({
startOnLoad: true,
securityLevel: 'sandbox', // <--- This saves you
});This forces the diagram into an iframe, neutralizing most XSS vectors even if the parser itself is buggy.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Gogs Gogs | < commit 71a72a7 | Commit 71a72a7 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Stored Cross-Site Scripting (XSS) |
| Attack Vector | Network (Markdown Injection) |
| Affected Component | Mermaid.js (bundled) |
| Impact | Session Hijacking, RCE (via Admin context) |
| Severity | High |
| Fix Version | Mermaid 11.12.1 |