GHSA-p4m3-mgmm-c664: SiYuan: Path Traversal via Double URL Encoding in /assets/*path (publish mode arbitrary file─read), Incomplete fix of CVE-2026-41894
Summary
The patch for CVE-2026-41894 ("Path Traversal via Double URL Encoding") sanitized the /export/ route but the
**identical root cause remains in the /assets/*path route. In publish mode (anonymous read-only HTTP endpoint,
default port 6808), an unauthenticated remote attacker can read arbitrary files inside WorkspaceDir — including
conf/conf.json (which contains the AccessAuthCode SHA256 hash, API token, and sync keys), temp/siyuan.db,
temp/blocktree.db, and siyuan.log — by double-URL-encoding .. segments.
Verified against siyuan v3.6.5:
- GET /assets/%252e%252e/%252e%252e/conf/conf.json → HTTP 200, 10349 bytes (conf.json served)
- GET /export/%252e%252e/%252e%252e/conf/conf.json → HTTP 401 (patched)
- GET /assets/%2e%2e/conf/conf.json → HTTP 404 (single-decode handled correctly)
Vulnerable Code
Step 1 — route & first decode** (kernel/server/serve.go:587-626):
The router registers GET /assets/*path for the publish listener. Gin performs one URL decoding pass on URL.Path,
so a request for /assets/%252e%252e/... yields context.Param("path") == "/%2e%2e/%2e%2e/conf/conf.json" — literal
%2e%2e strings, which path.Clean cannot collapse.
Step 2 — second decode via fallback (kernel/model/assets.go:536-563, GetAssetAbsPath):
p, err := getAssetAbsPath(relativePath)
if nil != err {
// fallback
decoded, e := url.PathUnescape(relativePath) // ← line 548, second decode
if nil == e {
p, err = getAssetAbsPath(decoded)
}
}
After the fallback decodes %2e%2e to .., filepath.Join(DataDir, "../../conf/conf.json") is Clean-ed to
WorkspaceDir/conf/conf.json, an existing file.
Step 3 — publish-mode access gate fall-through (kernel/model/publish_access.go:288,
CheckAbsPathAccessableByPublishAccess):
if !filelock.IsSubPath(util.DataDir, absPath) {
return true // ← fall-through allows anything outside DataDir but inside WorkspaceDir
}
Because the resolved file is *outside* DataDir (it's in WorkspaceDir), the gate returns true and
IsSensitivePath() is never invoked — .db / .
Details
Original advisory: https://github.com/advisories/GHSA-p4m3-mgmm-c664
Exploitation outlook
EPSS (FIRST.org) estimates each CVE’s probability of exploitation in the next 30 days — here is the CSIRTS.com read on those numbers.
- Moderate exploitation riskCVE-2026-540661.9% 30-day exploitation probability. Patch within normal cadence, watch for KEV listing. Riskier than 77% of all scored CVEs.
Referenced CVEs
| CVE | CSIRTS overview | External |
|---|---|---|
| CVE-2026-54066 | coverage & exploitation status | NVD · CVE.org |
More from GitHub Security Advisories
- criticalGHSA-g936-7jqj-mwv8: TSDProxy: Internal proxy auth token forwarded to backend services enables management API …2026-07-10
- highGHSA-fpg8-7664-jc5q: melange: Incomplete package integrity verification allows data section substitution2026-07-10
- mediumGHSA-48rx-c7pg-q66r: Excon does not redact additional sensitive/risky headers when following redirects2026-07-10
- highGHSA-h4g2-xfmw-q2c9: Clauster: Non-loopback deployments can serve the dashboard unauthenticated when auth.enab…2026-07-10
- mediumGHSA-rqq5-2gf9-4w4q: Secure Headers: CSP directive injection via sandbox, plugin_types, and report_to when giv…2026-07-10