---
id: CVE-2026-68586
aliases:
  - GHSA-36v8-mpjm-8j5r
  - GO-2026-6379
title: >-
  SiYuan: Cross-boundary content disclosure via getBacklinkDoc/getBackmentionDoc
  (publish mode): reader-reachable rendered DOM of publish-f…
summary: >-
  SiYuan: Cross-boundary content disclosure via getBacklinkDoc/getBackmentionDoc
  (publish mode): reader-reachable rendered DOM of publish-forbidden docs;
  sibling list endpoints are filtered
severity: high
cvss: 8.6
cvssVector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N'
vendor: siyuan-note
product: github.com/siyuan-note/siyuan/kernel
ecosystem: go
affected:
  - github.com/siyuan-note/siyuan/kernel < 0.0.0-20260721014413-f45749a7ef6e
patched:
  - github.com/siyuan-note/siyuan/kernel 0.0.0-20260721014413-f45749a7ef6e
published: '2026-09-03'
updated: '2026-09-10'
sourceUpdated: '2026-09-10T15:25:41.235385903Z'
source: OSV
sourceUrl: 'https://osv.dev/vulnerability/GHSA-36v8-mpjm-8j5r'
references:
  - url: >-
      https://github.com/siyuan-note/siyuan/security/advisories/GHSA-36v8-mpjm-8j5r
  - url: 'https://nvd.nist.gov/vuln/detail/CVE-2026-68586'
  - url: >-
      https://github.com/siyuan-note/siyuan/commit/f45749a7ef6e385f6e2af6b7dd12429d56d46f32
  - url: 'https://github.com/siyuan-note/siyuan'
  - url: >-
      https://www.vulncheck.com/advisories/siyuan-before-content-disclosure-via-getbacklinkdoc
  - url: 'https://github.com/advisories/GHSA-36v8-mpjm-8j5r'
tags:
  - osv
  - go
  - ghsa
epss: 0.00241
epssPercentile: 0.15576
cwe:
  - CWE-862
ingestedAt: '2026-09-03T22:09:24.090Z'
---

## Overview

**CVE:** This vulnerability corresponds to [CVE-2026-68586](https://nvd.nist.gov/vuln/detail/CVE-2026-68586).

### Summary

The backlink API splits into list endpoints (which documents reference a block) and content endpoints (the rendered text of those referencing blocks). The list endpoints apply a publish-access filter, the content endpoints do not. As a result, `/api/ref/getBacklinkDoc` and `/api/ref/getBackmentionDoc` return the rendered DOM of blocks belonging to a publish-forbidden document to an anonymous reader, with no access check.

Both content endpoints are gated by `CheckAuth` only, reachable by the publish `RoleReader` token and by the anonymous account when `Publish.Auth.Enable` is `false`.

### Details

The asymmetry between the list and content sides is the tell that this is an oversight, not intended behavior:

| Endpoint | Returns | Publish-access filter | Route |
|---|---|---|---|
| `getBacklink` | list (`*Path`) | `FilterPathsByPublishAccess` - present | `CheckAuth` |
| `getBacklink2` | list (`*Path`) | `FilterPathsByPublishAccess` - present | `CheckAuth` |
| `getBacklinkDoc` | rendered DOM | none | `CheckAuth` |
| `getBackmentionDoc` | rendered DOM | none | `CheckAuth` |

`model/backlink.go` contains no publish-access reference anywhere, and `Backlink.DOM` is the rendered HTML of the referencing blocks. `getBacklinkDoc(defID, refTreeID)` returns the rendered content of blocks in `refTreeID` - including a publish-forbidden, publish-disabled, or password-protected document with no access check. The filtered list siblings (`getBacklink`/`getBacklink2`) demonstrate that the publish boundary is meant to apply to this data; the content endpoints simply omit it.

A reader is not limited to the filtered backlink list, they call `getBacklinkDoc` directly with any `refTreeID`. This also yields a reference-existence oracle: the response reveals whether the forbidden document `refTreeID` references the block `defID`.

### Proof of Concept

Reproduced on a local instance (SiYuan running locally, publish mode enabled on port 6808, publish Basic Auth disabled). Setup: a publish-forbidden document `D` (`REFTREEID`) whose body contains the unique marker `SECRET_MARKER_77` and which references a block `DEFID` in a separate known document.

**1. Mark the target document publish-forbidden (admin action, the boundary that should block reads):**
```
POST http://127.0.0.1:6806/api/filetree/setPublishAccess
Authorization: Token <admin-token>
{"id":"REFTREEID","visible":false,"password":"","disable":true}
```

**2. Baseline: the list endpoint correctly hides the forbidden doc from the reader (anonymous, port 6808):**
```
POST http://127.0.0.1:6808/api/ref/getBacklink2
{"id":"DEFID","k":"","mk":""}
```
The returned backlinks do not include the forbidden document `D`, the list side is filtered.

**3. Disclosure: the content endpoint returns the forbidden doc's blocks anyway (anonymous, port 6808):**
```
POST http://127.0.0.1:6808/api/ref/getBacklinkDoc
{"defID":"DEFID","refTreeID":"REFTREEID","keyword":""}
```
Returns HTTP 200; `data.backlinks[].dom` contains `SECRET_MARKER_77`, the rendered content of the publish-forbidden document, returned to an anonymous reader. `getBackmentionDoc` behaves identically for mention-type references.

### Impact

An anonymous reader (publish mode with auth disabled) or any publish `RoleReader`, can read the rendered content of a publish-forbidden document's referencing blocks, defeating a boundary the administrator explicitly configured, and can determine whether a forbidden document references a given block (a reference-existence oracle).

**Precondition (stated honestly):** the request requires `refTreeID` (the forbidden document's ID) and `defID` (a block it references). `defID` may be any published/known block, so if the forbidden document references any public content, `defID` is known and only `refTreeID` need be supplied. Block/document IDs for forbidden documents are also obtainable from other `CheckAuth`-only endpoints that lack the publish-access filter (reported separately). This endpoint alone does not enumerate arbitrary documents; it discloses content once an ID is known.

Impact is confidentiality-only: content disclosure plus a reference-existence oracle, no modification. No admin role, CSRF token, or write permission is required. Encrypted notebooks are out of scope.

### Suggested fix

Apply the same publish-access check the list siblings use. In `getBacklinkDoc`/`getBackmentionDoc`, filter each `Backlink` by its source document's box/path via `CheckPathAccessableByPublishIgnore` plus the publish-password cookie check consistent with `FilterPathsByPublishAccess` in `getBacklink`/`getBacklink2`. Any endpoint returning rendered block DOM should enforce the same publish boundary as the corresponding list endpoint.

## Affected packages

- `github.com/siyuan-note/siyuan/kernel < 0.0.0-20260721014413-f45749a7ef6e`

## Remediation

Upgrade to a patched release:

- `github.com/siyuan-note/siyuan/kernel 0.0.0-20260721014413-f45749a7ef6e`
