Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 271/271 feeds/APIs · 464/464 docs · 7/9 advisories · checked 03:28 UTC Customize Public modeDiscuss ChangeIntel on Discord

SiYuan: getBookmarkLabels returns every bookmark label in the workspace to anonymous readers, with no publish-access filtering

Exact source revision recorded 24 Oct 2026 13:19:41 UTC. It does not establish the source's current position. Latest evidence.

osv · GHSA-j4ph-9xwf-wcj4

Published

### Summary `/api/attr/getBookmarkLabels` is registered with `CheckAuth` only and applies no filtering of any kind. It runs a scan of the entire `blocks` table and returns the distinct set of every bookmark label in the workspace. An anonymous reader in publish mode receives the author's complete bookmark vocabulary, regardless of whether the bookmarked blocks live in published, hidden, password-protected or forbidden documents. The adjacent endpoint that returns bookmarks with their blocks does filter, and does so in a way that makes the intended rule explicit: it drops a label entirely when no accessible block carries it. ### Details **Route.** `kernel/api/router.go:297` on master, `:300` on the development branch: ```go ginServer.Handle("POST", "/api/attr/getBookmarkLabels", model.CheckAuth, getBookmarkLabels) ``` No `CheckAdminRole`, no `CheckReadonly`. Reachable by the publish `RoleReader` token and anonymously when `Publish.Auth.Enable` is `false`. **The handler** (`kernel/api/attr.go`) is a single line: ```go ret.Data = model.BookmarkLabels() ``` which reaches `kernel/model/bookmark.go:184` and then `sql.QueryBookmarkLabels()` at `kernel/sql/block_query.go:315`: ```go sqlStmt := "SELECT * FROM blocks WHERE ial LIKE ?" // "%bookmark=%" // collect distinct ialAttr(block.IAL, "bookmark") into a set, sort, return ``` There is no notebook scoping, no path scoping, no publish-access check and no filter function anywhere on the path. Every block in the workspace carrying a bookmark attribute contributes its label to the result. Bookmark labels are user-authored organisational text. They routinely name the thing being organised, which is the reason they are useful and also the reason they disclose. **The guarded sibling states the rule outright.** `getBookmark` (`kernel/api/router.go:190` on master, `:193` on the development branch) carries the same `CheckAuth`-only exposure and handles the same data. It does this: ```go bookmark.Blocks = model.FilterBlocksByPublishAccess(c, publishAccess, bookmark.Blocks) bookmark.Count = len(bookmark.Blocks) if bookmark.Count > 0 { *tempBookmarks = append(*tempBookmarks, bookmark) } ``` The final condition is the significant part. The label is not merely returned with an empty block list, it is omitted from the response entirely when no accessible block carries it. The project has therefore already decided that a reader must not learn a bookmark label they have no accessible block for. `getBookmarkLabels` returns those same labels unconditionally, one endpoint away. **Relationship to the tag-label fix.** Commit `4515fa257` ("Enforce publish access for tag labels") established that user-authored organisational labels are data the publish boundary must protect. Bookmark labels are the same class of data, drawn from the same IAL attribute store, reached through a structurally similar handler. That commit touched `kernel/api/tag.go`, `kernel/model/publish_access.go` and its test file, and nothing in `attr.go` or the bookmark path, so this endpoint was not covered by it. Worth noting that the defect here is broader than the one in the tag path. The tag filter existed and checked the visible tier, so it correctly excluded forbidden documents and leaked only the password tier. `getBookmarkLabels` has no filter at all, so labels from forbidden documents are returned as well. ### Proof of Concept Precondition: publish mode enabled (default port 6808), anonymous when `Publish.Auth.Enable` is `false`, otherwise any publish reader account. At least one bookmarked block in a document that is not published. ``` POST http://127.0.0.1:6808/api/attr/getBookmarkLabels {} → 200, a sorted array of every distinct bookmark label in the workspace, including labels used only inside documents the reader has no publish access to ``` No arguments are required. For contrast, the sibling endpoint returns nothing for those same labels to the same session: ``` POST http://127.0.0.1:6808/api/attr/getBookmark {} → 200, labels omitted entirely where no accessible block carries them ``` One endpoint withholds the label, the other returns it. ### Impact An anonymous reader in publish mode, or any publish `RoleReader`, obtains the author's complete bookmark vocabulary across the workspace. Bookmark labels are free text chosen to describe what is being organised, so the set discloses subject matter, project names, personal and organisational names, and the general shape of what the author is working on, including material in documents that are hidden, password-protected or explicitly forbidden to readers. Scoped honestly: this returns labels only. No block identifiers, document identifiers or content are included, so it does not extend into the identifier-to-content resolution paths. Confidentiality only, with no integrity or availability impact. ### Suggested fix Mirror `getBookmark`. For read-only roles, build the label set from blocks that have passed `FilterBlocksByPublishAccess`, and emit a label only if at least one surviving block carries it. That is the rule the sibling endpoint already implements, applied to the same data.

Source published
1 Oct 2026 16:23:37 UTC
Source modified
7 Oct 2026 17:56:02 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 26 Oct 03:28 UTC · 271 of 271 readable · 268 fully read · documentation 464/464 current