GHSA-mvpm-v6q4-m2pf: SiYuan has Stored XSS to RCE via Unsanitized Bazaar Package Metadata
Stored XSS to RCE via Unsanitized Bazaar Package MetadataSummarySiYuan's Bazaar (community marketplace) renders package metadata fields (`displayName`, `description`) using template literals without HTML escaping. A malicious package author can inject arbitrary HTML/JavaScript into these fields, which executes automatically when any user browses the Bazaar page. Because SiYuan's Electron configuration enables `nodeIntegration: true` with `contextIsolation: false`, this XSS escalates directly to full Remote Code Execution on the victim's operating system — with zero user interaction beyond opening the marketplace tab.Affected Component**Metadata rendering**: `app/src/config/bazaar.ts:275-277`**Electron config**: `app/electron/main.js:422-426` (`nodeIntegration: true`, `contextIsolation: false`)Affected VersionsSiYuan <= 3.5.9Severity**Critical** — CVSS 9.6 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)CWE-79: Improper Neutralization of Input During Web Page Generation (Stored XSS)Vulnerable CodeIn `app/src/config/bazaar.ts:275-277`, package metadata is injected directly into HTML templates without escaping:The inconsistency is notable: the `title` attribute is escaped via `escapeAttr()`, but the actual rendered text content is not — indicating the risk was partially recognized but incompletely mitigated.The Electron renderer at `app/electron/main.js:422-426` is configured with:This means any JavaScript executing in the renderer process has direct access to Node.js APIs including `require('child_process')`, `require('fs')`, and `require('os')`.Proof of ConceptStep 1: Create a malicious plugin manifestCreate a GitHub repository with a valid SiYuan plugin structure. In `plugin.json`:Step 2: Submit to BazaarSubmit the repository to the SiYuan Bazaar community marketplace via the standard contribution process (pull request to the bazaar index repository).Step 3: Zero-click RCEWhen **any** SiYuan desktop user navigates to **Settings > Bazaar > Plugins**, the package listing renders the malicious `displayName`. The `<img src=x>` tag fails to load, firing the `onerror` handler, which calls `require('child_process').exec('calc.exe')`.**No click is required.** The payload executes the moment the Bazaar page loads and the package card is rendered in the DOM.Escalation: Reverse shellEscalation: Data exfiltration (API token theft)Escalation: Silent persistence (Windows)Attack ScenarioAttacker creates a legitimate-looking GitHub repository with a SiYuan plugin/theme/template.Attacker submits it to the SiYuan Bazaar via the standard community contribution process.The `plugin.json` manifest contains an XSS payload in the `displayName` or `description` field.When **any** SiYuan desktop user opens the Bazaar tab, the malicious package card renders the unescaped metadata.The injected `<img onerror>` (or `<svg onload>`, `<details ontoggle>`, etc.) fires automatically.JavaScript executes in the Electron renderer with full Node.js access (`nodeIntegration: true`).The attacker achieves arbitrary OS command execution — reverse shell, data exfiltration, persistence, ransomware, etc.**The user does not need to install, click, or interact with the malicious package in any way.** Browsing the marketplace is sufficient.Impact**Full remote code execution** on any SiYuan desktop user who browses the Bazaar**Zero-click** — payload fires on page load, no interaction required**Supply-chain attack** — targets the entire SiYuan user community via the official marketplaceCan steal API tokens, session cookies, SSH keys, browser credentials, and arbitrary filesCan install persistent backdoors, scheduled tasks, or ransomwareAffects all platforms: Windows, macOS, LinuxSuggested Fix1. Escape all package metadata in template rendering (`bazaar.ts`)2. Server-side sanitization in the Bazaar index pipelineSanitize metadata fields at the Bazaar index build stage so malicious content never reaches clients:3. Long-term: Harden Electron configuration
CVSS v4.0: 5.3