CVE-2026-40259: SiYuan Publish Reader Can Arbitrarily Delete Vulnerability

HIGH
Advisory Identifier: GHSA-7m5h-w69j-qggg
CVSS 8.1
CWE-285
Published
Advisory Summary

CVE-2026-40259 is a HIGH (CVSS 8.1) publish reader can arbitrarily delete vulnerability (CWE-285: Improper Authorization) affecting SiYuan libraries in the Go ecosystem. The issue involves publish reader can arbitrarily delete attribute view files via `/api/av/removeunusedattributeview`. Remediation is available by upgrading affected packages to version 0.0.0-20260407035653-2f416e5253f1.

CVE-2026-40259 at a Glance

CVE Identifier CVE-2026-40259
GHSA Identifier GHSA-7m5h-w69j-qggg
Severity HIGH
CVSS v3.1 Base Score 8.1
CWE Classification CWE-285: Improper Authorization
Affected Product SiYuan
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 8.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-40259

To remediate this vulnerability, upgrade all affected SiYuan dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/siyuan-note/siyuan/kernel fixed: 0.0.0-20260407035653-2f416e5253f1
Upgrade github.com/siyuan-note/siyuan/kernel to 0.0.0-20260407035653-2f416e5253f1
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/siyuan-note/siyuan/kernel

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-40259, inspect your project dependencies for SiYuan:

inspect dependencies for github.com/siyuan-note/siyuan/kernel

Analyze the terminal output to locate any version of SiYuan in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.0.0-20260407035653-2f416e5253f1), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-285: Improper Authorization, where the product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action. In SiYuan, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## 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](/root/audit/siyuan/kernel/model/auth.go#L105) ```go ClaimsKeyRole: RoleReader, ``` ### 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) ```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](/root/audit/siyuan/kernel/api/router.go#L507) ```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](/root/audit/siyuan/kernel/api/av.go#L32) ```go avID := arg["id"].(string) model.RemoveUnusedAttributeView(avID) ``` ### 6. The model deletes the AV file unconditionally - [kernel/model/attribute_view.go](/root/audit/siyuan/kernel/model/attribute_view.go#L49) ```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](/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 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(...)`.

CVE-2026-40259 Affected SiYuan Versions

1 component(s)
Fixed Versions:
0.0.0-20260407035653-2f416e5253f1
Product / Package Ecosystem Affected Versions Fixed Version
github.com/siyuan-note/siyuan/kernel
Go
< 0.0.0-20260407035653-2f416e5253f1
0.0.0-20260407035653-2f416e5253f1

References

Frequently Asked Questions

What is CVE-2026-40259?
CVE-2026-40259 is a security vulnerability in SiYuan involving Publish Reader Can Arbitrarily Delete Attribute View Files via `/api/av/removeUnusedAttributeView` under CWE-285: Improper Authorization.
Which SiYuan versions are affected?
Affected versions include releases prior to 0.0.0-20260407035653-2f416e5253f1 across affected packages: github.com/siyuan-note/siyuan/kernel.
What version fixes CVE-2026-40259?
Upgrading to 0.0.0-20260407035653-2f416e5253f1 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/siyuan-note/siyuan/kernel affected?
Yes, github.com/siyuan-note/siyuan/kernel is affected in releases prior to 0.0.0-20260407035653-2f416e5253f1. Upgrade your dependency to 0.0.0-20260407035653-2f416e5253f1 or later.
What is the CVSS score of CVE-2026-40259?
CVE-2026-40259 has a CVSS v3.1 base score of 8.1 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H.
What CWE is associated with CVE-2026-40259?
CVE-2026-40259 is classified under CWE-285: Improper Authorization (The product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.).
How can I check Go dependencies for CVE-2026-40259?
Run the dependency tree command: 'inspect dependencies for github.com/siyuan-note/siyuan/kernel'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-7m5h-w69j-qggg",
  "modified" : "2026-04-24T20:36:59Z",
  "published" : "2026-04-10T19:32:07Z",
  "aliases" : [ "CVE-2026-40259" ],
  "summary" : "SiYuan: Publish Reader Can Arbitrarily Delete Attribute View Files via `/api/av/removeUnusedAttributeView`",
  "details" : "## Summary\n\nAn authenticated publish-service reader can invoke `/api/av/removeUnusedAttributeView` and cause persistent deletion of arbitrary attribute view (`AV`) definition files from the workspace.\n\nThe 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:\n\n- that the caller is allowed to perform write/destructive actions; or\n- that the target AV is actually unused.\n\nThis is a persistent integrity and availability issue reachable from the publish surface.\n\n## Root Cause\n\n### 1. Publish users are issued a `RoleReader` JWT\n\n- [kernel/model/auth.go](/root/audit/siyuan/kernel/model/auth.go#L105)\n\n```go\nClaimsKeyRole: RoleReader,\n```\n\n### 2. The publish reverse proxy forwards that token upstream\n\n- [kernel/server/proxy/publish.go](/root/audit/siyuan/kernel/server/proxy/publish.go#L131)\n- [kernel/server/proxy/publish.go](/root/audit/siyuan/kernel/server/proxy/publish.go#L186)\n\n### 3. `CheckAuth` accepts `RoleReader`\n\n- [kernel/model/session.go](/root/audit/siyuan/kernel/model/session.go#L201)\n\n```go\nif role := GetGinContextRole(c); IsValidRole(role, []Role{\n    RoleAdministrator,\n    RoleEditor,\n    RoleReader,\n}) {\n    c.Next()\n    return\n}\n```\n\n### 4. The route is exposed with `CheckAuth` only\n\n- [kernel/api/router.go](/root/audit/siyuan/kernel/api/router.go#L507)\n\n```go\nginServer.Handle(\"POST\", \"/api/av/removeUnusedAttributeView\", model.CheckAuth, removeUnusedAttributeView)\n```\n\nThere is no `CheckAdminRole` and no `CheckReadonly`.\n\n### 5. The handler forwards attacker-controlled `id` directly to the delete sink\n\n- [kernel/api/av.go](/root/audit/siyuan/kernel/api/av.go#L32)\n\n```go\navID := arg[\"id\"].(string)\nmodel.RemoveUnusedAttributeView(avID)\n```\n\n### 6. The model deletes the AV file unconditionally\n\n- [kernel/model/attribute_view.go](/root/audit/siyuan/kernel/model/attribute_view.go#L49)\n\n```go\nfunc RemoveUnusedAttributeView(id string) {\n    absPath := filepath.Join(util.DataDir, \"storage\", \"av\", id+\".json\")\n    if !filelock.IsExist(absPath) {\n        return\n    }\n    ...\n    if err = filelock.RemoveWithoutFatal(absPath); err != nil {\n        ...\n        return\n    }\n    IncSync()\n}\n```\n\nCrucially, 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\".\n\n## Attack Prerequisites\n\n- Publish service enabled\n- Attacker can access the publish service\n- If publish auth is enabled, attacker has valid publish-reader credentials\n- Attacker knows an `avID`\n\n## Obtaining `avID`\n\n`avID` is not secret. It is exposed extensively in frontend markup as `data-av-id`.\n\nExamples:\n\n- [app/src/protyle/render/av/render.ts](/root/audit/siyuan/app/src/protyle/render/av/render.ts#L117)\n- [app/src/protyle/render/av/layout.ts](/root/audit/siyuan/app/src/protyle/render/av/layout.ts#L120)\n- [app/src/protyle/render/av/groups.ts](/root/audit/siyuan/app/src/protyle/render/av/groups.ts#L52)\n\nAny publish-visible database/attribute view can therefore disclose a valid `avID` to the attacker.\n\n## Exploit Path\n\n1. Attacker browses published content containing an attribute view.\n2. Attacker extracts the `data-av-id` value from the page/DOM.\n3. Attacker sends a POST request to `/api/av/removeUnusedAttributeView` through the publish service.\n4. Publish proxy injects a valid `RoleReader` token.\n5. `CheckAuth` accepts the request.\n6. The handler passes the attacker-controlled `avID` to `model.RemoveUnusedAttributeView`.\n7. The backend deletes `data/storage/av/<avID>.json`.\n\n## Proof of Concept\n\nRequest:\n\n```http\nPOST /api/av/removeUnusedAttributeView HTTP/1.1\nHost: <publish-host>:<publish-port>\nContent-Type: application/json\nAuthorization: Basic <publish-account-creds-if-enabled>\n\n{\n  \"id\": \"<exposed-data-av-id>\"\n}\n```\n\nExpected result:\n\n- HTTP 200\n- backend increments sync state\n- the target attribute view file is removed from `data/storage/av/`\n- published and local workspace behavior for that AV becomes broken until restored from history or recreated\n\n## Impact\n\nThis gives a low-privileged publish reader a destructive persistent write primitive against workspace data.\n\nPractical consequences include:\n\n- deletion of live attribute view definitions\n- corruption/breakage of published database views\n- breakage of local workspace rendering and AV-backed relationships\n- operational disruption until restore or manual repair\n\nThe bug affects integrity and availability, not merely UI state.\n\n## Recommended Fix\n\nAt minimum:\n\n1. Block publish/read-only users from this route.\n2. Require admin/write authorization.\n3. Re-validate that the target AV is actually unused before deletion.\n\nSafe router fix:\n\n```go\nginServer.Handle(\"POST\", \"/api/av/removeUnusedAttributeView\",\n    model.CheckAuth,\n    model.CheckAdminRole,\n    model.CheckReadonly,\n    removeUnusedAttributeView,\n)\n```\n\nAnd inside the model or handler, reject deletion unless the target `id` is present in `UnusedAttributeViews(...)`.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/siyuan-note/siyuan/kernel"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.0.0-20260407035653-2f416e5253f1"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-7m5h-w69j-qggg"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-40259"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/siyuan-note/siyuan"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/releases/tag/v3.6.4"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-285" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-04-10T19:32:07Z",
    "nvd_published_at" : "2026-04-16T23:16:33Z"
  }
}