SiYuan versions up to and including v3.7.2 have a critical SQL injection vulnerability in the backlink/mention search functionality. The vulnerability arises because stored block metadata and client-supplied keywords are concatenated into a SQL MATCH/search statement with insufficient escaping, allowing single quotes to break out of string literals. This enables an attacker to execute arbitrary SQL commands on the main read-write database handle, potentially reading and writing across notebooks. The issue is fixed in version v3.7.4.
SiYuan versions before v3.7.4 have a critical vulnerability in the WebSocket broadcast feature that allows anonymous users to bypass publish boundaries and receive live, unfiltered content updates. This includes edits to password-protected and forbidden documents without any authentication. The advisory is a duplicate and refers to GHSA-mw8r-mw84-88v2 for the original report.
**CVE:** This vulnerability corresponds to [CVE-2026-72812](https://nvd.nist.gov/vuln/detail/CVE-2026-72812).
### Summary
The `/api/ref/refreshBacklink` endpoint is gated by `CheckAuth` only. Unlike its mutating siblings, it carries no `CheckAdminRole`, no `CheckReadonly`, and no inline reader-role guard so it falls through all three authorization mechanisms the codebase uses to protect write operations. A publish `RoleReader` or the anonymous account when `Publish.Auth.Enable` is `false` can invoke it, forcing the server to flush its pending write-transaction queue, scan all references globally, load and parse referencing trees from disk, and enqueue database writes. This violates the read-only invariant (it writes even in a globally read-only workspace), provides an unauthenticated resource-amplification/DoS primitive, and applies no per-object access check to the caller-supplied ID.
### Details
**Route / auth tier.** `router.go`: `Handle("POST", "/api/ref/refreshBacklink", model.CheckAuth, refreshBacklink)`, `CheckAuth` only. `CheckAuth` admits `RoleReader`; the publish proxy forwards port-6808 traffic with a Reader JWT (anonymous account when publish auth is disabled). Anonymous/reader reachable.
**Guard fall-through.** SiYuan protects write handlers with one of three mechanisms: route-level `CheckAdminRole` (AV/riff/repo/sync/setting/snippet/notebook mutations), route-level `CheckReadonly` (filetree/block/attr/tag mutations), or an inline `IsReadOnlyRoleContext` check (e.g. `updateEmbedBlock`, `updateRecentDoc*Time`). `refreshBacklink` has none of the three.
**Write path reached.** `refreshBacklink` calls `model.RefreshBacklink(id)`:
- `FlushTxQueue()` — forces the pending write-transaction queue to disk.
- `refreshRefsByDefID(defID)` → `QueryRefsByDefID(defID)` (global scan with encrypted-box fallback loop) → `filesys.LoadTrees(rootIDs)` (disk read and Lute parse of every referencing tree) → `sql.UpdateRefsTreeQueue(tree)` (enqueues DB writes) → ref-count task update.
The handler also does not consult `util.ReadOnly`, so it executes its writes even when the workspace is configured globally read-only.
**No per-object authorization.** `defID` is attacker-controlled and receives no publish-access or ownership check, so a reader can force a reference reindex of any document, including publish-forbidden/unpublished ones (cross-scope).
### Proof of Concept
Reproduced on a local instance (SiYuan running locally, publish mode enabled on port 6808, publish Basic Auth disabled), as an anonymous reader (no token):
**Target endpoint executes the full write path:**
```
POST http://127.0.0.1:6808/api/ref/refreshBacklink
{"id":"<any block id>"}
```
Returns `{"code":0,"msg":"","data":null}` HTTP 200 the handler ran to completion, flushing the transaction queue and enqueuing ref writes.
**Controls: the guarded mutating siblings correctly reject the same anonymous session:**
```
POST http://127.0.0.1:6808/api/tag/renameTag → 403 (CheckAdminRole and CheckReadonly)
POST http://127.0.0.1:6808/api/block/foldBlock → 403
POST http://127.0.0.1:6808/api/block/updateEmbedBlock → code 0 no-op (inline IsReadOnlyRoleContext blocks the write)
```
The 200-vs-403 contrast confirms `refreshBacklink` is reachable and executes where its siblings are blocked.
### Impact
An anonymous reader (publish mode with auth disabled) or any publish `RoleReader` can:
- **Bypass the read-only invariant:** trigger persistent server-side writes (transaction-queue flush and reference reindex), including in a workspace configured globally read-only.
- **Amplify resource use without authentication:** each call forces a transaction flush, a global reference scan, and disk read and parse of every referencing tree, with an attacker-controlled `id` and no rate limiting, a DoS primitive.
- **Act cross-scope:** `defID` receives no publish-access check, so a reader can force reindexing of documents outside their publish scope. This is an integrity-invariant violation and a resource-amplification vector, not data corruption or injection, the caller cannot control the content of the writes, only trigger them. Impact is integrity-low and availability-low; no confidentiality impact and no attacker-controlled data reaches storage.
### Suggested fix
Apply the same guard its mutating siblings use, add `CheckReadonly` (and `CheckAdminRole` if reference refresh is intended to be an authenticated operation) to the route, or an inline `IsReadOnlyRoleContext` check consistent with `updateEmbedBlock`. The endpoint should also honor `util.ReadOnly` and apply a publish-access check to `defID` so a reader cannot force cross-scope reindexing. More broadly, the three-way guard strategy (route middleware vs. inline check vs. none) is what allowed this handler to receive no gate at all; a structural backstop, a role-scoped route group for the mutation surface would prevent recurrence.
**CVE:** This vulnerability corresponds to [CVE-2026-72811](https://nvd.nist.gov/vuln/detail/CVE-2026-72811).
### Summary
The backlink/mention search query (`kernel/model/backlink.go`) concatenates stored block metadata (title, name, alias, anchor text) and the client-supplied keyword into a SQL `MATCH`/search statement, escaping only the double-quote character (`"`) and not the single quote (`'`). A single quote in either the client keyword or in stored document metadata breaks out of the string literal. The query runs on the main read-write `siyuan.db` handle through a statement-stacking-capable driver.
This yields two vectors:
- **First-order:** a client-supplied keyword containing `'` injects directly. This path is reachable by an anonymous reader on the publish surface.
- **Second-order:** a document whose title/name/alias contains `'` is stored safely (indexing uses parameterized inserts) but detonates when that stored value is later concatenated into the backlink query including on another user's kernel that has ingested the malicious document.
### Details
**Storage is safe; reuse is not.** Indexing INSERTs (`kernel/sql/upsert.go`) are parameterized (`(?,?,…)` with bound arguments for `Name`/`Content`/`Markdown`/`IAL`), so malicious `.sy` content is stored intact and safely. The injection is in the *reuse* path: the backlink/mention MATCH query (`kernel/model/backlink.go`, around line 980) builds its condition by concatenating the stored title/name/alias/anchor and the client keyword, applying only `"`→escaping and not `'`. A `'` in either source terminates the literal and lands in SQL context.
**Sink / handle.** The concatenated statement reaches `SelectBlocksRawStmtNoParse` → `query()` on the global read-write `siyuan.db` handle (DSN has no `mode=ro`/`_query_only`), driven by the vendored `88250/go-sqlite3` fork whose connection `query` loops over `;`-separated statements (stacking possible). Ceiling is arbitrary SQL cross-notebook read and write.
**Route / auth tier.** The backlink/mention family (`getBacklink`, `getBacklink2`, `getBacklinkDoc`, `getBackmentionDoc`) is `CheckAuth`-only, so the first-order client-keyword vector is reachable by the publish `RoleReader` token and by the anonymous account when `Publish.Auth.Enable` is `false`.
**Scope of the injection surface (confirmed the only second-order sink).** A sweep of the query paths that reuse stored content confirmed this is the sole injectable reuse sink: indexing INSERTs are parameterized, tag search uses `content LIKE ?` with pattern escaping, virtual refs use in-memory matching (no SQL), `IN(...)` joins use node-IDs/hex-hashes/ROWIDs (no quote surface), and the graph name/content filter escapes `'`. Only the backlink/mention MATCH path concatenates with `'` unescaped.
### Proof of Concept
Reproduced on a local instance (SiYuan running locally, publish mode enabled on port 6808, publish Basic Auth disabled). Strictly non-destructive verification, a syntax-error probe and a stored-value observation; no UNION exfiltration, no write, no DDL.
**First-order (client keyword), anonymous reader on port 6808:**
```
POST http://127.0.0.1:6808/api/ref/getBacklink2
{"id":"<block id>","k":"a'b","mk":""}
```
The keyword `a'b` reaches the concatenated `MATCH` condition and produces a SQL parse error, confirming the single quote breaks out of the literal and the client keyword lands in SQL context.
**Second-order (stored title), setup via admin then observed anonymously:**
A document whose title contains a single quote e.g. `("locked-doc")` style metadata is stored safely by the parameterized indexer, then appears at the exact unescaped position in the backlink query when that document participates in a backlink/mention lookup, breaking the query the same way. This demonstrates that stored document metadata detonates on reuse, independent of the client keyword.
Verification was limited to the syntax-error probe and the stored-value position observation; no exfiltration or write statement was executed. Full reproduction detail available privately on request.
### Impact
**First-order:** an anonymous reader (publish mode with auth disabled) or any publish `RoleReader` injects arbitrary SQL into the backlink/mention query via the keyword parameter, on the read-write main handle: cross-notebook read disclosure and, via statement stacking, write and `ATTACH`. This is the same anonymous-arbitrary-SQL severity as the search/embed injection findings, on a different sink.
**Second-order:** a document whose title/name/alias contains injection content is stored safely but executes when that stored value is concatenated into the backlink query. This fires on any kernel that has ingested the malicious document for example via a shared or imported `.sy` file, sync, or an opened `.sy.zip`. The precondition here is content delivery into the victim workspace (not a direct anonymous request), so it is a distinct, delivery-dependent threat model from the f
**CVE:** This vulnerability corresponds to [CVE-2026-72810](https://nvd.nist.gov/vuln/detail/CVE-2026-72810).
### Summary
WebSocket sessions established through the publish surface (port 6808, `RoleReader` anonymous when `Publish.Auth.Enable` is `false`) are added to the same broadcast session pool as authenticated sessions. The kernel's broadcast functions push content events transactions carrying block DOM, document save/create, move/rename to every session in the pool with no role or publish-access filtering. As a result, an anonymous reader who holds a WebSocket connection open passively receives a real-time feed of every edit made in the workspace, including edits to password-protected, publish-forbidden, and unpublished documents. Because these events are delivered over the push channel and never pass through an HTTP handler, none of the publish-access filters that gate the HTTP endpoints apply.
### Details
**Session admission.** `HandleConnect` admits the injected `RoleReader` publish token, and the session is registered via `AddPushChan` into the same `sessions` pool used for authenticated clients.
**Unfiltered broadcast.** The broadcast functions (`Broadcast`, `broadcastOthers`, `broadcastOtherAppMains`, …) write to every session in the pool with no role or publish-access check. The `isPublish` flag on a session is consulted only to send the "service closed" notice it is never used to gate content. Content events are pushed via `PushModeBroadcast → Broadcast()`, so transactions (with rendered block DOM), `savedoc`/create, and `moveDoc`/rename events reach the publish reader's socket unfiltered.
**No HTTP filter applies.** This is a push channel, the events originate from the kernel's own edit pipeline and are broadcast directly to open sockets. They never traverse an HTTP handler, so the publish-access filters that gate the HTTP content endpoints (and the incomplete/missing filters reported separately on those endpoints) are not in the path at all. The WebSocket route (`/ws`) is `CheckAuth`-only, which the publish `RoleReader` token satisfies.
### Proof of Concept
Reproduced on a local instance (SiYuan running locally, publish mode enabled on port 6808, publish Basic Auth disabled). An anonymous client (no token, no password) opens `wss://127.0.0.1:6808/ws` and holds it open while an administrator edits documents in the workspace.
The anonymous socket received, in real time and unfiltered:
- `updateAttrs` with `name=WS_LEAK_SECRET_9931` on a block whose `rootID` is a **password-protected** document.
- The secret title `WS_SECRET_DOC_7742` of a newly created document.
- The create event carrying the notebook (box) name `CritChain` and the document path.
No HTTP request was made beyond the WebSocket upgrade; the content arrived over the push channel.
### Impact
An anonymous reader (publish mode with auth disabled) or any publish `RoleReader` who merely holds a WebSocket connection open receives a live feed of every edit an administrator makes: block DOM, attributes, titles, notebook names, and document structure, including for password-protected, publish-forbidden, and unpublished documents. This defeats the publish-access and publish-password boundaries entirely for any content edited while the socket is open. The precondition is trivial: an administrator active in the workspace while the anonymous socket is connected. Confidentiality-only (passive disclosure); the channel is receive-only for the reader. Encrypted-notebook content follows the same broadcast path if edited while unlocked.
### Suggested fix
Filter broadcasts by session before writing: for any session flagged `isPublish`, apply the same publish-access/publish-ignore/publish-password checks used on the HTTP content path before pushing a content event, or exclude publish sessions from content broadcasts entirely and deliver only the events a publish viewer is authorized to see. The `isPublish` flag is already present on the session; it should gate content, not only the service-closed notice.
chi versions before v5.2.2 contain an open redirect vulnerability in the RedirectSlashes middleware function that uses the Host header to construct redirect URLs. Attackers can manipulate the Host header to redirect users to arbitrary hosts, enabling phishing attacks and credential theft.
### Summary
`/api/block/getRefIDs` filters its results for reader roles through a helper that checks only the visibility tiers. The password tier is not checked, because the helper does not receive the request context and therefore cannot evaluate the publish auth cookie. A reader who has not entered a document's publish password learns that the document references a given block.
A function ten lines away in the same file does perform the full check, on the same input type.
### Details
**Route,** identical at `eef105683` (`kernel/api/router.go:235`) and dev `a7ae96ce` (`:245`):
```go
ginServer.Handle("POST", "/api/block/getRefIDs", model.CheckAuth, getRefIDs)
```
No `CheckReadonly`, no `CheckAdminRole`.
**The filter chain.** `getRefIDs` (`kernel/api/block.go:631`) checks `isEncryptedNotebookDeniedForPublish`, calls `model.GetBlockRefsInBox`, then for read-only roles:
```go
publishIgnore := model.GetInvisiblePublishAccess(publishAccess)
refDefs, originalRefBlockIDs = model.FilterRefDefsByPublishIgnore(publishIgnore, refDefs)
```
`FilterRefDefsByPublishIgnore` (`kernel/model/publish_access.go:1324`) collects the reference and definition identifiers, resolves their block trees, and delegates the decision to `FilterBlockTreesByPublishIgnore` (`:1314`), whose entire body is:
```go
for id, bt := range bts {
if CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) {
ret[id] = bt
}
}
```
Inside that helper, `CheckPublishAuthCookie`, `GetPathPasswordByPublishAccess`, `checkBlockTreeAccessableByPublishAccess` and `password` all appear zero times.
**The complete check exists in the same file.** `kernel/model/publish_access.go:431`:
```go
func checkBlockTreeAccessableByPublishAccess(c *gin.Context, publishAccess PublishAccess, bt *treenode.BlockTree) bool {
if bt == nil || IsEncryptedBoxDeniedByPublishAccess(bt.BoxID) {
return false
}
publishIgnore := filterDisablePublishAccess(publishAccess)
passwordID, password := GetPathPasswordByPublishAccess(bt.BoxID, bt.Path, publishAccess)
return CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) &&
(password == "" || CheckPublishAuthCookie(c, passwordID, password))
}
```
Same package, same file, same `*treenode.BlockTree` input. One takes the context and evaluates the password; the other does not receive it and structurally cannot.
**Route-level contrast.** The adjacent `getChildBlocks` and `getTailChildBlocks` both carry `model.CheckAdminRole`.
**Note on a prior assessment.** `getRefIDs` has been described as correctly filtered because it calls a publish-access filter. It does. The filter it calls covers the visibility tiers only.
### Proof of Concept
Kernel 3.7.2, publish mode on port 6808, `Publish.Auth.Enable` false, anonymous client. A public document referenced from a second document whose publish tier was changed between runs. `getRefIDs` was called anonymously each time.
| Tier of the referring document | Anonymous result |
|---|---|
| Public | reference returned (baseline) |
| Password-protected | **reference still returned** |
| Hidden | `[]`, filtered |
| Forbidden | `[]`, filtered |
The hidden and forbidden rows confirm the filter runs and works. The password-protected row is the defect.
### Impact
An anonymous reader in publish mode, or any publish `RoleReader`, learns that a password-protected document contains a reference to a given block, without entering that document's password, and receives the block identifiers involved.
Scoped precisely: the response carries identifiers only. `type RefDefs { RefID string; DefIDs []string }`, returned alongside `originalRefBlockIDs`, a map of identifier to identifier. There is no reference text, title or content. The disclosure is the existence of a relationship, plus identifiers usable as input to other endpoints.
Confidentiality only.
### Suggested fix
Thread `*gin.Context` into `FilterRefDefsByPublishIgnore` and `FilterBlockTreesByPublishIgnore`, and use `checkBlockTreeAccessableByPublishAccess` in place of the bare `CheckPathAccessableByPublishIgnore` call, so the password tier and the encrypted-box check are both applied.
SiYuan versions before v3.7.4 have an information disclosure vulnerability in the /api/block/getRefIDs endpoint. This endpoint does not properly enforce password protection tiers, allowing unauthenticated users to discover references to specific blocks within password-protected documents without needing the document password. The advisory is a duplicate and has been withdrawn in favor of GHSA-vg99-7gj7-2fr5.
A missing authorization vulnerability exists in SiYuan's development branch in the /api/av/getAttributeViewSearchTarget endpoint. This endpoint allows anonymous users to retrieve database row content without proper publish-access checks. The issue was introduced in a commit not present in stable versions and was patched in version 3.7.4. No stable released version is affected.
SiYuan versions up to and including v3.7.2 contain an authentication bypass vulnerability in the kernel's CheckAuth function. This flaw grants administrator privileges to any request originating from the loopback address (127.0.0.1) for certain endpoints, bypassing access auth codes. The vulnerability can potentially be exploited remotely via a fixed-port reverse proxy that forwards requests over loopback without proper authentication token injection or trusted proxy configuration. The issue was patched in version v3.7.4. This advisory is a duplicate and has been withdrawn in favor of GHSA-3mp7-4rh5-jrv9.
SiYuan versions up to and including v3.7.2 have an information disclosure vulnerability in the /api/asset/getFileAnnotation endpoint. This endpoint returns PDF annotation content without enforcing publish-access checks, allowing unauthorized access to private annotations of forbidden, password-protected, or unpublished documents. The vulnerability does not expose annotations in encrypted notebooks. It was fixed in version v3.7.4.
SiYuan versions before v3.7.4 contain a second-order SQL injection vulnerability in attribute-view template columns. This flaw allows execution of arbitrary SQL commands on the victim's kernel when a malicious package with crafted template columns is imported and rendered. The vulnerability arises because the queryBlocks function executes raw SQL using string substitution instead of parameterized queries. This enables attackers to gain read and write access across notebooks.
SiYuan versions before v3.7.4 have an authentication bypass vulnerability in the FilterViewByPublishAccess filter. This flaw allows unauthenticated users to access password-protected document rows, including sensitive metadata like titles and block IDs, without providing the required password. The advisory is marked as a duplicate of GHSA-6mcf-g667-w3qv and has been withdrawn to preserve external references.
SiYuan versions prior to v3.7.4 have a vulnerability where certain API endpoints do not enforce publish-access checks, allowing unauthorized disclosure of content and metadata from protected or forbidden documents. This affects the getBlockBreadcrumb, getRefText, and getBlockTreeInfos endpoints. Anonymous users or those with limited publish RoleReader permissions can access document titles, content snippets, reference text, and path metadata by supplying block IDs. The advisory is a duplicate and has been withdrawn in favor of GHSA-67x2-mq63-v9vm.
SiYuan kernel versions up to 3.7.2 contain a vulnerability where the getGraph and getLocalGraph endpoints do not validate the publish-password tier. This allows anonymous users to access block-level content of password-protected documents without providing a password. The vulnerability has a critical severity with a CVSS score of 8.6. The advisory is a duplicate of GHSA-vpjw-wf5h-cgpq and no patch links are provided in this record.
SiYuan kernel versions up to 3.7.2 have a vulnerability where the getBlockAttrs and batchGetBlockAttrs endpoints do not enforce publish-access checks. This allows attackers to retrieve block attributes such as names, aliases, memos, and custom fields from protected documents without authorization. The advisory is a duplicate and references GHSA-qvq9-hq6p-v378. No patch links are provided in this data.
SiYuan versions up to 3.7.2 have an information disclosure vulnerability in the resolveAssetPath endpoint. This endpoint returns absolute filesystem paths in response to CheckAuth-only requests, allowing attackers to obtain the server's absolute workspace path, including the operating system username and installation layout. The advisory is a duplicate of GHSA-jv8v-xq2h-657v and has been withdrawn to preserve references.
SiYuan versions up to 3.7.2 have a vulnerability where publish-access filters are not enforced on certain filetree path-resolution endpoints. This allows unauthenticated users or any publish reader token to enumerate the entire private document tree, including hidden, password-protected, or publish-forbidden documents. The vulnerability has a CVSS score of 5.8, indicating medium severity. This advisory is a duplicate and has been withdrawn in favor of GHSA-5w7r-f4cg-rqq7.
SiYuan versions up to 3.7.2 contain a vulnerability in the renderAttributeView function that fails to properly filter related-database content. This allows anonymous users to access Relation and Rollup cell contents from hidden or password-protected databases. The flaw also permits bypassing row filtering when the first column is a non-block type. This advisory is a duplicate of GHSA-mfrj-v65r-979c and has been withdrawn to preserve references.
SiYuan versions up to 3.7.2 have an information disclosure vulnerability in the getEncryptedNotebookStatus endpoint. This flaw allows anonymous users and publish-mode accounts to retrieve encrypted notebook identifiers, names, and their current unlock status without proper access control. The vulnerability exposes sensitive notebook names and whether they are unlocked in memory. The advisory is a duplicate of GHSA-f2rw-w22v-54vh and has been withdrawn to preserve references. The CVSS score is 5.8, indicating a medium severity issue.