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 log

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