GHSA-v684-q882-jgmq: High severity go/github.com/siyuan-note/siyuan/kernel vulnerability

Published Sep 8, 2026
·
Updated

CVE: This vulnerability corresponds to CVE-2026-72789.

Summary

publishAccess.json is an opt-out list. The publish gate returns accessible for anything not explicitly listed in it. Encrypted notebooks are never written into that file, because only the administrator-gated setPublishAccess writes it and no part of the encryption subsystem does. Consequently every encrypted notebook is publish-accessible as far as the gate is concerned.

While an encrypted notebook is unlocked, an anonymous reader in publish mode can list it, enumerate its documents, and retrieve their fully decrypted content. No key material, no password, and no cracking is involved. The kernel decrypts the data and serves it because the authorization layer never asks whether the notebook is encrypted.

This is reported as a defect in the gate rather than in any individual handler. Endpoints that were previously reviewed and found to apply the correct checks do apply them. The checks return true.

Details

The gate defaults to accessible. CheckPathAccessableByPublishIgnore(box, path, ignore) iterates the ignore list and returns false only on a match:

go for , item := range publishIgnore { if item.ID == box || strings.Contains(path, item.ID) { return false } } return true // unlisted means accessible

Encrypted notebooks are never listed. publishAccess.json is written only by setPublishAccess (kernel/api/router.go:169), which carries CheckAdminRole and CheckReadonly. Nothing in crypto.go, encryptedops.go or notebookcrypto.go writes to it. An administrator would have to manually add each encrypted notebook to the ignore list to protect it, and nothing in the product prompts or documents that.

Neither core gate rejects them. On both eef105683 and v3.7.4-alpha.1:

go func checkBlockTreeAccessableByPublishAccess(...) bool { return CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) && (password == "" || CheckPublishAuthCookie(c, passwordID, password)) }

For an encrypted notebook the first term is true because it is unlisted, and the second is true because encrypted notebooks carry no publish password. The conjunction returns true.

IsEncryptedBox does not appear in kernel/model/publishaccess.go at all on master. On the development branch it appears once, inside parseAttributeViewForPublishAccess, where it routes into encrypted boxes rather than excluding them. The publish authorization layer has no concept of an encrypted notebook.

The chain. Verified on the development branch, which is the harder target because the block-metadata, attribute-view, path-resolution and encrypted-notebook-status fixes have all landed there.

1. lsNotebooks calls ListNotebooks(), which includes encrypted notebooks and reports Encrypted: boxConf.Encrypted without filtering on it. The reader filter skips only notebooks that are Closed or publish-invisible. An unlocked encrypted notebook is neither, so it is returned to an anonymous reader together with its identifier. 2. listDocsByPath{notebook: <encrypted box id>, path: "/"} applies only CheckPathAccessableByPublishIgnore, which passes, returning the document identifiers and titles inside the encrypted notebook. 3. getDoc routes to GetDocInBox(...), after which FilterContentByPublishAccess(...) returns the content unmodified because the box is unlisted. The response is the fully decrypted document. getBlockKramdown reaches GetBlockKramdownInBox for the same result in Markdown.

Handlers reach the decrypted store through encryptedNotebookFromArg(arg) on a client-supplied notebook argument. This function predates the current release and is not a recent addition.

The precondition is the unlock window. GetBlockTreeInBox returns nil while a notebook is locked, treating it as nonexistent, and non-nil once unlocked. So the exposure is bounded to the period during which the legitimate user has the notebook open, which is precisely the period in which they are working in it. No action by the attacker triggers or extends that window, but no unusual condition is required either.

On endpoints previously assessed as correctly gated. getBlockDOM and getBlockKramdown apply password and visibility checks. exportPreview applies FilterContentByPublishAccess. Those assessments are accurate and this report does not contradict them. The checks execute and return true, because the visibility term resolves to accessible for an unlisted box and the password term is vacuous for a notebook that has no publish password. Patching any individual handler would not change that result.

Proof of Concept

Precondition: publish mode enabled (default port 6808), anonymous when Publish.Auth.Enable is false, otherwise any publish reader account. An encrypted notebook that is currently unlocked by the legitimate user.

Step 1, obtain the encrypted notebook's identifier:

POST http://127.0.0.1:6808/api/notebook/lsNotebooks {}

→ 200. The list includes the encrypted notebook, with encrypted: true and its id.

Step 2, enumerate its documents:

POST http://127.0.0.1:6808/api/filetree/listDocsByPath {"notebook":"<encrypted box id>","path":"/"}

→ 200, document identifiers and titles from inside the encrypted notebook

Step 3, retrieve decrypted content:

POST http://127.0.0.1:6808/api/filetree/getDoc {"id":"<document id from step 2>"}

→ 200, the fully decrypted document

getBlockKramdown returns the same content as decrypted Markdown. Repeating step 3 while the notebook is locked returns not-found, which confirms the unlock window is the only thing standing between an anonymous reader and the plaintext.

Impact

The confidentiality guarantee of the encrypted-notebook feature is defeated against a remote, unauthenticated attacker for as long as the notebook is unlocked. A user who encrypts a notebook is expressing that its contents should be protected beyond the ordinary publish boundary. The publish gate does not recognise that intent, and instead treats the notebook as publicly readable because nobody added it to an opt-out list that the encryption feature does not write to.

The exposure is full document content rather than metadata, and it applies to every document in the notebook. It requires no key material, no password, no offline work and no interaction with the encryption subsystem at all.

Suggested fix

Fail closed on encryption, independently of publishAccess.json:

go if IsEncryptedBox(bt.BoxID) { return false }

in both checkBlockTreeAccessableByPublishAccess and CheckBlockTreeMetadataAccessableByPublishAccess, and exclude encrypted notebooks from lsNotebooks and listDocsByPath for read-only roles.

The broader point is the default. An opt-out authorization list means every future notebook type, storage backend or content class is publicly accessible until somebody remembers to add it. Encrypted notebooks are the case where that default is most clearly wrong, but they are unlikely to be the only one.

Affected Software

1 affected componentFixes available
go/github.com/siyuan-note/siyuan/kernel<0.0.0-20260726020813-a25c2dd06aae
0.0.0-20260726020813-a25c2dd06aae

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade go/github.com/siyuan-note/siyuan/kernel to a version that resolves this vulnerability.

    Fixed in 0.0.0-20260726020813-a25c2dd06aae

Event History

Sep 8, 2026
Advisory Published
via GitHub·05:56 PM
Data Sourced
via GitHub·05:56 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Under what conditions can an unauthenticated user access notebook content?

The notebook must be encrypted and unlocked. In publish mode, an anonymous reader can then list the notebook, enumerate its documents, and retrieve fully decrypted content.

2

Does an attacker need the notebook password or encryption keys?

No. The issue does not require key material, a password, or cracking encryption; the kernel decrypts and serves the content because the publish authorization gate does not check whether the notebook is encrypted.

3

Is access blocked unless an administrator explicitly enables publishing?

No. The publish access mechanism is an opt-out list: the gate treats paths as accessible unless they are explicitly listed in publishAccess.json. Encrypted notebooks are not written to that file by the encryption subsystem.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203