GHSA-gw25-m53r-qh88: Path Traversal
Summary SiYuan's /export/ file handler was hardened against export disclosure (issue #12213) by adding an IsSubPath(exportBaseDir, fullPath) check and an IsSensitivePath() check in commit bb481e1. These guards were added only to the main branch of the handler. The handler begins with a short-circuit branch: go if strings.HasPrefix(c.Request.URL.Path, "/export/temp/") { c.File(filepath.Join(util.TempDir, c.Request.URL.Path)) return } This branch joins the broader util.TempDir with the raw, percent-decoded request path and serves it with neither IsSubPath nor IsSensitivePath. An authenticated request to /export/temp/%2e%2e/.../etc/passwd traverses out of TempDir and reads arbitrary files - exactly the sensitive-file disclosure the patch intended to prevent. Present in the latest master.
Affected - From commit bb481e1 (the hardening) through the latest master. - Requires SiYuan access authorization (model.CheckAuth) - but the patch's stated goal is to deny sensitive-file export even to authorized callers.
Root cause kernel/server/serve.go serveExport(): the main branch has IsSubPath + IsSensitivePath; the /export/temp/ short-circuit branch (above it) has neither and uses util.TempDir as its root. c.Request.URL.Path is percent-decoded by net/http, so %2e%2e becomes .. and filepath.Join collapses it.
Incomplete-fix lineage - Export disclosure (issue #12213; CVE-2026-30869) -> fix bb481e1 / d68bd5a (GHSA-6865-qjcf-286f): guards on the main branch + IsSensitivePath extended to .db/.log. - Follow-up CVE-2026-41894 (GHSA-hjh7-r5w8-5872) in the same /export path family. - The /export/temp/ short-circuit branch was never covered by the guards (this report).
Proof of concept (benign) 1. Authenticate (access auth code). 2. GET /export/<sensitive> (main branch) -> 401/403 (guards work). 3. GET /export/temp/%2e%2e/%2e%2e/.../tmp/<planted-marker> (or /etc/hostname) -> 200 + file content, demonstrating the unguarded traversal. The PoC reads only a planted marker / /etc/hostname; no credentials.
Impact Authenticated arbitrary file read bypassing the sensitive-file protection: /etc/passwd, ~/.ssh/, SiYuan .db workspace data, .log.
Remediation - Apply IsSubPath + IsSensitivePath to the /export/temp/ branch (or restrict its root to TempDir/temp with an IsSubPath check). - filepath.Clean the request path and reject ... - Merge both branches into one guarded file-serving function.
References - Hardening advisory: https://github.com/siyuan-note/siyuan/security/advisories/GHSA-6865-qjcf-286f (commit d68bd5a); issue #12213. - CVE chain: CVE-2026-30869 -> CVE-2026-41894 (GHSA-hjh7-r5w8-5872). - serve.go guards commit: https://github.com/siyuan-note/siyuan/commit/bb481e1290c4a34255652ede85a546504505d2a7 - Residual source (master): kernel/server/serve.go serveExport() lines 308-312.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
go/github.com/siyuan-note/siyuan/kernelto a version that resolves this vulnerability.Fixed in 0.0.0-20260510110132-b763d787d1f2 - Upgrade
Upgrade
siyuanto a version that resolves this vulnerability.Fixed in d68bd5aPatch GHSA-6865-qjcf-286f - Configuration
In serveExport(), remove the short-circuit that serves files from the raw percent-decoded c.Request.URL.Path under /export/temp/. Instead, ensure the /export/temp/ branch is guarded equivalently to the main /export/ handler: join to a safe root (TempDir/temp or export base dir) using a cleaned path (filepath.Clean) and explicitly reject '..' traversal, then enforce IsSubPath(exportBaseDir, fullPath) and IsSensitivePath checks (including the extended patterns for sensitive files such as *.db/*.log as described in the hardening).
siyuan kernel/server/serve.go (serveExport) Export temp path handling guards = Apply IsSubPath + IsSensitivePath to the /export/temp/ branch (or restrict its root to TempDir/temp) and reject path traversal via filepath.Clean and rejecting '..'
Event History
Frequently Asked Questions
Who can exploit this issue?
An attacker needs SiYuan access authorization because the export handler requires authentication. Any authenticated caller able to send requests to the affected endpoint may attempt exploitation over the network.
What does an attacker need to send?
The attacker needs a crafted request under /export/temp/ containing percent-decoded traversal sequences, such as %2e%2e, to escape the temporary directory. No user interaction is required.
Which code revisions are affected?
The issue is present from commit bb481e1, which added hardening to the main export-handler path, through the latest master identified by the advisory. The provided data does not identify affected release version numbers.
How can I determine whether my code is vulnerable?
Inspect kernel/server/serve.go in serveExport(). The vulnerable implementation has a /export/temp/ short-circuit that serves filepath.Join(util.TempDir, c.Request.URL.Path) without IsSubPath or IsSensitivePath validation.