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

SiYuan publish service's /api/bookmark/getBookmark passes nil context to FilterBlocksByPublishAccess, skipping password checks and leaking bookmarked blocks from protected documents to unauthenticated visitors.

- Payout: Research
- Severity: high
- Program: SiYuan
- Found by: ngocnn97
- Disclosed: 2026-03-31
- CVE: CVE-2026-34453
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-c77m-r996-jr3q
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-c77m-r996-jr3q
- Tags: area:web domain:web impact:data-leak kind:research platform:ghsa sev:high tech:go technique:source-review vuln:access-control vuln:auth-bypass vuln:info-disclosure

**Target:** SiYuan kernel bookmark API (/api/bookmark/getBookmark) via publish service
**Root cause:** getBookmark calls FilterBlocksByPublishAccess with a nil request context; the filter treats c == nil as authorized and skips CheckPublishAuthCookie, so protected-document blocks pass the publish password gate.
**Steps:**
1. Enable publish service; create document publish-bookmark-poc with block content BOOKMARK_SECRET_123.
2. Assign a bookmark label (e.g. leak-test) to the block.
3. Set document Publish Access Control to Protected with password test123.
4. Open publish service in incognito without entering the password.
5. Send `POST /api/bookmark/getBookmark` with body `{}` via fetch from the console.
6. Observe response data[0].blocks contains the protected block content.
**How found:** Code review of the bookmark API path: noticed getBookmark passes nil instead of the request context into FilterBlocksByPublishAccess, and that the filter's `c == nil` branch short-circuits the password check; confirmed by reproducing against a Protected document in incognito.
**Exploitation:** Anonymous publish visitor (RoleReader token accepted by CheckAuth) reaches the bookmark API; the nil-context filter returns bookmarked blocks from Protected documents without the publish-auth cookie, leaking content directly in the JSON response.
**Insight:** When an authorization helper takes a context and has a nil-tolerant branch, grep every caller for literal nil arguments — read-only/publish proxy paths that pass nil silently disable the check.

## Proof of concept (copied exactly from the original report)

**Fetch request to getBookmark bypassing password**

```
fetch("/api/bookmark/getBookmark", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: "{}"
})
  .then(r => r.json())
  .then(x => console.log(JSON.stringify(x, null, 2)));
```

