GitHub Advisoryadvisory · Go
GHSA-275h-v5h9-vr82 Siyuan: Authenticated path traversal in /snippets/ static handler (serveSnippets) leaks conf/conf.json secrets and siyuan.db
HIGH
Official page ↗
- Aliases
- CVE-2026-59832
- Ecosystem
- Go
- Packages
- github.com/siyuan-note/siyuan/kernel
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
- Weaknesses
- CWE-22, CWE-23
- Reviewed
- 2026-09-02
Affected packages
• Go github.com/siyuan-note/siyuan/kernel: < 0.0.0-20260704035520-68cc0f537dfa → fixed in 0.0.0-20260704035520-68cc0f537dfa
Description
Reporter: Cavan Loughran, Celvex Group Inc.
Summary
-------
The /snippets/*filepath route handler serveSnippets in kernel/server/serve.go performs a bare filepath.Join(util.SnippetsPath, filePath) on the single-decoded c.Request.URL.Path and serves the result with c.File(), with NO IsSubPath containment and NO IsSensitivePath denylist - unlike the sibling /export/ (serveExport) and /appearance/ (serveAppearance) handlers, which both carry IsSubPath, and unlike /assets/ (serveAssets), whose traversal was fixed in GHSA-p4m3-mgmm-c664. Because util.SnippetsPath = WorkspaceDir/data/snippets, an authenticated request to GET /snippets/%2e%2e/%2e%2e/conf/conf.json resolves to WorkspaceDir/conf/conf.json and leaks the kernel API token and AccessAuthCode (the same secret file CVE-2026-30869 leaked from /export/); GET /snippets/%2e%2e/%2e%2e/temp/siyuan.db leaks the full document database.
Affected versions
-----------------
v3.6.5 and current master (verified by direct source read). The /export/ and /assets/ fixes were endpoint-scoped and never reached serveSnippets.
Technical detail
----------------
Sink, kernel/server/serve.go, serveSnippets (verbatim, current master and v3.6.5):
func serveSnippets(ginServer *gin.Engine) {
ginServer.Handle("GET", "/snippets/*filepath", model.CheckAuth, func(c *gin.Context) {
filePath := strings.TrimPrefix(c.Request.URL.Path, "/snippets/")
if !model.IsAdminRoleContext(c) {
if "conf.json" == filePath {
c.Status(http.StatusUnauthorized)
return
}
}
ext := filepath.Ext(filePath)
name := strings.TrimSuffix(filePath, ext)
confSnippets, err := model.LoadSnippets()
...
for _, s := range confSnippets {
if s.Name == name && ("" != ext && s.Type == ext[1:]) {
c.Header("Content-Type", mime.TypeByExtension(ext))
c.String(http.StatusOK, s.Content)
return
}
}
// when not matched in the config file, look it up on the filesystem
filePath = filepath.Join(util.SnippetsPath, filePath) // WorkspaceDir/conf/conf.json (kernel API token, AccessAuthCode, cookie signing material - the same secrets CVE-2026-30869 leaked).
• GET /snippets/%2e%2e/%2e%2e/temp/siyuan.db -> WorkspaceDir/temp/siyuan.db (the entire document database).
• GET /snippets/%2e%2e/%2e%2e/%2e%2e/etc/passwd (and deeper) reaches host files outside the workspace; c.File serves any path Clean resolves to, subject only to OS file permissions.
Incomplete-fix lineage (patch-diff)
-----------------------------------
SiYuan has been fixing path traversal in file-serving handlers ONE endpoint at a time:
• /export/ (serveExport): CVE-2026-30869 (IsSensitivePath denylist, v3.5.10), then CVE-2026-41894 / GHSA-hjh7-r5w8-5872 (double-encode bypass, v3.6.5). Now has IsSubPath(exportBaseDir, fullPath) + IsSensitivePath.
• /appearance/ (serveAppearance): hardened alongside; has IsSubPath(appearancePath, filePath).
• /assets/*path (serveAssets): GHSA-p4m3-mgmm-c664; delegates to model.GetAssetAbsPath (containment) + publish-access check.
• /snippets/*filepath (serveSnippets): NONE. NO IsSubPath, NO IsSensitivePath; only a literal "conf.json" string match for non-admins, defeated by traversal.
The fixes that closed /export/ and /assets/ were endpoint-scoped (per-handler IsSubPath/IsSensitivePath/GetAssetAbsPath) rather than a shared request-level path-confinement primitive applied to every c.File/http.ServeFile sink. serveSnippets was never touched. It reaches the SAME secret file (conf/conf.json) the parent CVE-2026-30869 was filed for, at a LOWER bar in one respect: it needs only single URL encoding (no double-encode trick), because there is no containment check to bypass in the first place.
Privilege / reachability (stated honestly)
------------------------------------------
The route is gated by model.CheckAuth only (any authenticated user), NOT CheckAdminRole. CheckAuth admits any principal that presents a valid API token (Conf.Api.Token), a valid session whose AccessAuthCode == Conf.AccessAuthCode, or BasicAuth. The handler's own if !model.IsAdminRoleContext(c) branch confirms non-admin reachability; that branch only blocks the literal string "conf.json", which the traversal payload "../../conf/conf.json" does not match, so even non-admins leak the secret file. SiYuan supports non-admin authenticated roles (RoleEditor, RoleReader) in shared/published workspace modes, plus access-auth-code logins. Privilege required: PR:L (a valid authenticated session), NOT pre-auth and NOT admin-gated. Reading conf/conf.json yields the admin API token/AccessAuthCode, letting a non-admin escalate to full kernel-admin API control; siyuan.db leaks all note content. The kernel HTTP server is the published interface for self-hosted/Docker deployments (AV:N).
SiYuan's SECURITY.md excludes arbitrary file WRITE outside the workspace as a non-issue, but this finding is arbitrary file READ of in-workspace secrets (conf/conf.json, siyuan.db) and host files. Read-side traversal of conf.json is exactly what CVE-2026-30869 was accepted for, so this is squarely in scope.
Non-destructive: no weaponized exploit is included; the chain is described in prose for the maintainer to reproduce.
Secondary (reported for completeness, not the headline): serveRepoDiff (/repo/diff/*path) shares the same bare filepath.Join(util.TempDir, "repo", "diff", requestPath) + http.ServeFile with NO containment, so .. in requestPath escapes TempDir/repo/diff. BUT it carries model.CheckAdminRole (admin-only), so the trust boundary crossed is weak (an admin already holds the API token). Lower severity; shares the same one-line fix.
Impact
------
Authenticated (non-admin, PR:L) arbitrary workspace file read: kernel API token + AccessAuthCode (conf/conf.json), the full document database (siyuan.db), and host files outs…
Related
Alias · CVE (1)
- CVE-2026-59832 SiYuan is an open-source personal knowledge management system. vulnerability
References
- https://github.com/siyuan-note/siyuan/security/advisories/GHSA-275h-v5h9-vr82 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-59832 advisory
- https://github.com/siyuan-note/siyuan/commit/68cc0f537dfa4502496dfa794e71835421c25c09 web
- https://github.com/siyuan-note/siyuan package
- https://github.com/siyuan-note/siyuan/releases/tag/v3.7.1 web