GHSA-56MP-4F3V-FGJ2

Vulnerability from github – Published: 2026-07-10 20:37 – Updated: 2026-07-10 20:37
VLAI
Summary
SiYuan: Stored XSS to RCE via Unsanitized Attribute View Asset Cell Content
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 <), 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 .ariaLabel elements.
  • escapeAttr() (util/escape.ts:14) escapes only " and ' — NOT < or >.
  • popover.ts:33 global mouseover handler reads aria-label via getAttribute (which attribute-decodes entities).
  • Line 144: showTooltip(decodeURIComponent(tip), ...) then tooltip.ts:41: messageElement.innerHTML = message results 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.Content raw (kernel/av/value.go:53), no server-side sanitization.

## Attack Vector

  1. Attacker creates a malicious note containing an Attribute View (database).
  2. Attacker adds an asset cell with link content: <img src=x onerror=require('child_process').exec('calc')>
  3. Victim opens the note for immediate RCE (Sink 1), or hovers over the cell for RCE (Sink 2).
  4. 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

  1. Open SiYuan desktop app (v3.6.5).
  2. Create a new document.
  3. Insert an Attribute View (database): / then select "Table".
  4. Add a column of type "Asset".
  5. Click the asset cell, then "Add Link".
  6. In the "Link" textarea, paste: <img src=x onerror=alert(1)>
  7. Leave "Title" empty or fill with benign text.
  8. Click outside the dialog to save.
  9. 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 .sy files, executes on every open.

## Suggested Fix

  1. Replace escapeAttr() with escapeAriaLabel() for all aria-label attributes in AV cell renderers.
  2. Escape item.name and item.content with escapeHtml() 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).

Show details on source website

{
  "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"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

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.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…