GitHub Advisoryadvisory · Go

GHSA-pmc9-f5qr-2pcr SiYuan has a SVG Sanitizer Bypass via Whitespace in `javascript:` URI — Unauthenticated XSS

MEDIUM Published 2026-03-10 Modified 2026-03-10 Official page ↗
Aliases
CVE-2026-31809
Ecosystem
Go
Packages
github.com/siyuan-note/siyuan/kernel
Severity
Moderate
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N
Weaknesses
CWE-79
Reviewed
2026-03-10

Affected packages

• Go github.com/siyuan-note/siyuan/kernel: < 0.0.0-20260310025236-297bd526708f → fixed in 0.0.0-20260310025236-297bd526708f

Description

SVG Sanitizer Bypass via Whitespace in `javascript:` URI — Unauthenticated XSS Summary SiYuan's SVG sanitizer (`SanitizeSVG`) checks `href` attributes for the `javascript:` prefix using `strings.HasPrefix()`. However, inserting ASCII tab (`&#9;`), newline (`&#10;`), or carriage return (`&#13;`) characters inside the `javascript:` string bypasses this prefix check. Browsers strip these characters per the WHATWG URL specification before parsing the URL scheme, so the JavaScript still executes. This allows an attacker to inject executable JavaScript into the unauthenticated `/api/icon/getDynamicIcon` endpoint, creating a reflected XSS. This is a second bypass of the fix for CVE-2026-29183 (fixed in v3.5.9), distinct from the `` element bypass. Affected Component • File: `kernel/util/misc.go` • Function: `SanitizeSVG()` (lines 234-319) • Specific check: Line 271 — `strings.HasPrefix(val, "javascript:")` • Endpoint: `GET /api/icon/getDynamicIcon?type=8&content=...` (unauthenticated) • Version: SiYuan Click me&color=blue ``` Vector 2: Newline character (`&#10;`) ``` GET /api/icon/getDynamicIcon?type=8&content=Click me&color=blue ``` Vector 3: Carriage return (`&#13;`) ``` GET /api/icon/getDynamicIcon?type=8&content=Click me&color=blue ``` Vector 4: Multiple whitespace characters ``` GET /api/icon/getDynamicIcon?type=8&content=Click me&color=blue ``` Processing trace 1. Input: `` 2. html.Parse: Decodes entity → attribute value = `java\tscript:alert(document.domain)` 3. Sanitizer: `TrimSpace(ToLower(val))` = `java\tscript:alert(document.domain)` (tab preserved in middle) 4. HasPrefix check: `"java\tscript:..."` does NOT start with `"javascript:"` → passes through 5. html.Render: Outputs literal tab character in href (tabs are not HTML-special) 6. Browser URL parser: Strips tab per WHATWG URL spec → `javascript:alert(document.domain)` 7. User clicks link → JavaScript executes Attack Scenario Same as CVE-2026-29183 / advisory #01: 1. Attacker crafts a malicious `getDynamicIcon` URL 2. Victim navigates to the URL (or is redirected) 3. SVG renders with `Content-Type: image/svg+xml` 4. Victim clicks the text link in the SVG 5. JavaScript executes in SiYuan's origin 6. Attacker steals session cookies, API tokens, or makes authenticated API calls Impact • Severity: CRITICAL (CVSS ~9.1) • Type: CWE-79 (Improper Neutralization of Input During Web Page Generation) • Unauthenticated reflected XSS via SVG injection • Executes in the SiYuan application origin • Bypasses the fix for CVE-2026-29183 • Independent of the `` element bypass (advisory #01) — different root cause Suggested Fix Replace the simple `HasPrefix` check with whitespace-stripped comparison: ```go // Strip ASCII tab, newline, CR before checking for javascript: prefix cleaned := strings.Map(func(r rune) rune { if r == '\t' || r == '\n' || r == '\r' { return -1 // Remove character } return r }, val) if key == "href" || key == "xlink:href" || key == "xlinkhref" { if strings.HasPrefix(cleaned, "javascript:") { continue } if strings.HasPrefix(cleaned, "data:") { if strings.Contains(cleaned, "text/html") || strings.Contains(cleaned, "image/svg+xml") || strings.Contains(cleaned, "application/xhtml+xml") { continue } } } ``` This should also be applied to the `data:` URI check, as the same whitespace bypass could potentially affect it.

Related

Alias · CVE (1)

  • CVE-2026-31809 SiYuan is a personal knowledge management system. vulnerability

Weakness · CWE (1)

  • CWE-79 Improper Neutralization of Input During Web Page Generation weakness

References