# SiYuan: Stored XSS in Attribute View Gallery/Kanban Cover Rendering Allows Arbitrary Command Execution in Desktop Client

Stored XSS via unescaped coverURL in SiYuan Attribute View Gallery/Kanban reaches Node.js APIs in the Electron desktop client, yielding OS command execution.

- Payout: Research
- Severity: critical
- Program: SiYuan
- Found by: ngocnn97
- Disclosed: 2026-03-31
- CVE: CVE-2026-34448
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-rx4h-526q-4458
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-rx4h-526q-4458
- Tags: area:os domain:desktop impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:electron technique:source-review vuln:rce vuln:xss

**Target:** Electron desktop client Attribute View Gallery/Kanban cover image rendering
**Root cause:** IsPossiblyImage accepts arbitrary http(s) URLs without extension checks, and the front-end injects coverURL unescaped into an <img src> attribute. The Electron renderer runs with nodeIntegration enabled and contextIsolation/webSecurity disabled.
**Steps:**
1. Create an Attribute View database with an mAsset column and a row.
2. Add a legitimate image to the mAsset field so it is stored as type image.
3. Switch view to Gallery or Kanban and set Cover From -> Asset Field to the mAsset column.
4. Edit the image asset entry, replacing its link with `https://example.com/" onerror="require('child_process').exec('calc')`.
5. Save and reopen/refresh the Gallery or Kanban view; the injected onerror fires when the image fails to load.
**How found:** Code review of the cover-image flow: noticed IsPossiblyImage accepts arbitrary http(s) URLs and coverURL is interpolated into the img src attribute without escaping.
**Exploitation:** Attacker stores the payload in a shared database asset field; when a victim opens the Gallery/Kanban view with Cover From -> Asset Field, the broken-out onerror handler executes in the Electron renderer with Node.js access, running OS commands as the victim.
**Escalation:** Browser-context XSS escalated to OS command execution because the Electron renderer has nodeIntegration true and contextIsolation/webSecurity disabled.
**Insight:** Attribute View cover/asset fields that accept arbitrary URLs are a stored-XSS sink; in Electron apps check whether the renderer keeps nodeIntegration on, which turns a broken-out img attribute into command execution.

## Proof of concept (copied exactly from the original report)

**XSS payload in mAsset link field**

```
https://example.com/" onerror="require('child_process').exec('calc')
```

