GHSA-7m5h-w69j-qggg: SiYuan: Publish Reader Can Arbitrarily Delete Attribute View Files via `/api/av/removeUnusedAttributeView`
SummaryAn 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; orthat the target AV is actually unused.This is a persistent integrity and availability issue reachable from the publish surface.Root Cause1. Publish users are issued a `RoleReader` JWT[kernel/model/auth.go](/root/audit/siyuan/kernel/model/auth.go#L105)2. The publish reverse proxy forwards that token upstream[kernel/server/proxy/publish.go](/root/audit/siyuan/kernel/server/proxy/publish.go#L131)[kernel/server/proxy/publish.go](/root/audit/siyuan/kernel/server/proxy/publish.go#L186)3. `CheckAuth` accepts `RoleReader`[kernel/model/session.go](/root/audit/siyuan/kernel/model/session.go#L201)4. The route is exposed with `CheckAuth` only[kernel/api/router.go](/root/audit/siyuan/kernel/api/router.go#L507)There is no `CheckAdminRole` and no `CheckReadonly`.5. The handler forwards attacker-controlled `id` directly to the delete sink[kernel/api/av.go](/root/audit/siyuan/kernel/api/av.go#L32)6. The model deletes the AV file unconditionally[kernel/model/attribute_view.go](/root/audit/siyuan/kernel/model/attribute_view.go#L49)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 PrerequisitesPublish service enabledAttacker can access the publish serviceIf publish auth is enabled, attacker has valid publish-reader credentialsAttacker 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](/root/audit/siyuan/app/src/protyle/render/av/render.ts#L117)[app/src/protyle/render/av/layout.ts](/root/audit/siyuan/app/src/protyle/render/av/layout.ts#L120)[app/src/protyle/render/av/groups.ts](/root/audit/siyuan/app/src/protyle/render/av/groups.ts#L52)Any publish-visible database/attribute view can therefore disclose a valid `avID` to the attacker.Exploit PathAttacker browses published content containing an attribute view.Attacker extracts the `data-av-id` value from the page/DOM.Attacker sends a POST request to `/api/av/removeUnusedAttributeView` through the publish service.Publish proxy injects a valid `RoleReader` token.`CheckAuth` accepts the request.The handler passes the attacker-controlled `avID` to `model.RemoveUnusedAttributeView`.The backend deletes `data/storage/av/<avID>.json`.Proof of ConceptRequest:Expected result:HTTP 200backend increments sync statethe 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 recreatedImpactThis gives a low-privileged publish reader a destructive persistent write primitive against workspace data.Practical consequences include:deletion of live attribute view definitionscorruption/breakage of published database viewsbreakage of local workspace rendering and AV-backed relationshipsoperational disruption until restore or manual repairThe bug affects integrity and availability, not merely UI state.Recommended FixAt minimum:Block publish/read-only users from this route.Require admin/write authorization.Re-validate that the target AV is actually unused before deletion.Safe router fix:And inside the model or handler, reject deletion unless the target `id` is present in `UnusedAttributeViews(...)`.
CVSS v3.1: 8.1