# SiYuan: Publish-access filter on renderAttributeView leaves related-database content unfiltered and fails open on non-block first columns

SiYuan's publish-access filter on renderAttributeView only checks a row's first cell, leaking Relation/Rollup content from hidden databases and failing open on non-block first columns.

- Payout: Research
- Severity: high
- Program: SiYuan
- Found by (vendor credit): Shirshakhtml
- Found by: Shirshakhtml
- Disclosed: 2026-09-04
- CVE: CVE-2026-72798
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-mfrj-v65r-979c
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-mfrj-v65r-979c
- Tags: area:web domain:oss 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:** kernel API endpoint /api/av/renderAttributeView (attribute view renderer)
**Root cause:** FilterViewByPublishAccess keys row accessibility solely on row.Cells[0].Value.Block and never evaluates Relation.Contents or Rollup.Contents, which mirror content from other databases; when column 0 is not a block, bt stays nil and the row is returned unchecked.
**Steps:**
1. Enable publish mode (default port 6808) with Publish.Auth.Enable=false for anonymous access.
2. Create DB-A in a published document and DB-B in a publish-forbidden or password-protected document; add a Relation column in DB-A pointing at DB-B with a distinctive marker value.
3. POST `http://127.0.0.1:6808/api/av/renderAttributeView` with body `{"id":"<DB_A_AV_ID>"}`.
4. Observe DB-A rows returned and Relation/Rollup cell `Contents` containing DB-B block text and mirrored column values.
5. For fail-open: reorder DB-A so its first column is a non-block type (or use a detached row), mark its host document publish-forbidden, and repeat the request; the row is returned with no accessibility check.
**How found:** Code review of FilterViewByPublishAccess: noticed the accessibility decision reads only row.Cells[0].Value.Block, that the `if bt != nil` guard silently skips the check when column 0 is not a block, and that ValueRelation/ValueRollup carry mirrored Contents populated from a separate attribute view (relationDestAv) never passed through publish-access evaluation.
**Exploitation:** An anonymous reader or any publish RoleReader requests a published database's renderAttributeView; surviving rows carry Relation/Rollup Contents mirrored from a hidden, publish-forbidden, or password-protected database, disclosing its block text, titles and column values. Reordering the first column to a non-block type bypasses row filtering entirely.
**Insight:** When auditing publish/ACL filters over structured records, check whether the filter inspects only a primary key cell while denormalized mirror fields (relations, rollups, lookups) carry foreign content; also test fail-open branches where the guard variable is nil for user-reorderable layouts.

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

**POST to renderAttributeView for related-database leak**

```
POST http://127.0.0.1:6808/api/av/renderAttributeView
{"id":"<DB_A_AV_ID>"}
Rows of DB-A are returned (correctly, since its host document is public), and their Relation/Rollup cell `Contents` include DB-B's block text and mirrored column values, despite DB-B's host document being excluded from publishing.
```

