GitHub Security Advisory: GHSA-c77m-r996-jr3q
Package: github.com/siyuan-note/siyuan/kernel (GO)
Severity: HIGH (CVSS 7.5)
CWE: CWE-863: Incorrect Authorization
Vulnerable versions: <= 3.6.1
Patched in: 3.6.2

Summary: SiYuan: Unauthenticated Access to Password-Protected Bookmarks via /api/bookmark/getBookmark

Details:
### Summary
The publish service exposes bookmarked blocks from password-protected documents to unauthenticated visitors. In publish/read-only mode, `/api/bookmark/getBookmark` filters bookmark results by calling `FilterBlocksByPublishAccess(nil, ...)`. Because the filter treats a `nil` context as authorized, it skips the publish password check and returns bookmarked blocks from documents configured as `Protected`. As a result, anyone who can access the publish service can retrieve content from protected documents without providing the required password, as long as at least one block in the document is bookmarked.

### Details
The issue is caused by an authorization bypass in the bookmark API path used by the publish service.

In `kernel/api/bookmark.go`, `getBookmark` checks whether the current request is in a read-only role and then filters bookmarks for publish access. However, it passes `nil` as the request context:
```go
if model.IsReadOnlyRoleContext(c) {
    publishAccess := model.GetPublishAccess()
    tempBookmarks := &model.Bookmarks{}
    for _, bookmark := range *bookmarks {
        bookmark.Blocks = model.FilterBlocksByPublishAccess(nil, publishAccess, bookmark.Blocks)
```
In `kernel/model/publish_access.go`, `FilterBlocksByPublishAccess` allows access when `c == nil`:
```go
if CheckPathAccessableByPublishIgnore(block.Box, block.Path, publishIgnore) &&
   (c == nil || password == "" || CheckPublishAuthCookie(c, passwordID, password)) {
    ret = append(ret, block)
}
```
This bypasses the intended password enforcement performed by `CheckPublishAuthCookie`, which validates the `publish-auth-<id>` cookie for protected content.

The publish proxy authenticates anonymous publish visitors with a `RoleReader` token, and `CheckAuth` accepts `RoleReader`, so unauthenticated publish visitors can reach `/api/bookmark/getBookmark` and trigger the vulnerable code path.

I reproduced this by creating a protected document, bookmarking a block inside it, opening the publish service in an incognito session without entering the document password, and sending a `POST /api/bookmark/getBookmark` request. The response returned a bookmark group containing the protected block in `data[0].blocks`, confirming the bypass.

### PoC

1. Start SiYuan with the publish service enabled.
2. Create a new document, for example publish-bookmark-poc.
3. Add a block containing identifiable content, for example BOOKMARK_SECRET_123.
4. Open the block attributes and assign a bookmark label, for example leak-test.
5. In Doc Tree, enable Publish Access Control and set the document to Protected.
6. Set a password for that document, for example test123, and confirm the change.
7. Open the publish service in a fresh incognito/private browser session.
8. Verify that opening the protected document through the publish UI requires the password.
9. Without entering the password, open the browser developer console and run:
```js
fetch("/api/bookmark/getBookmark", {
  method: "POST",
  h