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-23645
6.10.03%

SiYuan Note: When Your 'Secure' Notes Start Executing Code

Alon Barad
Alon Barad
Software Engineer

Feb 17, 2026·5 min read·3 visits

PoC Available

Executive Summary (TL;DR)

SiYuan Note versions prior to 3.5.4-dev2 fail to sanitize uploaded SVG files. Attackers can embed JavaScript in these 'images', leading to Stored XSS. The vendor patch attempts to strip <script> tags but misses event handlers, allowing for immediate re-exploitation.

A critical Stored Cross-Site Scripting (XSS) vulnerability in SiYuan Note allows attackers to weaponize the 'local-first' knowledge base. By uploading malicious SVG assets, an attacker can execute arbitrary JavaScript within the context of the application. The vendor's initial attempt to patch this issue relies on incomplete blacklisting, leaving the door open for trivial bypasses.

The Target: Your Digital Brain

SiYuan Note pitches itself as a privacy-focused, local-first knowledge management system. It's where users dump their most sensitive thoughts, passwords, and research, trusting that "local-first" means "secure from the web." It supports Markdown, block-based editing, and rich assets.

That last part—rich assets—is where things get messy. To make notes look pretty, SiYuan allows users to upload images. Among the supported formats is SVG (Scalable Vector Graphics). If you've been in the security game for more than ten minutes, you know exactly where this is going.

SVGs aren't just pictures; they are XML documents. They support the full power of the DOM, including JavaScript execution. When an application treats an SVG like a harmless PNG, it's not just displaying an image; it's rendering an active document. If you control the document, you control the viewer.

The Flaw: XML Disguised as Art

The root cause here is a classic "file type confusion" in security architecture. The developers implemented a feature to upload assets but failed to scrub them for active content. In versions prior to 3.5.4-dev2, the application accepted any file with an .svg extension and served it back to the browser with Content-Type: image/svg+xml.

Browsers are obedient. When they see image/svg+xml, they parse the XML. If that XML contains a <script> tag, the browser executes it. Because SiYuan serves these assets from the same origin as the application, that script runs with full trust.

This is Stored XSS 101. An attacker uploads malware.svg as an attachment to a shared note or a public workspace. When the victim opens the note—or even just loads the asset URL—the payload fires. No clicks required, just rendering the "image."

The Code: A Swing and a Miss

The vendor attempted to fix this in version 3.5.4-dev2 (commit 11115da3). They introduced a toggle allowSVGScript (default false) and a sanitization function RemoveScriptsInSVG. Let's look at the logic they added to kernel/server/serve.go and the utility package.

The logic is essentially a blacklist filter:

// Pseudo-code of the fix implementation
func RemoveScriptsInSVG(data []byte) []byte {
    doc, _ := html.Parse(bytes.NewReader(data))
    // Recursively walk the DOM
    var f func(*html.Node)
    f = func(n *html.Node) {
        if n.Type == html.ElementNode && n.Data == "script" {
            // Remove the node
            return
        }
        for c := n.FirstChild; c != nil; c = c.NextSibling {
            f(c)
        }
    }
    f(doc)
    // Re-render HTML
}

Do you see the problem? They are explicitly hunting for the <script> tag. That's it. They completely ignored the dozens of other ways to execute JavaScript in an SVG.

This is the coding equivalent of locking your front door but leaving the garage open, the windows smashed, and a "Free TV" sign on the lawn. By filtering only the script tag, they stop the most basic script kiddie, but anyone who has read the MDN documentation for SVG knows what comes next.

The Exploit: Bypassing the Patch

First, here is the standard exploit that works against unpatched versions (< 3.5.4-dev2). It's a standard XML file with a script block:

<!-- CVE-2026-23645 Standard Payload -->
<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert('Pwned via Script Tag')</script>
</svg>

Now, let's look at the bypass for the patched version. Since the code only removes <script> nodes, we can use event handlers. The onload event fires immediately when the SVG is rendered. The sanitization function leaves attributes completely untouched.

<!-- Patch Bypass Payload -->
<svg xmlns="http://www.w3.org/2000/svg" onload="alert('Pwned via Event Handler')">
  <rect width="100" height="100" fill="red" />
</svg>

Or, if we want to be fancy and use links:

<svg xmlns="http://www.w3.org/2000/svg">
  <a href="javascript:alert(1)">
    <text x="20" y="20">Click me for a prize</text>
  </a>
</svg>

If the application is running in an Electron wrapper (which SiYuan often does), and node integration is enabled or context isolation is weak, this XSS can escalate to Remote Code Execution (RCE) by accessing Node.js primitives via the window object.

The Fix: Do It Right or Don't Do It

The current mitigation provided by the vendor is a configuration flag: allowSVGScript. Ensure this is set to false, but understand that strictly speaking, the software is still vulnerable to the bypasses described above if the sanitizer isn't improved.

For the developers, the lesson is simple: Don't write your own sanitizers. You will fail. HTML and XML are too complex, and browsers are too forgiving.

The correct fix involves using a battle-tested library like DOMPurify (frontend) or a strict server-side policy that whitelists specific tags (like rect, path, circle) and attributes (fill, stroke, width), while aggressively stripping everything else. If you are stripping bad things (blacklisting), you have already lost. You must only allow known good things (whitelisting).

Official Patches

SiYuanRelease notes for v3.5.4-dev2

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 (Self-hosted)

Affected Versions Detail

Product
Affected Versions
Fixed Version
SiYuan Note
SiYuan
< 3.5.4-dev23.5.4-dev2
AttributeDetail
CVE IDCVE-2026-23645
CVSS v3.16.1 (Medium)
CWECWE-79 (Improper Neutralization of Input)
Attack VectorNetwork (Stored XSS)
ImpactSession Hijacking / Potential RCE
Fix StatusPatched (Incomplete Sanitization)

MITRE ATT&CK Mapping

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

The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.

Known Exploits & Detection

GitHub Issue #16844Original report detailing the SVG upload lack of sanitization
NucleiDetection Template Available

Vulnerability Timeline

Vulnerability reported via GitHub Issue
2026-01-16
Patch released in v3.5.4-dev2
2026-01-16
GHSA Advisory Published
2026-01-16

References & Sources

  • [1]GHSA Advisory
  • [2]PortSwigger: Stored XSS

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.