A AegiFlow
HIGHCVSS 8.6

GHSA-9cqf-hhrq-7v45

SiYuan: getAttributeViewSearchTarget returns database row content to anonymous readers with no publish-access check, reopening the class closed one day earlier at the adjacent route

Published
2026-10-01
Modified
2026-10-01
Sources
github-advisory

Summary

### Scope note This endpoint does not exist in v3.7.3 or on master. It was introduced on the development branch by commit `9b8e8956f` on 2026-07-27 and is present on the current development head. No released stable version is affected. ### Summary `/api/av/getAttributeViewSearchTarget` is registered with `CheckAuth` only and performs no authorization of any kind. Given a database identifier and a keyword, it searches that database's rows and returns matching content, regardless of whether the caller may see the database, the rows, or the notebook holding them. Commit `64c26e74b` on 2026-07-26 closed a reader-reachable gap on `getAttributeViewFieldViews` by adding `CheckReadonly` at router line 536. `9b8e8956f`, the following day, registered this endpoint at line 535 without it. The new route returns more than the one that was just closed: row content rather than field-visibility metadata. ### Details **Route.** `kernel/api/router.go:535` on the development branch: ```go ginServer.Handle("POST", "/api/av/getAttributeViewSearchTarget", model.CheckAuth, getAttributeViewSearchTarget) ``` No `CheckReadonly`, no `CheckAdminRole`. **Handler.** `kernel/api/av.go` parses its arguments and calls `model.GetAttributeViewSearchTarget(id, keywords)`. `IsReadOnlyRoleContext`, `publishAccess` and any encrypted-notebook check are all absent. The sink is `kernel/model/attribute_view_render.go:69`. **The adjacent routes make the omission stark.** Lines 533 to 541 all handle the same data class: | Line | Route | Guard | |---|---|---| | 534 | `getAttributeViewKeys` | `CheckAuth`, filters in-body for readers | | 535 | `getAttributeViewSearchTarget` | `CheckAuth`, nothing | | 536 | `getAttributeViewFieldViews` | `CheckAuth`, `CheckReadonly` | | 540 | `searchAttributeView` | `CheckAuth`, `CheckReadonly` | | 541 | `getAttributeView` | `CheckAuth`, `CheckReadonly` | The guard at 536 is the one added by `64c26e74b`. The new route sits directly above it without one. **Why this is not bounded by identifier guessing.** The closest functional sibling, `renderAttributeView`, applies two distinct reader protections: 1. A containing-document gate, `CheckAttributeViewBlockAccessableByPublishAccess`, which is the full gate including `CheckPublishAuthCookie`. 2. Per-item filtering, `FilterAttributeViewByPublishAccess`, using the `itemAccess` and `attributeAccess` maps. This strips rows bound to restricted blocks out of views the reader is otherwise entitled to see. The second layer is what makes this exploitable without guesswork. A reader takes a database block identifier from the published DOM of a page they are allowed to view, then queries this endpoint against that same database and receives precisely the rows `renderAttributeView` removed from the view they were served. `GetAttributeViewSearchTarget` reaches the same internal render function; it simply bypasses the API-layer gate. **Two aggravating properties.** `getAttributeViewSearchMatches` runs `strings.Contains` across every value of every key, and the response carries `MatchedKeyID` alongside the title. That is a substring oracle over every column, so a caller can test for content without needing to retrieve it wholesale. The path also branches on `IsEncryptedBox(tree.Box)` into `ParseAttributeViewInBox`, so it reads encrypted notebooks while they are unlocked. `isEncryptedNotebookDeniedForPublish` appears seventeen times across the block, ref, outline, search and filetree paths, and does not appear in `av.go` at all. **Extraction is iterative.** Each request returns a single row. Systematic recovery of a database therefore requires many requests rather than one bulk response. There is no rate limiting on the path, and the substring oracle narrows the search considerably, but I mention it so the effort is not overstated. ### Proof of Concept Precondition: a development-branch build, publish mode enabled (default port 6808), anonymous when `Publish.Auth.Enable` is `false`. A publis

Affected packages

EcosystemPackageAffected versionsFixed versions
Gogithub.com/siyuan-note/siyuan/kernel—0.0.0-20260812083335-251596fc0de2

Remediation: Upgrade to 0.0.0-20260812083335-251596fc0de2 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.