high · 7.5CVE-2026-54066Jul 10, 2026

CVE-2026-54066: SiYuan Unauthenticated Path Traversal via Double URL Encoding in /assets/ (Publish Mode)

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

SiYuan's publish-mode file server double-decodes percent-encoded path segments, letting anyone on the network read sensitive workspace files like API tokens and full notebook databases without any…

Packagegithub.com/siyuan-note/siyuan/kernel
Ecosystemgo
Affected< 0.0.0-20260628153353-2d5d72223df4
Fixed in0.0.0-20260628153353-2d5d72223df4

The problem

SiYuan's publish endpoint (port 6808, anonymous by design) registers a GET /assets/*path route. The GetAssetAbsPath function in kernel/model/assets.go runs a second url.PathUnescape call as a compatibility fallback when the first path lookup fails. This re-introduces the exact double-decode primitive that the CVE-2026-41894 patch eliminated on the /export/ route.

A request for /assets/%252e%252e/%252e%252e/conf/conf.json arrives with Gin already having decoded %25 to %, leaving literal %2e%2e strings in the path param. The fallback then decodes those to .., and filepath.Join resolves the final path to WorkspaceDir/conf/conf.json.

Because CheckAbsPathAccessableByPublishAccess only checks IsSubPath(DataDir, absPath) and returns true for anything outside DataDir (without ever calling IsSensitivePath), the file is served without restriction.

Proof of concept

A working proof-of-concept for CVE-2026-54066 in github.com/siyuan-note/siyuan/kernel, with the exact payload below.

bash
curl -i "http://victim:6808/assets/%252e%252e/%252e%252e/conf/conf.json"

Three independent flaws chain together. First, the url.PathUnescape fallback in assets.go:548 decodes %2e%2e to .. on the second pass, after Go's HTTP layer has already done the first decode. Second, CheckAbsPathAccessableByPublishAccess (publish_access.go:288) returns true for any resolved path outside DataDir, even paths still inside WorkspaceDir such as conf/conf.json and temp/*.db.

Third, the IsSensitivePath() denylist that the CVE-2026-41894 patch wired into the /export/ handler was never applied to the /assets/ handler at all.

The fix (commit 2d5d72223df4, released as v3.7.0) removes the url.PathUnescape fallback from GetAssetAbsPath, matching the approach taken on /export/. It also tightens CheckAbsPathAccessableByPublishAccess to require containment within DataDir and always invoke IsSensitivePath(), closing the fall-through path.

CWE-23 (Relative Path Traversal) and CWE-1188 (Insecure Default: publish mode on by default) both apply.

The fix

Upgrade SiYuan to v3.7.0 or later. The patch removes the redundant url.PathUnescape fallback in kernel/model/assets.go, tightens the publish-access gate to require paths inside DataDir, and ensures IsSensitivePath() is always evaluated for /assets/ requests.

If upgrading immediately is not possible, disable publish mode (conf.publish.enable = false) until the update is applied.

Reported by 0x_Akoko.

References: [1][2][3]

Related research