CVE-2026-40259: SiYuan: Publish Reader Can Arbitrarily Delete Attribute View Files via removeUnusedAttributeView API
Summary
An authenticated publish-service reader can invoke /api/av/removeUnusedAttributeView and cause persistent deletion of arbitrary attribute view (AV) definition files from the workspace.
The route is protected only by generic CheckAuth, which accepts publish RoleReader requests. The handler forwards a caller-controlled id directly into a model function that deletes data/storage/av/<id>.json without verifying either:
- that the caller is allowed to perform write/destructive actions; or - that the target AV is actually unused.
This is a persistent integrity and availability issue reachable from the publish surface.
Root Cause
1. Publish users are issued a RoleReader JWT
- kernel/model/auth.go
go ClaimsKeyRole: RoleReader,
2. The publish reverse proxy forwards that token upstream
- kernel/server/proxy/publish.go - kernel/server/proxy/publish.go
3. CheckAuth accepts RoleReader
- kernel/model/session.go
go if role := GetGinContextRole(c); IsValidRole(role, []Role{ RoleAdministrator, RoleEditor, RoleReader, }) { c.Next() return }
4. The route is exposed with CheckAuth only
- kernel/api/router.go
go ginServer.Handle("POST", "/api/av/removeUnusedAttributeView", model.CheckAuth, removeUnusedAttributeView)
There is no CheckAdminRole and no CheckReadonly.
5. The handler forwards attacker-controlled id directly to the delete sink
- kernel/api/av.go
go avID := arg["id"].(string) model.RemoveUnusedAttributeView(avID)
6. The model deletes the AV file unconditionally
- kernel/model/attributeview.go
go func RemoveUnusedAttributeView(id string) { absPath := filepath.Join(util.DataDir, "storage", "av", id+".json") if !filelock.IsExist(absPath) { return } ... if err = filelock.RemoveWithoutFatal(absPath); err != nil { ... return } IncSync() }
Crucially, this function does not verify that the supplied AV is actually unused. The name of the function suggests a cleanup helper, but the implementation is really "delete AV file by id if it exists".
Attack Prerequisites
- Publish service enabled - Attacker can access the publish service - If publish auth is enabled, attacker has valid publish-reader credentials - Attacker knows an avID
Obtaining avID
avID is not secret. It is exposed extensively in frontend markup as data-av-id.
Examples:
- app/src/protyle/render/av/render.ts - app/src/protyle/render/av/layout.ts - app/src/protyle/render/av/groups.ts
Any publish-visible database/attribute view can therefore disclose a valid avID to the attacker.
Exploit Path
1. Attacker browses published content containing an attribute view. 2. Attacker extracts the data-av-id value from the page/DOM. 3. Attacker sends a POST request to /api/av/removeUnusedAttributeView through the publish service. 4. Publish proxy injects a valid RoleReader token. 5. CheckAuth accepts the request. 6. The handler passes the attacker-controlled avID to model.RemoveUnusedAttributeView. 7. The backend deletes data/storage/av/<avID>.json.
Proof of Concept
Request:
http POST /api/av/removeUnusedAttributeView HTTP/1.1 Host: <publish-host>:<publish-port> Content-Type: application/json Authorization: Basic <publish-account-creds-if-enabled>
{ "id": "<exposed-data-av-id>" }
Expected result:
- HTTP 200 - backend increments sync state - the target attribute view file is removed from data/storage/av/ - published and local workspace behavior for that AV becomes broken until restored from history or recreated
Impact
This gives a low-privileged publish reader a destructive persistent write primitive against workspace data.
Practical consequences include:
- deletion of live attribute view definitions - corruption/breakage of published database views - breakage of local workspace rendering and AV-backed relationships - operational disruption until restore or manual repair
The bug affects integrity and availability, not merely UI state.
Recommended Fix
At minimum:
1. Block publish/read-only users from this route. 2. Require admin/write authorization. 3. Re-validate that the target AV is actually unused before deletion.
Safe router fix:
go ginServer.Handle("POST", "/api/av/removeUnusedAttributeView", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeUnusedAttributeView, )
And inside the model or handler, reject deletion unless the target id is present in UnusedAttributeViews(...).
Other sources
SiYuan is an open-source personal knowledge management system. In versions 3.6.3 and below, the /api/av/removeUnusedAttributeView endpoint is protected only by generic authentication that accepts publish-service RoleReader tokens. The handler passes a caller-controlled id directly to a model function that unconditionally deletes the corresponding attribute view file from the workspace without verifying that the caller has write privileges or that the target attribute view is actually unused. An authenticated publish-service reader can permanently delete arbitrary attribute view definitions by extracting publicly exposed data-av-id values from published content, causing breakage of database views and workspace rendering until manually restored. This issue has been fixed in version 3.6.4.
— MITRE
Affected Software
Event History
Frequently Asked Questions
What is the severity of CVE-2026-40259?
CVE-2026-40259 is considered a high severity vulnerability due to its potential for unauthorized deletion of arbitrary attribute view definition files.
How do I fix CVE-2026-40259?
To fix CVE-2026-40259, upgrade to a version of the affected software that is later than 0.0.0-20260407035653-2f416e5253f1.
Who is affected by CVE-2026-40259?
Users of the Siyuan Note software version 0.0.0-20260407035653-2f416e5253f1 and earlier are affected by CVE-2026-40259.
What type of authorization is involved in CVE-2026-40259?
CVE-2026-40259 is exposed due to inadequate authorization checks, allowing any authenticated user with the publish RoleReader to exploit the vulnerability.
What actions can be exploited due to CVE-2026-40259?
CVE-2026-40259 allows an authenticated user to invoke an API that can lead to the persistent deletion of arbitrary attribute view definition files.