{"id":"CVE-2026-41894","kind":"vulnerability","aliases":["CVE-2026-41894","GHSA-hjh7-r5w8-5872"],"related":[],"title":"SiYuan: Path Traversal via Double URL Encoding in `/export/` Endpoint (Incomplete Fix Bypass for CVE-2026-30869)","description":"### Summary\nThe 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.\n\n### Details\nIn `kernel/server/serve.go`, the `serveExport()` function (line 314-320) processes file paths as follows:\n\n```go\nfilePath := strings.TrimPrefix(c.Request.URL.Path, \"/export/\")\ndecodedPath, err := url.PathUnescape(filePath)     // second decode\nfullPath := filepath.Join(exportBaseDir, decodedPath)\n```\n\nGo's HTTP server already decodes percent-encoded characters once during request parsing. The additional `url.PathUnescape()` call creates a double-decode vulnerability:\n\n1. Attacker sends: `GET /export/%252e%252e/siyuan.db`\n2. Go HTTP decodes `%25` → `%`, result: `URL.Path = /export/%2e%2e/siyuan.db`\n3. Go's path cleaner sees `%2e%2e` as literal characters (not `..`), no redirect occurs\n4. `url.PathUnescape(\"%2e%2e\")` decodes to `..`\n5. `filepath.Join(exportBaseDir, \"../siyuan.db\")` resolves to `<workspace>/temp/siyuan.db`\n\nThe CVE-2026-30869 fix added `IsSensitivePath()` which blocks `<workspace>/conf/` and OS-level paths (`/etc`, `/root`, etc.). However, it does NOT block:\n- `<workspace>/temp/siyuan.db` — full document database\n- `<workspace>/temp/blocktree.db` — block tree database\n- `<workspace>/temp/siyuan.log` — kernel log\n- `<workspace>/temp/asset_content.db` — asset content database\n\nNote: 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.\n\n### PoC\n[poc.zip](https://github.com/user-attachments/files/26866234/poc.zip)\nPlease extract the uploaded compressed file before proceeding\n\n1. docker compose up -d --build\n2. sh poc.sh\n\n<img width=\"550\" height=\"184\" alt=\"스크린샷 2026-04-19 오후 5 08 30\" src=\"https://github.com/user-attachments/assets/6aea4334-0b5a-4f45-bd1f-ecfad61ba524\" />\n\n\n\n\n### Impact\n- 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.\n- Information disclosure: Kernel log (`siyuan.log`) leaks internal server paths, versions, configuration details, and error messages.","summary":"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.","severity":"high","cvss":7.1,"epss":0.00374,"exploited":false,"exploited_at":null,"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"],"how_to_check":"- Check the SiYuan kernel version: `go:github.com/siyuan-note/siyuan/kernel` is affected below `3.6.5`.\n- Look for `/export/%25` requests in web server or reverse proxy access logs.","fix":"- SiYuan kernel: move to `3.6.5` or later.","status":"open","published_at":"2026-04-22T20:51:22+00:00","first_seen_at":"2026-11-09T01:50:07+00:00","triaged_at":"2026-11-09T01:50:47+00:00","updated_at":"2026-11-09T01:54:15+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T01:50:47Z","model":"claude-haiku-5-5","output":{"fix":"- SiYuan kernel: move to `3.6.5` or later.","reach":"internet","title":"SiYuan: double URL encoding lets authenticated users read the workspace database","impact":"high","summary":"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.","audience":"businesses","category":"package","nicknames":[],"who_cares":["siyuan","self-hosted","go"],"confidence":0.8,"fix_status":"available","needs_auth":"yes","worst_case":"Attacker downloads the full document database and logs.","analyst_note":"","how_to_check":"- Check the SiYuan kernel version: `go:github.com/siyuan-note/siyuan/kernel` is affected below `3.6.5`.\n- Look for `/export/%25` requests in web server or reverse proxy access logs.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["managed-hosting","no-publish-service","auth-required"],"who_is_affected":"Teams that run a self-hosted SiYuan server, or expose its Publish service, where users have accounts. The flaw is in the `/export/` path handling of the kernel, so a standalone desktop install with no network access is not exposed to remote attackers.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"unknown","severity_if_unknown":"high"},"cost_usd":0.000816,"input_tokens":5653,"output_tokens":501,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2026':16A,19A,27A,82C,236C '-22':33B '-30869':17A,83C,237C '-320':144C '-41894':20A,28A '/appearance':274C '/conf':243C '/etc':249C '/export':9A,42B,154C,193C,203C,295C '/root':250C '/siyuan-note/siyuan/kernel':31A '/siyuan.db':230C '/temp/asset_content.db':268C '/temp/blocktree.db':261C '/temp/siyuan.db':233C,257C '/temp/siyuan.log':265C '/user-attachments/files/26866234/poc.zip)':301C '1':189C,310C '2':196C,316C '25':200C '252e':52B,53B,114C,115C,194C '252e/siyuan.db':195C '2e':204C,212C,213C,223C,224C '2e/siyuan.db':205C '3':206C '314':143C '4':221C '447':287C '5':227C '5872':25A 'account':62B 'add':88C,239C 'addit':180C 'address':96C 'alreadi':169C 'arbitrari':121C 'asset':269C 'attack':59B,108C,190C 'attribut':346C 'authent':107C,323C 'block':70B,242C,256C,262C,344C 'build':315C 'bypass':13A 'c.request.url.path':153C 'call':103C,182C 'caus':99C 'charact':174C,216C 'check':91C,290C 'cleaner':210C 'compos':312C 'compress':306C 'configur':363C 'contain':342C 'content':270C 'correct':280C 'creat':183C 'cve':15A,18A,26A,81C,235C 'cwe':32B 'd':314C 'data':320C 'databas':68B,72B,128C,260C,264C,271C,341C 'decod':44B,160C,170C,187C,199C,225C 'decodedpath':155C,164C 'denylist':90C 'detail':136C,364C 'directori':118C 'disclosur':354C 'docker':311C 'document':67B,135C,259C,340C,345C 'doubl':5A,111C,186C 'double-decod':185C 'download':65B,336C 'encod':7A,113C,173C 'endpoint':10A 'entir':338C 'err':156C 'error':366C 'escap':54B 'etc':251C 'exfiltr':321C 'export':56B 'exportbasedir':163C,229C 'extract':303C 'file':45B,123C,146C,279C,307C 'filepath':151C,158C 'filepath.join':162C,228C 'fix':12A,79C,238C 'folder':57B 'follow':149C 'full':126C,258C,349C 'full-text':348C 'fullpath':161C 'function':141C 'get':192C 'ghsa':22A 'ghsa-hjh7-r5w8':21A 'github.com':30A,300C 'github.com/siyuan-note/siyuan/kernel':29A 'github.com/user-attachments/files/26866234/poc.zip)':299C 'go':34B,150C,165C,197C,207C 'gulu.file.issubpath':282C 'handler':43B,275C,296C 'hjh7':23A 'host':38B 'howev':252C 'http':167C,198C 'impact':319C 'includ':124C,325C 'incomplet':11A 'index':352C 'inform':353C 'intern':359C 'issensitivepath':92C,240C 'kernel':75B,130C,266C,355C 'kernel/server/serve.go':138C 'leak':358C 'level':247C 'line':142C,286C 'liter':215C 'log':76B,131C,267C,356C 'low':327C 'low-privileg':326C 'messag':367C 'miss':292C 'note':272C 'occur':220C 'os':246C 'os-level':245C 'packag':35B 'pars':178C 'path':2A,46B,147C,209C,248C,285C,361C 'percent':172C 'percent-encod':171C 'pleas':302C 'poc':297C 'poc.sh':318C 'poc.zip':298C 'privileg':328C 'proceed':309C 'process':145C 'publish':333C 'publish/reader':329C 'r5w8':24A 'read':120C 'redirect':219C 'redund':101C 'request':50B,177C 'resolv':231C 'result':201C 'root':98C 'search':351C 'second':159C 'see':211C 'self':37B 'self-host':36B 'send':191C 'serveexport':105C,140C 'server':168C,360C 'servic':334C 'sh':317C 'siyuan':1A,39B,40B,85C 'siyuan.db':129C 'siyuan.log':357C 'sqlite':127C,339C 'strings.trimprefix':152C 'summari':77C 'text':350C 'travers':3A,117C 'tree':71B,263C 'twice':47B 'upload':305C 'url':6A,112C 'url.path':202C 'url.pathunescape':102C,157C,181C,222C 'use':110C,281C 'user':134C,324C,330C 'v3.5.10':86C 'valid':284C 'version':362C 'via':4A,331C 'vulner':188C 'workspac':122C","nicknames":[],"mitigations":[],"who_affected":"Teams that run a self-hosted SiYuan server, or expose its Publish service, where users have accounts. The flaw is in the `/export/` path handling of the kernel, so a standalone desktop install with no network access is not exposed to remote attackers.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"pkeys":["githubcomsiyuannotesiyuankernel"],"ptokens":["com","github","kernel","note","siyuan"],"labels":["cwe22","go","high","package","selfhosted","siyuan"],"campaign_type":null,"search_a":"'-2026':16A,19A,27A '-30869':17A '-41894':20A,28A '/export':9A '/siyuan-note/siyuan/kernel':31A '5872':25A 'bypass':13A 'cve':15A,18A,26A 'doubl':5A 'encod':7A 'endpoint':10A 'fix':12A 'ghsa':22A 'ghsa-hjh7-r5w8':21A 'github.com':30A 'github.com/siyuan-note/siyuan/kernel':29A 'hjh7':23A 'incomplet':11A 'path':2A 'r5w8':24A 'siyuan':1A 'travers':3A 'url':6A 'via':4A","search_b":"'-22':33B '/export':42B '252e':52B,53B 'account':62B 'attack':59B 'block':70B 'cwe':32B 'databas':68B,72B 'decod':44B 'document':67B 'download':65B 'escap':54B 'export':56B 'file':45B 'folder':57B 'go':34B 'handler':43B 'host':38B 'kernel':75B 'log':76B 'packag':35B 'path':46B 'request':50B 'self':37B 'self-host':36B 'siyuan':39B,40B 'tree':71B 'twice':47B","search_c":"'-2026':82C,236C '-30869':83C,237C '-320':144C '/appearance':274C '/conf':243C '/etc':249C '/export':154C,193C,203C,295C '/root':250C '/siyuan.db':230C '/temp/asset_content.db':268C '/temp/blocktree.db':261C '/temp/siyuan.db':233C,257C '/temp/siyuan.log':265C '/user-attachments/files/26866234/poc.zip)':301C '1':189C,310C '2':196C,316C '25':200C '252e':114C,115C,194C '252e/siyuan.db':195C '2e':204C,212C,213C,223C,224C '2e/siyuan.db':205C '3':206C '314':143C '4':221C '447':287C '5':227C 'add':88C,239C 'addit':180C 'address':96C 'alreadi':169C 'arbitrari':121C 'asset':269C 'attack':108C,190C 'attribut':346C 'authent':107C,323C 'block':242C,256C,262C,344C 'build':315C 'c.request.url.path':153C 'call':103C,182C 'caus':99C 'charact':174C,216C 'check':91C,290C 'cleaner':210C 'compos':312C 'compress':306C 'configur':363C 'contain':342C 'content':270C 'correct':280C 'creat':183C 'cve':81C,235C 'd':314C 'data':320C 'databas':128C,260C,264C,271C,341C 'decod':160C,170C,187C,199C,225C 'decodedpath':155C,164C 'denylist':90C 'detail':136C,364C 'directori':118C 'disclosur':354C 'docker':311C 'document':135C,259C,340C,345C 'doubl':111C,186C 'double-decod':185C 'download':336C 'encod':113C,173C 'entir':338C 'err':156C 'error':366C 'etc':251C 'exfiltr':321C 'exportbasedir':163C,229C 'extract':303C 'file':123C,146C,279C,307C 'filepath':151C,158C 'filepath.join':162C,228C 'fix':79C,238C 'follow':149C 'full':126C,258C,349C 'full-text':348C 'fullpath':161C 'function':141C 'get':192C 'github.com':300C 'github.com/user-attachments/files/26866234/poc.zip)':299C 'go':150C,165C,197C,207C 'gulu.file.issubpath':282C 'handler':275C,296C 'howev':252C 'http':167C,198C 'impact':319C 'includ':124C,325C 'index':352C 'inform':353C 'intern':359C 'issensitivepath':92C,240C 'kernel':130C,266C,355C 'kernel/server/serve.go':138C 'leak':358C 'level':247C 'line':142C,286C 'liter':215C 'log':131C,267C,356C 'low':327C 'low-privileg':326C 'messag':367C 'miss':292C 'note':272C 'occur':220C 'os':246C 'os-level':245C 'pars':178C 'path':147C,209C,248C,285C,361C 'percent':172C 'percent-encod':171C 'pleas':302C 'poc':297C 'poc.sh':318C 'poc.zip':298C 'privileg':328C 'proceed':309C 'process':145C 'publish':333C 'publish/reader':329C 'read':120C 'redirect':219C 'redund':101C 'request':177C 'resolv':231C 'result':201C 'root':98C 'search':351C 'second':159C 'see':211C 'send':191C 'serveexport':105C,140C 'server':168C,360C 'servic':334C 'sh':317C 'siyuan':85C 'siyuan.db':129C 'siyuan.log':357C 'sqlite':127C,339C 'strings.trimprefix':152C 'summari':77C 'text':350C 'travers':117C 'tree':263C 'upload':305C 'url':112C 'url.path':202C 'url.pathunescape':102C,157C,181C,222C 'use':110C,281C 'user':134C,324C,330C 'v3.5.10':86C 'valid':284C 'version':362C 'via':331C 'vulner':188C 'workspac':122C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-04-19T09:48:52+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-hjh7-r5w8-5872","url":"https://github.com/advisories/GHSA-hjh7-r5w8-5872","severity":"high","published_at":"2026-04-22T20:51:22+00:00","modified_at":"2026-11-09T01:45:34+00:00","first_seen_at":"2026-11-09T01:50:07+00:00","fetched_at":"2026-11-09T01:57:44+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-41894","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41894","severity":null,"published_at":"2026-04-24T19:17:14+00:00","modified_at":"2026-06-17T10:47:09+00:00","first_seen_at":"2026-11-09T01:54:15+00:00","fetched_at":"2026-11-09T01:54:15+00:00","note":null}],"documents":[{"id":105104,"url":"https://api.github.com/repos/siyuan-note/siyuan/security-advisories/GHSA-hjh7-r5w8-5872","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":105105,"url":"https://github.com/siyuan-note/siyuan/security/advisories/GHSA-hjh7-r5w8-5872","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-04-19T09:48:52+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":105106,"url":"https://github.com/siyuan-note/siyuan/commit/bb481e1290c4a34255652ede85a546504505d2a7","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":105107,"url":"https://github.com/siyuan-note/siyuan/releases/tag/v3.6.5","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[{"type":"epss","score":0.00374,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-41894"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}