CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-23847
6.10.03%

SVG Shenanigans: Drawing Cross-Site Scripting in SiYuan

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 16, 2026·6 min read·12 visits

PoC Available

Executive Summary (TL;DR)

A Reflected XSS vulnerability in SiYuan's `/api/icon/getDynamicIcon` endpoint allows attackers to inject arbitrary JavaScript via the `content` parameter. The server reflects this input into an unsanitized SVG response served with an XML MIME type, leading to immediate code execution in the victim's browser.

In the world of Personal Knowledge Management (PKM), we treat our digital notebooks as extensions of our brains—secure, private, and trusted. But what happens when your 'second brain' has a hole in it? CVE-2026-23847 exposes a Reflected Cross-Site Scripting (XSS) vulnerability in SiYuan, a popular open-source privacy-first note-taking app. By abusing the dynamic icon generation API, attackers can inject malicious JavaScript payload directly into SVG images rendered by the browser. This isn't just about popping alert boxes; it's about compromising the integrity of a user's entire knowledge base through a single, carelessly crafted link.

The Hook: Your Second Brain, Lobotomized

SiYuan pitches itself as a privacy-first, local-first personal knowledge management system. It's the kind of tool where you store your deepest thoughts, your startup ideas, and maybe even your passwords (though please, don't do that). The architecture is interesting—it's often self-hosted or run locally, serving a web interface to the user. This makes it a web app, subject to all the classic web vulnerabilities, even if it feels like a desktop application.

Deep within the application's API lies a feature designed to make your notes look pretty: getDynamicIcon. Its job is simple. You give it some text, and it generates a sleek SVG icon on the fly to display next to your document titles. It's a nice touch for UI polish.

But here's the thing about 'simple' features: they are often where developers let their guard down. The assumption is, "It's just generating an icon, what could go wrong?" As it turns out, when you mix user input with vector graphics and forget to sanitize, the answer is "everything."

The Flaw: Trusting User Input (Again)

The vulnerability lives in kernel/api/icon.go. The logic is painfully straightforward. The application accepts a content query parameter, representing the text you want inside your icon. It then constructs an SVG string by concatenating this input into a template. Specifically, when the type parameter is set to 8 (text-based icons), the code grabs the string and drops it right into a <text> element.

Here is the fatal mistake: The application serves this response with the Content-Type: image/svg+xml. To a modern browser, an SVG served this way is not just a picture; it's an XML document capable of executing JavaScript. If the application had served it as image/png or sanitized the input, we wouldn't be having this conversation.

Instead, the developers performed direct string interpolation without escaping XML special characters. This is the equivalent of locking your front door but leaving the window wide open with a neon sign pointing to it. An attacker doesn't need to be a wizard; they just need to know how to close a tag.

The Code: The Smoking Gun

Let's look at the crime scene. In the vulnerable version of kernel/api/icon.go, the code effectively did this:

// The Vulnerable Logic
func getDynamicIcon(c *gin.Context) {
    content := c.Query("content")
    // ... font calculation logic ...
    
    // DIRECT INJECTION
    svg := fmt.Sprintf(`<svg ...><text ...>%s</text></svg>`, content)
    
    c.Header("Content-Type", "image/svg+xml")
    c.String(http.StatusOK, svg)
}

See that %s? That's where the dragon lives. There is zero validation. If content contains </text><script>..., Go happily prints it into the buffer.

The fix, introduced in commit 5c0cc375b47567e15edd2119066b09bb0aa18777, adds a sanity check. They didn't just escape the output; they added a scrubber function and a configuration toggle.

// The Patched Logic
func getDynamicIcon(c *gin.Context) {
    // ... generation logic ...
 
    // The Guard Rail
    if !model.Conf.Editor.AllowSVGScript {
        svg = util.RemoveScriptsInSVG(svg)
    }
 
    c.Header("Content-Type", "image/svg+xml")
    c.String(http.StatusOK, svg)
}

The util.RemoveScriptsInSVG function is now the bouncer at the club, kicking out any <script> tags before they hit the browser. It's a blacklist approach, which makes security researchers twitchy (whitelists are always better), but for this specific vector, it stops the bleeding.

The Exploit: Weaponizing Vector Graphics

Exploiting this is trivial. We need to construct a URL that, when clicked by a victim, renders an SVG containing our malicious payload. Since the input is placed inside a <text> tag, we first need to break out of that context.

The Payload Construction:

  1. Close the prison: </text>
  2. Inject the weapon: <script>alert(document.domain)</script>
  3. Clean up the crime scene: <text> (to keep the SVG valid enough to render, purely optional for execution but good for stealth).

The Final URL:

http://target-siyuan-instance/api/icon/getDynamicIcon
?type=8
&content=</text><script>alert('Pwned')</script><text>

When the victim clicks this link, their browser requests the icon. The server reflects the payload back with Content-Type: image/svg+xml. The browser sees the <script> tag and executes it in the origin of the SiYuan instance.

> [!WARNING] > Re-exploitation Potential: The patch uses a function called RemoveScriptsInSVG. If this function creates a blacklist (e.g., regex replacing <script>), it might be bypassable using alternative SVG execution vectors like <a xlink:href="javascript:..."> or event handlers like <svg onload=...>. A determined attacker would immediately fuzz this sanitizer against the SVG specification.

The Impact: Why Should We Panic?

This is a Reflected XSS, meaning it requires user interaction (clicking a link). However, in the context of a personal knowledge management system, the impact is severe.

If an attacker tricks a user into clicking a malicious link while they are logged into their SiYuan instance:

  1. Session Hijacking: The script can steal session cookies or local storage tokens, giving the attacker full access to the account.
  2. Data Exfiltration: The script can issue AJAX requests to SiYuan's API to search for and exfiltrate private notes, diaries, and sensitive documentation.
  3. RCE Potential: In some self-hosted setups or if SiYuan is running in an Electron wrapper with poor isolation, XSS can sometimes be chained into Remote Code Execution (RCE).

Essentially, if you click the wrong link, your private "second brain" becomes public property.

The Fix: Stopping the Bleeding

The immediate fix is to upgrade SiYuan to version 3.5.4 or later. The developers have patched the specific hole by stripping scripts from generated SVGs.

Beyond the patch, there is a configuration setting you should verify. In Settings -> Editor, ensure that "Allow execution of SVG scripts" is disabled. This is the default in newer versions, but if you've been tinkering with configs, check it.

For developers reading this: Never use string concatenation to build XML or HTML. Use proper encoding libraries. And if you must serve user-generated SVGs, serve them with Content-Security-Policy: script-src 'none' or force them to download via Content-Disposition: attachment to prevent inline execution.

Official Patches

SiYuanCommit fixing the issue by adding script sanitization.

Fix Analysis (1)

Technical Appendix

CVSS Score
6.1/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
EPSS Probability
0.03%
Top 92% most exploited

Affected Systems

SiYuan Note (All versions < 3.5.4)

Affected Versions Detail

Product
Affected Versions
Fixed Version
SiYuan
b3log
< 3.5.43.5.4
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork (Reflected)
CVSS v3.16.1 (Medium)
EPSS Score0.00028 (Top 8%)
ImpactConfidentiality & Integrity Loss
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-79
Cross-site Scripting (XSS)

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Known Exploits & Detection

GitHub IssueOriginal report detailing the SVG injection vector via the content parameter.

Vulnerability Timeline

Vulnerability reported in GitHub Issue #16844
2026-01-16
Patch commit 5c0cc37 pushed to repository
2026-01-18
GHSA-w836-5gpm-7r93 published
2026-01-19
CVE-2026-23847 assigned
2026-01-19

References & Sources

  • [1]GitHub Security Advisory
  • [2]NVD Detail

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.