---
id: "CVE-2026-41894"
page: "https://infrastack.ai/live/CVE-2026-41894"
kind: "vulnerability"
aliases: ["CVE-2026-41894", "GHSA-hjh7-r5w8-5872"]
title: "SiYuan: Path Traversal via Double URL Encoding in `/export/` Endpoint (Incomplete Fix Bypass for CVE-2026-30869)"
severity: "high"
cvss: 7.1
epss: 0.00374
exploited: false
status: "open"
category: "package"
products: [{"name": "github.com/siyuan-note/siyuan/kernel", "fixed": ["3.6.5"], "affected": "< 3.6.5", "ecosystem": "go"}]
ecosystems: ["go"]
tags: ["cwe-22", "go", "package", "self-hosted", "siyuan"]
not_affected_if: ["auth-required", "managed-hosting", "no-publish-service"]
published_at: "2026-04-22T20:51:22+00:00"
first_seen_at: "2026-11-09T01:50:07+00:00"
updated_at: "2026-11-09T01:54:15+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-hjh7-r5w8-5872", "first_seen_at": "2026-11-09T01:50:07+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41894", "first_seen_at": "2026-11-09T01:54:15+00:00"}]
exploit_sources: [{"type": "epss", "score": 0.00374, "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-41894"}]
---

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

Link for people: https://infrastack.ai/live/CVE-2026-41894 (this .md is for agents)

SiYuan's `/export/` handler decodes file paths twice, so a request with `%252e%252e` escapes the export folder. An attacker with an account can then download the document database, the block tree database and the kernel log.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- go:github.com/siyuan-note/siyuan/kernel affected < 3.6.5 fixed 3.6.5

## How to check

- Check the SiYuan kernel version: `go:github.com/siyuan-note/siyuan/kernel` is affected below `3.6.5`.
- Look for `/export/%25` requests in web server or reverse proxy access logs.

## Fix

- SiYuan kernel: move to `3.6.5` or later.

## Timeline

- 2026-11-09T01:50:07+00:00 ghsa https://github.com/advisories/GHSA-hjh7-r5w8-5872
- 2026-11-09T01:54:15+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-41894

## Documents

- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/siyuan-note/siyuan/security-advisories/GHSA-hjh7-r5w8-5872
- [advisory] Maintainer's advisory: github.com https://github.com/siyuan-note/siyuan/security/advisories/GHSA-hjh7-r5w8-5872
- [fix] Patch: github.com https://github.com/siyuan-note/siyuan/commit/bb481e1290c4a34255652ede85a546504505d2a7
- [fix] Release: github.com https://github.com/siyuan-note/siyuan/releases/tag/v3.6.5

## From the sources

### 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/")
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](https://github.com/user-attachments/files/26866234/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.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
