---
id: CVE-2026-68587
aliases:
  - GHSA-69mh-gvh4-8gp7
  - GO-2026-6381
title: >-
  SiYuan: Full-content disclosure of publish-disabled documents via
  getHeading*Transaction endpoints (publish mode): reader-reachable rende…
summary: >-
  SiYuan: Full-content disclosure of publish-disabled documents via
  getHeading*Transaction endpoints (publish mode): reader-reachable rendered DOM
  with no publish-access check
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-20260721013353-69db783b782a
patched:
  - github.com/siyuan-note/siyuan/kernel 0.0.0-20260721013353-69db783b782a
published: '2026-09-03'
updated: '2026-09-10'
sourceUpdated: '2026-09-10T15:26:00.079898016Z'
source: OSV
sourceUrl: 'https://osv.dev/vulnerability/GHSA-69mh-gvh4-8gp7'
references:
  - url: >-
      https://github.com/siyuan-note/siyuan/security/advisories/GHSA-69mh-gvh4-8gp7
  - url: 'https://nvd.nist.gov/vuln/detail/CVE-2026-68587'
  - url: >-
      https://github.com/siyuan-note/siyuan/commit/69db783b782aea865ea70a1c5ab656e5c0f3dadb
  - url: 'https://github.com/siyuan-note/siyuan'
  - url: >-
      https://www.vulncheck.com/advisories/siyuan-before-information-disclosure-via-getheading-transaction
  - url: 'https://github.com/advisories/GHSA-69mh-gvh4-8gp7'
tags:
  - osv
  - go
  - ghsa
epss: 0.00245
epssPercentile: 0.16071
cwe:
  - CWE-862
ingestedAt: '2026-09-03T22:09:24.133Z'
---

## Overview

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

### Summary

Three "heading transaction" endpoints `/api/block/getHeadingDeleteTransaction`, `/api/block/getHeadingLevelTransaction`, and `/api/block/getHeadingInsertTransaction` return the rendered block DOM of a heading and its subtree in the computed transaction payload, with no publish-access check. They are gated by `CheckAuth` only, so they are reachable by the publish `RoleReader` token, and by the anonymous account when `Publish.Auth.Enable` is `false`.

Despite their write-implying names, these endpoints perform no mutation on this path, they compute a transaction object and return it, and that object contains `RenderNodeBlockDOM` output. As a result, an anonymous reader who supplies a heading block ID can read the full rendered content of a document that has been explicitly marked publish-disabled by an administrator, content that the reader-facing `/api/filetree/getDoc` path correctly refuses to return.

### Details

**Route / auth tier.** All three routes are registered `CheckAuth`-only (no `CheckAdminRole`). `CheckAuth` admits `RoleReader`, and the publish proxy forwards port-6808 traffic with a Reader JWT (anonymous account when publish auth is disabled). Anonymous/reader reachable.

**No publish-access filter.** `GetHeadingDeleteTransaction` / `GetHeadingLevelTransaction` / `GetHeadingInsertTransaction` load the heading's block tree and render its DOM into the returned transaction's operation data. None of the three invokes `IsReadOnlyRoleContext` / the publish-access filter that the reader-safe content path (`getDoc`) applies. The reader-facing content endpoints (`getDoc`, and the `CheckAdminRole`-gated `getBlockDOM`/`getBlockKramdown`) treat rendered block DOM as gated content; these three transaction endpoints return the same rendered DOM without that gate.

**Write-implying name, read behavior.** On this code path the endpoints only *compute* the transaction (e.g. the `undoOperations` a delete would produce) and return it; nothing is deleted or modified. The returned operation data includes the rendered HTML of the heading subtree. This mismatch, mutation-shaped name, content-returning behavior is why the gap is easy to miss.

### Proof of Concept

Reproduced on a local instance (SiYuan running locally, publish mode enabled on port 6808, publish Basic Auth disabled). `DOC` is a document containing a heading `HEADING` whose body contains the unique marker `UNIQUE_MARKER_99`, and `DOC` is marked publish-disabled by the administrator.

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

**2. Baseline: the reader-safe content path correctly blocks it (anonymous, port 6808):**
```
POST http://127.0.0.1:6808/api/filetree/getDoc
{"id":"DOC"}
```
Returns the blocked/placeholder response, the publish filter is applied, no content.

**3. Disclosure: the transaction endpoint returns the content (anonymous, port 6808):**
```
POST http://127.0.0.1:6808/api/block/getHeadingDeleteTransaction
{"id":"HEADING"}
```
Returns HTTP 200; `data.undoOperations[].data` contains the rendered HTML of the heading subtree, including the hidden body text `UNIQUE_MARKER_99`. Nothing is deleted — the endpoint only computes the transaction. This is the full content of a publish-disabled document returned to an anonymous reader.

**4. Sibling endpoints: same disclosure, same input:**
```
POST http://127.0.0.1:6808/api/block/getHeadingLevelTransaction
{"id":"HEADING","level":2}

POST http://127.0.0.1:6808/api/block/getHeadingInsertTransaction
{"id":"HEADING"}
```
Both return the rendered DOM of the heading subtree in their operation data.

### Impact

An anonymous reader (publish mode with auth disabled) or any publish `RoleReader` who supplies a heading block ID can read the full rendered content of a publish-disabled document, defeating a boundary the administrator explicitly configured.

**Precondition:** the request requires the target heading's block ID. Block IDs are high-entropy and are not returned by this endpoint, so this endpoint alone does not permit untargeted enumeration of arbitrary documents. However, block/heading IDs for publish-disabled documents are obtainable from other `CheckAuth`-only endpoints that lack the publish-access filter (the publish-boundary handler set reported separately, e.g. `getHeadingChildrenIDs`). Chained with such an ID source, this yields end-to-end unauthenticated full-content disclosure of publish-disabled documents. Presented standalone, the attacker must already possess the heading ID.

Impact is confidentiality-only: these endpoints return content but do not (on this path) modify data. 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 content path uses `IsReadOnlyRoleContext` / the publish-access filter used by `getDoc` to all three `getHeading*Transaction` handlers, or gate them behind `CheckAdminRole` consistent with `getBlockDOM`/`getBlockKramdown`. Any endpoint that returns rendered block DOM should enforce the same publish boundary as the primary content path.

## Affected packages

- `github.com/siyuan-note/siyuan/kernel < 0.0.0-20260721013353-69db783b782a`

## Remediation

Upgrade to a patched release:

- `github.com/siyuan-note/siyuan/kernel 0.0.0-20260721013353-69db783b782a`
