Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹 Go

GHSA-hjh7-r5w8-5872

GHSA-hjh7-r5w8-5872 is a security vulnerability in github.com/siyuan-note/siyuan/kernel. O3 Security confirms whether GHSA-hjh7-r5w8-5872 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

SiYuan: Path Traversal via Double URL Encoding in `/export/` Endpoint (Incomplete Fix Bypass for CVE-2026-30869)

Also known asCVE-2026-41894GO-2026-5429
Published
Apr 22, 2026
Updated
Jun 25, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed

Blast Radius

1 pkg affected
🐹github.com/siyuan-note/siyuan/kernel

Real-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

The fix for CVE-2026-30869 in SiYuan v3.5.10 only added a denylist check (IsSensitivePath) but did not address the root cause — a redundant url.PathUnescape() call in serveExport(). An authenticated attacker can use double URL encoding (%252e%252e) to traverse directories and read arbitrary workspace files including the full SQLite database (siyuan.db), kernel log, and all user documents.

Details

In kernel/server/serve.go, the serveExport() function (line 314-320) processes file paths as follows:

filePath := strings.TrimPrefix(c.Request.URL.Path, "/export/")
decodedPath, err := url.PathUnescape(filePath)     // second decode
fullPath := filepath.Join(exportBaseDir, decodedPath)

Go's HTTP server already decodes percent-encoded characters once during request parsing. The additional url.PathUnescape() call creates a double-decode vulnerability:

  1. Attacker sends: GET /export/%252e%252e/siyuan.db
  2. Go HTTP decodes %25%, result: URL.Path = /export/%2e%2e/siyuan.db
  3. Go's path cleaner sees %2e%2e as literal characters (not ..), no redirect occurs
  4. url.PathUnescape("%2e%2e") decodes to ..
  5. filepath.Join(exportBaseDir, "../siyuan.db") resolves to <workspace>/temp/siyuan.db

The CVE-2026-30869 fix added IsSensitivePath() which blocks <workspace>/conf/ and OS-level paths (/etc, /root, etc.). However, it does NOT block:

  • <workspace>/temp/siyuan.db — full document database
  • <workspace>/temp/blocktree.db — block tree database
  • <workspace>/temp/siyuan.log — kernel log
  • <workspace>/temp/asset_content.db — asset content database

Note: the /appearance/ handler in the same file correctly uses gulu.File.IsSubPath() to validate paths (line 447), but this check is missing from the /export/ handler.

PoC

poc.zip Please extract the uploaded compressed file before proceeding

  1. docker compose up -d --build
  2. sh poc.sh
<img width="550" height="184" alt="스크린샷 2026-04-19 오후 5 08 30" src="https://github.com/user-attachments/assets/6aea4334-0b5a-4f45-bd1f-ecfad61ba524" />

Impact

  • Data exfiltration: An authenticated user (including low-privilege Publish/Reader users via the Publish service) can download the entire SQLite document database containing all blocks, documents, attributes, and full-text search indexes.
  • Information disclosure: Kernel log (siyuan.log) leaks internal server paths, versions, configuration details, and error messages.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/siyuan-note/siyuan/kernelall versions3.6.5

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/siyuan-note/siyuan/kernel. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update github.com/siyuan-note/siyuan/kernel to 3.6.5 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-hjh7-r5w8-5872 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-hjh7-r5w8-5872 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-hjh7-r5w8-5872. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary The fix for CVE-2026-30869 in SiYuan v3.5.10 only added a denylist check (`IsSensitivePath`) but did not address the root cause — a redundant `url.PathUnescape()` call in `serveExport()`. An authenticated attacker can use double URL encoding (`%252e%252e`) to traverse directories and read arbitrary workspace files including the full SQLite database (`siyuan.db`), kernel log, and all user documents. ### Details In `kernel/server/serve.go`, the `serveExport()` function (line 314-320) processes file paths as follows: ```go filePath := strings.TrimPrefix(c.Request.URL.Path, "/export/
O3 Security · Impact-Aware SCA

Is GHSA-hjh7-r5w8-5872 in your dependencies?

O3 detects GHSA-hjh7-r5w8-5872 across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.