# SiYuan vulnerable to reflected XSS via SVG namespace prefix bypass in SanitizeSVG (getDynamicIcon, unauthenticated)

Unauthenticated SiYuan getDynamicIcon endpoint reflects content into SVG; namespace-prefixed <x:script> bypasses SanitizeSVG blocklist, executing JS at server origin.

- Payout: Research
- Severity: high
- Program: SiYuan
- Found by: fg0x0
- Disclosed: 2026-04-01
- CVE: CVE-2026-34605
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-73g7-86qr-jrg3
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-73g7-86qr-jrg3
- Tags: area:web domain:web impact:ato impact:data-leak kind:research platform:ghsa sev:high tech:go tech:rest technique:incomplete-fix technique:source-review vuln:xss

**Target:** /api/icon/getDynamicIcon endpoint (type=8 SVG text rendering)
**Root cause:** SanitizeSVG compares the full qualified tag name from the Go HTML5 parser (e.g. "x:script") against a blocklist of bare local names, so namespace-prefixed elements pass; the SVG is then served as image/svg+xml without CSP and the browser's XML parser resolves the prefix and executes the script.
**Steps:**
1. Send `GET /api/icon/getDynamicIcon?type=8&color=red&content=%3C%2Ftext%3E%3Cx%3Ascript%20xmlns%3Ax%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3Ealert%28document.domain%29%3C%2Fx%3Ascript%3E%3Ctext%3E` to the SiYuan host on port 6806
2. Decoded content: `</text><x:script xmlns:x="http://www.w3.org/2000/svg">alert(document.domain)</x:script><text>`
3. SanitizeSVG parses with lute/html; Node.Data is `x:script`, not `script`, so the blocklist check fails to remove it
4. Response is served as image/svg+xml with no CSP; open the URL directly in a browser to trigger the alert at the SiYuan origin
**How found:** Reporter examined the SanitizeSVG fix added in v3.6.0 and noticed the blocklist compares lowercased c.Data against bare tag names; testing the lute HTML parser showed namespace-prefixed elements retain the prefix in Node.Data, so the comparison misses them.
**Exploitation:** Attacker on the same network crafts the URL and shares it with a victim; when opened, JS runs same-origin at http://<siyuan-host>:6806 and, leveraging the victim's session cookies and the permissive CORS config, can call any API endpoint to read notes, export data, or modify settings.
**Escalation:** Same-origin script execution plus Access-Control-Allow-Origin: * allows full API access with the victim's session, turning reflected XSS into data theft and settings modification.
**Insight:** When a sanitizer blocklists tag names from an HTML5 parser, test namespace-prefixed qualified names (x:script, x:iframe, x:foreignObject) — parsers keep the prefix in the tag field, so local-name comparison is required; also check whether the output is later parsed as XML/SVG where prefixes resolve.

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

**Unauthenticated GET request with namespace-prefixed script payload**

```
GET /api/icon/getDynamicIcon?type=8&color=red&content=%3C%2Ftext%3E%3Cx%3Ascript%20xmlns%3Ax%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3Ealert%28document.domain%29%3C%2Fx%3Ascript%3E%3Ctext%3E HTTP/1.1
Host: 127.0.0.1:6806
```

**Decoded malicious content parameter value**

```
</text><x:script xmlns:x="http://www.w3.org/2000/svg">alert(document.domain)</x:script><text>
```

