GHSA-56MP-4F3V-FGJ2
Vulnerability from github – Published: 2026-07-10 20:37 – Updated: 2026-07-10 20:37SiYuan v3.6.5 and earlier versions contain a stored cross-site scripting (XSS) vulnerability in the Attribute View (database) asset cell renderer that escalates to remote code execution (RCE) in the Electron desktop client. This is a neighbor-bug of CVE-2026-44588: the fix for -44588 used escapeAriaLabel() (double-escapes <), but the AV asset renderers were left using the weaker escapeAttr() (escapes only quotes) or no escaping at all.
## Vulnerability Details
The Electron renderer is configured with nodeIntegration: true and contextIsolation: false (app/electron/main.js:307), allowing any JavaScript executing in the renderer to directly access Node.js APIs including require('child_process').
Two XSS sinks exist.
### Sink 1 (Direct Stored XSS - triggers on page load)
app/src/protyle/render/av/cell.ts:1008:
text += `<span class="b3-chip av__celltext--url ariaLabel" aria-label="${escapeAttr(item.content)}" data-name="${escapeAttr(item.name)}"
data-url="${escapeAttr(item.content)}">${item.name || item.content}`;
The >${item.name || item.content}</span> portion is raw user input with zero escaping.
app/src/protyle/render/av/blockAttr.ts:93 (even worse - completely unescaped):
html += `<img loading="lazy" class="av__cellassetimg ariaLabel" aria-label="${item.content}" src="${getCompressURL(item.content)}">`;
Rendered via action.ts:860: cellElement.innerHTML = renderCell(...) results in immediate XSS on page load.
### Sink 2 (Hover-triggered XSS via aria-label round-trip)
- Same lines emit
aria-label="${escapeAttr(item.content)}"on.ariaLabelelements. escapeAttr()(util/escape.ts:14) escapes only"and'— NOT<or>.popover.ts:33global mouseover handler readsaria-labelviagetAttribute(which attribute-decodes entities).- Line 144:
showTooltip(decodeURIComponent(tip), ...)thentooltip.ts:41:messageElement.innerHTML = messageresults in XSS on hover.
### Source
app/src/protyle/render/av/asset.ts:405:addAssetLink()reads user input from a free-form<textarea>with no sanitization.- Kernel stores
MAsset.Contentraw (kernel/av/value.go:53), no server-side sanitization.
## Attack Vector
- Attacker creates a malicious note containing an Attribute View (database).
- Attacker adds an asset cell with link content:
<img src=x onerror=require('child_process').exec('calc')> - Victim opens the note for immediate RCE (Sink 1), or hovers over the cell for RCE (Sink 2).
- In a sync/collaboration scenario, the malicious note propagates to all users.
## Proof of Concept
Payload (Direct XSS) — in an AV asset cell link field, enter:
<img src=x onerror=alert(document.domain)>
For RCE in Electron desktop:
<img src=x onerror=require('child_process').exec('calc')>
### Steps to Reproduce
- Open SiYuan desktop app (v3.6.5).
- Create a new document.
- Insert an Attribute View (database):
/then select "Table". - Add a column of type "Asset".
- Click the asset cell, then "Add Link".
- In the "Link" textarea, paste:
<img src=x onerror=alert(1)> - Leave "Title" empty or fill with benign text.
- Click outside the dialog to save.
- Observe: Alert fires immediately (Sink 1). Hovering over the cell also triggers (Sink 2).
## Impact
- Remote Code Execution on victim's system via malicious note sync/import.
- Data exfiltration: attacker can read all notes, access filesystem, steal credentials.
- Persistence: malicious payload stored in
.syfiles, executes on every open.
## Suggested Fix
- Replace
escapeAttr()withescapeAriaLabel()for allaria-labelattributes in AV cell renderers. - Escape
item.nameanditem.contentwithescapeHtml()before concatenating into element text content.
Affected files: app/src/protyle/render/av/cell.ts, app/src/protyle/render/av/blockAttr.ts, app/src/protyle/render/av/asset.ts.
## Additional Context
This vulnerability is a neighbor-bug of CVE-2026-44588. The fix for -44588 correctly used escapeAriaLabel() (which double-escapes < to survive the attribute -> getAttribute -> innerHTML round-trip), but the AV asset cell renderers were left using the weaker escapeAttr() or no escaping. This is part of a pattern of incomplete fixes in SiYuan (see also CVE-2026-33066, CVE-2026-29183). The long-term fix should set ElectroncontextIsolation: true and nodeIntegration: false.
## Report Reporter (GitHub: Yunkaiwjs).
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/siyuan-note/siyuan/kernel"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.0-20260628153353-2d5d72223df4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-50551"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-10T20:37:04Z",
"nvd_published_at": "2026-06-24T22:16:47Z",
"severity": "CRITICAL"
},
"details": "SiYuan v3.6.5 and earlier versions contain a stored cross-site scripting (XSS) vulnerability in the Attribute View (database) asset cell renderer that escalates to remote code execution (RCE) in the Electron desktop client. This is a neighbor-bug of CVE-2026-44588: the fix for -44588 used `escapeAriaLabel()` (double-escapes `\u003c`), but the AV asset renderers were left using the weaker `escapeAttr()` (escapes only quotes) or no escaping at all.\n\n ## Vulnerability Details\n\n The Electron renderer is configured with `nodeIntegration: true` and `contextIsolation: false` (app/electron/main.js:307), allowing any JavaScript executing in the renderer to directly access Node.js APIs including `require(\u0027child_process\u0027)`.\n\n Two XSS sinks exist.\n\n ### Sink 1 (Direct Stored XSS - triggers on page load)\n\n `app/src/protyle/render/av/cell.ts:1008`:\n\n text += `\u003cspan class=\"b3-chip av__celltext--url ariaLabel\" aria-label=\"${escapeAttr(item.content)}\" data-name=\"${escapeAttr(item.name)}\"\n data-url=\"${escapeAttr(item.content)}\"\u003e${item.name || item.content}\u003c/span\u003e`;\n\n The `\u003e${item.name || item.content}\u003c/span\u003e` portion is raw user input with zero escaping.\n\n `app/src/protyle/render/av/blockAttr.ts:93` (even worse - completely unescaped):\n\n html += `\u003cimg loading=\"lazy\" class=\"av__cellassetimg ariaLabel\" aria-label=\"${item.content}\" src=\"${getCompressURL(item.content)}\"\u003e`;\n\n Rendered via `action.ts:860`: `cellElement.innerHTML = renderCell(...)` results in immediate XSS on page load.\n\n ### Sink 2 (Hover-triggered XSS via aria-label round-trip)\n\n - Same lines emit `aria-label=\"${escapeAttr(item.content)}\"` on `.ariaLabel` elements.\n - `escapeAttr()` (util/escape.ts:14) escapes only `\"` and `\u0027` \u2014 NOT `\u003c` or `\u003e`.\n - `popover.ts:33` global mouseover handler reads `aria-label` via `getAttribute` (which attribute-decodes entities).\n - Line 144: `showTooltip(decodeURIComponent(tip), ...)` then `tooltip.ts:41`: `messageElement.innerHTML = message` results in XSS on hover.\n\n ### Source\n\n - `app/src/protyle/render/av/asset.ts:405`: `addAssetLink()` reads user input from a free-form `\u003ctextarea\u003e` with no sanitization.\n - Kernel stores `MAsset.Content` raw (kernel/av/value.go:53), no server-side sanitization.\n\n ## Attack Vector\n\n 1. Attacker creates a malicious note containing an Attribute View (database).\n 2. Attacker adds an asset cell with link content: `\u003cimg src=x onerror=require(\u0027child_process\u0027).exec(\u0027calc\u0027)\u003e`\n 3. Victim opens the note for immediate RCE (Sink 1), or hovers over the cell for RCE (Sink 2).\n 4. In a sync/collaboration scenario, the malicious note propagates to all users.\n\n ## Proof of Concept\n\n Payload (Direct XSS) \u2014 in an AV asset cell link field, enter:\n\n \u003cimg src=x onerror=alert(document.domain)\u003e\n\n For RCE in Electron desktop:\n\n \u003cimg src=x onerror=require(\u0027child_process\u0027).exec(\u0027calc\u0027)\u003e\n\n ### Steps to Reproduce\n\n 1. Open SiYuan desktop app (v3.6.5).\n 2. Create a new document.\n 3. Insert an Attribute View (database): `/` then select \"Table\".\n 4. Add a column of type \"Asset\".\n 5. Click the asset cell, then \"Add Link\".\n 6. In the \"Link\" textarea, paste: `\u003cimg src=x onerror=alert(1)\u003e`\n 7. Leave \"Title\" empty or fill with benign text.\n 8. Click outside the dialog to save.\n 9. Observe: Alert fires immediately (Sink 1). Hovering over the cell also triggers (Sink 2).\n\n ## Impact\n\n - Remote Code Execution on victim\u0027s system via malicious note sync/import.\n - Data exfiltration: attacker can read all notes, access filesystem, steal credentials.\n - Persistence: malicious payload stored in `.sy` files, executes on every open.\n\n ## Suggested Fix\n\n 1. Replace `escapeAttr()` with `escapeAriaLabel()` for all `aria-label` attributes in AV cell renderers.\n 2. Escape `item.name` and `item.content` with `escapeHtml()` before concatenating into element text content.\n\n Affected files: `app/src/protyle/render/av/cell.ts`, `app/src/protyle/render/av/blockAttr.ts`, `app/src/protyle/render/av/asset.ts`.\n\n ## Additional Context\n\n This vulnerability is a neighbor-bug of CVE-2026-44588. The fix for -44588 correctly used `escapeAriaLabel()` (which double-escapes `\u003c` to survive the attribute -\u003e `getAttribute` -\u003e `innerHTML` round-trip), but the AV asset cell renderers were left using the weaker `escapeAttr()` or no escaping. This is part of a pattern of incomplete fixes in SiYuan (see also CVE-2026-33066, CVE-2026-29183). The long-term fix should set Electron`contextIsolation: true` and `nodeIntegration: false`.\n\n ## Report\n Reporter (GitHub: Yunkaiwjs).",
"id": "GHSA-56mp-4f3v-fgj2",
"modified": "2026-07-10T20:37:04Z",
"published": "2026-07-10T20:37:04Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-56mp-4f3v-fgj2"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-50551"
},
{
"type": "PACKAGE",
"url": "https://github.com/siyuan-note/siyuan"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "SiYuan: Stored XSS to RCE via Unsanitized Attribute View Asset Cell Content"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.