SiYuan: path traversal via /export/temp/ short-circuit branch (incomplete fix for the export-disclosure hardening, GHSA-6865-qjcf-286f)GHSA-gw25-m53r-qh88
MEDIUMFix: siyuan-note/siyuan@b763d78GHSA-gw25-m53r-qh88 is a medium-severity (CVSS 6.5) Path Traversal vulnerability in github.com/siyuan-note/siyuan/kernel. A fix is available for github.com/siyuan-note/siyuan/kernel — see the affected versions and patch details below.
Exploitation Status
No confirmed exploitation observed yet
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for GHSA-gw25-m53r-qh88.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-gw25-m53r-qh88 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
github.com/siyuan-note/siyuan/kernelReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
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:
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 +IsSensitivePathextended to*.db/*.log. - Follow-up CVE-2026-41894 (GHSA-hjh7-r5w8-5872) in the same
/exportpath family. - The
/export/temp/short-circuit branch was never covered by the guards (this report).
Proof of concept (benign)
- Authenticate (access auth code).
GET /export/<sensitive>(main branch) -> 401/403 (guards work).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+IsSensitivePathto the/export/temp/branch (or restrict its root toTempDir/tempwith anIsSubPathcheck). filepath.Cleanthe 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.goserveExport()lines 308-312.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/siyuan-note/siyuan/kernel | all versions | 0.0.0-20260510110132-b763d787d1f2go get github.com/siyuan-note/siyuan/kernel@v0.0.0-20260510110132-b763d787d1f2 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/siyuan-note/siyuan/kernel, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update github.com/siyuan-note/siyuan/kernel to 0.0.0-20260510110132-b763d787d1f2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-gw25-m53r-qh88 is resolved across your whole dependency graph.
Workarounds
Resolve every user-supplied path to its canonical form and reject anything that escapes the intended directory, and run the component under an account that has no read or write access outside the directory it legitimately serves.
Frequently Asked Questions
Is GHSA-gw25-m53r-qh88 in your dependencies?
Find it across Go, including transitive dependencies.