GitHub Security Advisory: GHSA-v3mg-9v85-fcm7
Package: siyuan (GO)
Severity: MODERATE (CVSS 0.0)
CWE: CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Vulnerable versions: <= 0.0.0[PII_REDACTED]4916-fd6526133bb3
Patched in: unpatched

Summary: SiYuan Vulnerable to Remote Code Execution via Malicious Bazaar Package — Marketplace XSS

Details:
# Remote Code Execution via Malicious Bazaar Package — Marketplace XSS

## Summary

SiYuan's Bazaar (community marketplace) renders plugin/theme/template metadata and README content without sanitization. A malicious package author can achieve RCE on any user who browses the Bazaar by:

1. **Package metadata XSS (zero-click):** Package `displayName` and `description` fields are injected directly into HTML via template literals without escaping. Just loading the Bazaar page triggers execution.
2. **README XSS (one-click):** The `renderREADME` function uses `lute.New()` without `SetSanitize(true)`, so raw HTML in the README passes through to `innerHTML` unsanitized.

Both vectors execute in Electron's renderer with `nodeIntegration: true` and `contextIsolation: false`, giving full OS command execution.

## Affected Component

- **Metadata rendering:** `app/src/config/bazaar.ts:275-277`
- **README rendering (backend):** `kernel/bazaar/package.go:635-645` (`renderREADME`)
- **README rendering (frontend):** `app/src/config/bazaar.ts:607` (`innerHTML`)
- **Electron config:** `app/electron/main.js:422-426` (`nodeIntegration: true`)
- **Version:** SiYuan <= 3.5.9

## Vulnerable Code

### Vector 1: Package metadata — no HTML escaping (bazaar.ts:275-277)

```typescript
// Package name injected directly into HTML template — NO escaping
${item.preferredName}${item.preferredName !== item.name
    ? ` <span class="ft__on-surface ft__smaller">${item.name}</span>` : ""}

// Package description injected directly — NO escaping
<div class="b3-card__desc" title="${escapeAttr(item.preferredDesc) || ""}">
    ${item.preferredDesc || ""}  <!-- UNESCAPED HTML -->
</div>
```

Note: The `title` attribute uses `escapeAttr()`, but the actual text content does not — inconsistent escaping.

### Vector 2: README rendering — no Lute sanitization (package.go:635-645)

```go
func renderREADME(repoURL string, mdData []byte) (ret string, err error) {
    luteEngine := lute.New()  // Fresh Lute instance — SetSanitize NOT called
    luteEngine.SetSoftBreak2HardBreak(false)
    luteEngine.SetCodeSyntaxHighlight(false)
    linkBase := "https://cdn.jsdelivr.net/gh/" + ...
    luteEngine.SetLinkBase(linkBase)
    ret = luteEngine.Md2HTML(string(mdData))  // Raw HTML in markdown preserved
    return
}
```

Compare with the SiYuan note renderer in `kernel/util/lute.go:81`:
```go
luteEngine.SetSanitize(true)  // Notes ARE sanitized — but README is NOT
```

### Frontend innerHTML injection (bazaar.ts:607)

```typescript
fetchPost("/api/bazaar/getBazaarPackageREADME", {...}, response => {
    mdElement.innerHTML = response.data.html;  // Unsanitized HTML from README
});
```

## Proof of Concept

### Vector 1: Malicious package manifest (zero-click RCE)

A malicious `plugin.json` (or `theme.json`, `template.json`):

```json
{
    "name": "helpful-plugin",
    "displayName": {
        "default": "Helpful Plugin<img src=x onerror=\"require('child_process').exec('calc.exe')\">"
    },
    "descrip