Every CVE Search/ Get API key

CVE-2026-62204

SiYuan before v3.7.4 Plugin Overwrite via Bazaar Install

5.9 MediumFix available

SiYuan versions before v3.7.4 fail to validate that packageName matches the downloaded package content in bazaar install endpoints. Attackers with same-origin access can overwrite existing trusted plugins by supplying mismatched packageName and repoURL parameters, achieving persistence across application restarts.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
High
Privileges needed
Low
User interaction
Passive

What an attacker can affect

Confidentiality
Low
Integrity
High
Availability
Low

Exploit likelihood

0.09%Unchanged since Aug 23, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.9

SiYuan before v3.7.4 Plugin Overwrite via Bazaar Install

SiYuan versions before v3.7.4 fail to validate that packageName matches the downloaded package content in bazaar install endpoints. Attackers with same-origin access can overwrite existing trusted plugins by supplying mismatched packageName and repoURL parameters, achieving persistence across application restarts.

Record
CVE-2026-62204
CVSS 4.0
5.9 MediumCVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L
CVSS 3.1
6.6 MediumCVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L
Weakness
CWE-345
Updated
2026-11-02
EUVDEUVD-2026-64270 5.9

SiYuan versions before v3.7.4 fail to validate that packageName matches the downloaded package content in bazaar install endpoints. Attackers with same-origin access can overwrite existing trusted plugins by supplying mismatched packageName and repoURL parameters, achieving persistence across application restarts.

Record
EUVD-2026-64270
CVSS 4.0
5.9 MediumCVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L
Updated
2026-11-02
GitHub AdvisoriesGHSA-989h-w3q9-3m2f 5.9

Duplicate Advisory: SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-rpx2-p6hp-x5gj. This link is maintained to preserve external references.

Original Description

SiYuan versions before v3.7.4 fail to validate that packageName matches the downloaded package content in bazaar install endpoints. Attackers with same-origin access can overwrite existing trusted plugins by supplying mismatched packageName and repoURL parameters, achieving persistence across application restarts.

Record
GHSA-989h-w3q9-3m2f
CVSS 4.0
5.9 MediumCVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
6.6 MediumCVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L
Weakness
CWE-345
Updated
2026-11-02
GitHub AdvisoriesGHSA-rpx2-p6hp-x5gj 6.6

SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin

Summary

installBazaarPlugin (and the equivalent endpoints for widgets/icons/templates/themes) accept packageName, repoURL, and repoHash as independent request fields with no validation that they refer to the same package. packageName alone determines the install destination; repoURL/repoHash alone determine what content gets downloaded and written there. A request can name an already-installed, already-enabled plugin as packageName while pointing repoURL/ repoHash at different, attacker-influenced content, silently overwriting the existing plugin's files.

Affected code

kernel/api/bazaar.go:

util.BindJsonArg("packageName", &packageName, true, true)
util.BindJsonArg("repoURL", &repoURL, true, true)
util.BindJsonArg("repoHash", &repoHash, true, true)
err := model.InstallBazaarPackage("plugins", repoURL, repoHash, packageName, 0)

kernel/model/bazaar.go:

func getPackageInstallPath(pkgType, packageName string) (string, string, error) {
    case "plugins":
        return filepath.Join(util.DataDir, "plugins", packageName), "plugin.json", nil

kernel/bazaar/install.go:

func installPackage(data []byte, installPath string) (err error) {
    ...
    if err = filelock.Copy(srcPath, installPath); err != nil { return }
    return
}

installPath is derived solely from packageName; the downloaded data is derived solely from repoURL/repoHash; nothing cross-validates them, and installPackage performs no check for whether installPath already contains an existing package before copying into it. The same pattern applies to the widget/icon/template/theme install endpoints, which share getPackageInstallPath.

This does not involve path traversal — installPath always resolves inside DataDir/plugins/ (or the corresponding directory for other package types), so this is not an instance of "arbitrary file write outside the workspace path" (excluded by this project's SECURITY.md). The issue is that the write lands in the wrong, existing location within the intended directory, not outside it — an authorization/identity-verification gap, not a path traversal.

Reachability / why this is not trivially remote

The bazaar endpoints are gated by model.CheckAuth, model.CheckAdminRole, model.CheckReadonly. When no lock-screen password is configured (the common desktop default), CheckAuth still enforces a same-origin check:

if !localhost || ... || ("" != origin && !util.IsLocalOrigin(origin)) || ... {
    // reject with 401
}

I verified IsLocalOrigin/IsLocalHostname correctly parse the Origin header via url.Parse and check exact hostname / loopback IP:

func IsLocalOrigin(origin string) bool {
    if u, err := url.Parse(origin); err == nil {
        return IsLocalHostname(u.Hostname())
    }
    return false
}
func IsLocalHostname(hostname string) bool {
    if "localhost" == hostname || strings.HasSuffix(hostname, ".localhost") {
        return true
    }
    if ip := net.ParseIP(hostname); nil != ip {
        return ip.IsLoopback()
    }
    return false
}

This was tested against common bypass patterns (subdomain-suffix tricks like localhost.attacker.com, and Origin: null as sent by sandboxed iframes) and found no bypass — this is a correctly-implemented check. This means a purely remote attacker (e.g. a malicious website) cannot invoke this endpoint via CSRF; a request must originate from SiYuan's own renderer origin.

Realistic impact

Given SiYuan runs as an Electron app with nodeIntegration: true (per prior advisories in this repository), a same-origin script-execution primitive (e.g. an XSS bug in the renderer, of which several have been fixed in this repository recently) is typically already sufficient for direct code execution. This bug's marginal value is persistence: a transient XSS-driven compromise can use this endpoint to overwrite an existing, trusted, already-enabled plugin's index.js, converting a…

Record
GHSA-rpx2-p6hp-x5gj
CVSS 3.1
6.6 MediumCVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L
Weakness
CWE-345, CWE-668
Updated
2026-11-02
NVD 5.9

SiYuan versions before v3.7.4 fail to validate that packageName matches the downloaded package content in bazaar install endpoints. Attackers with same-origin access can overwrite existing trusted plugins by supplying mismatched packageName and repoURL parameters, achieving persistence across application restarts.

Record
CVE-2026-62204
CVSS 4.0
5.9 MediumCVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
6.6 MediumCVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L
Weakness
CWE-345
Updated
2026-11-02
OSVGHSA-989h-w3q9-3m2f 5.9

Duplicate Advisory: SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-rpx2-p6hp-x5gj. This link is maintained to preserve external references.

Original Description

SiYuan versions before v3.7.4 fail to validate that packageName matches the downloaded package content in bazaar install endpoints. Attackers with same-origin access can overwrite existing trusted plugins by supplying mismatched packageName and repoURL parameters, achieving persistence across application restarts.

Record
GHSA-989h-w3q9-3m2f
CVSS 4.0
5.9 MediumCVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
6.6 MediumCVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L
Weakness
CWE-345
Updated
2026-11-02
OSVGHSA-rpx2-p6hp-x5gj 6.6

SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin

Summary

installBazaarPlugin (and the equivalent endpoints for widgets/icons/templates/themes) accept packageName, repoURL, and repoHash as independent request fields with no validation that they refer to the same package. packageName alone determines the install destination; repoURL/repoHash alone determine what content gets downloaded and written there. A request can name an already-installed, already-enabled plugin as packageName while pointing repoURL/ repoHash at different, attacker-influenced content, silently overwriting the existing plugin's files.

Affected code

kernel/api/bazaar.go:

util.BindJsonArg("packageName", &packageName, true, true)
util.BindJsonArg("repoURL", &repoURL, true, true)
util.BindJsonArg("repoHash", &repoHash, true, true)
err := model.InstallBazaarPackage("plugins", repoURL, repoHash, packageName, 0)

kernel/model/bazaar.go:

func getPackageInstallPath(pkgType, packageName string) (string, string, error) {
    case "plugins":
        return filepath.Join(util.DataDir, "plugins", packageName), "plugin.json", nil

kernel/bazaar/install.go:

func installPackage(data []byte, installPath string) (err error) {
    ...
    if err = filelock.Copy(srcPath, installPath); err != nil { return }
    return
}

installPath is derived solely from packageName; the downloaded data is derived solely from repoURL/repoHash; nothing cross-validates them, and installPackage performs no check for whether installPath already contains an existing package before copying into it. The same pattern applies to the widget/icon/template/theme install endpoints, which share getPackageInstallPath.

This does not involve path traversal — installPath always resolves inside DataDir/plugins/ (or the corresponding directory for other package types), so this is not an instance of "arbitrary file write outside the workspace path" (excluded by this project's SECURITY.md). The issue is that the write lands in the wrong, existing location within the intended directory, not outside it — an authorization/identity-verification gap, not a path traversal.

Reachability / why this is not trivially remote

The bazaar endpoints are gated by model.CheckAuth, model.CheckAdminRole, model.CheckReadonly. When no lock-screen password is configured (the common desktop default), CheckAuth still enforces a same-origin check:

if !localhost || ... || ("" != origin && !util.IsLocalOrigin(origin)) || ... {
    // reject with 401
}

I verified IsLocalOrigin/IsLocalHostname correctly parse the Origin header via url.Parse and check exact hostname / loopback IP:

func IsLocalOrigin(origin string) bool {
    if u, err := url.Parse(origin); err == nil {
        return IsLocalHostname(u.Hostname())
    }
    return false
}
func IsLocalHostname(hostname string) bool {
    if "localhost" == hostname || strings.HasSuffix(hostname, ".localhost") {
        return true
    }
    if ip := net.ParseIP(hostname); nil != ip {
        return ip.IsLoopback()
    }
    return false
}

This was tested against common bypass patterns (subdomain-suffix tricks like localhost.attacker.com, and Origin: null as sent by sandboxed iframes) and found no bypass — this is a correctly-implemented check. This means a purely remote attacker (e.g. a malicious website) cannot invoke this endpoint via CSRF; a request must originate from SiYuan's own renderer origin.

Realistic impact

Given SiYuan runs as an Electron app with nodeIntegration: true (per prior advisories in this repository), a same-origin script-execution primitive (e.g. an XSS bug in the renderer, of which several have been fixed in this repository recently) is typically already sufficient for direct code execution. This bug's marginal value is persistence: a transient XSS-driven compromise can use this endpoint to overwrite an existing, trusted, already-enabled plugin's index.js, converting a…

Record
GHSA-rpx2-p6hp-x5gj
CVSS 3.1
6.6 MediumCVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L
Weakness
CWE-345, CWE-668
Updated
2026-11-06
OSVGO-2026-6834 No score

SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin in github.com/siyuan-note/siyuan/kernel

SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin in github.com/siyuan-note/siyuan/kernel

Record
GO-2026-6834
Updated
2026-11-06

Affected products

Affected packages

Affected packages
PackageAffectedFixed
github.com/siyuan-note/siyuan/kernel
Go
before 0.0.0-20260808010544-26ccb40d390e
0.0.0-20260808010544-26ccb40d390e

History

History
RecordedChangedSources
affected, aliases, facts, referencesOSV
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-62204 is a medium vulnerability (CVSS 5.9) in Siyuan-note Siyuan, published August 22, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.09%, the 0th percentile. Fixed in github.com/siyuan-note/siyuan/kernel 0.0.0-20260808010544-26ccb40d390e.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open