CVE-2026-40259 — kernel
HIGHCVE-2026-40259 is a high-severity (CVSS 8.1) CWE-285 vulnerability in github.com/siyuan-note/siyuan/kernel. A fix is available for github.com/siyuan-note/siyuan/kernel — see the affected versions and patch details below.
SiYuan: Publish Reader Can Arbitrarily Delete Attribute View Files via removeUnusedAttributeView API
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-40259.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
github.com/siyuan-note/siyuan/kernelReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
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
ClaimsKeyRole: RoleReader,
2. The publish reverse proxy forwards that token upstream
3. CheckAuth accepts RoleReader
if role := GetGinContextRole(c); IsValidRole(role, []Role{
RoleAdministrator,
RoleEditor,
RoleReader,
}) {
c.Next()
return
}
4. The route is exposed with CheckAuth only
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
avID := arg["id"].(string)
model.RemoveUnusedAttributeView(avID)
6. The model deletes the AV file unconditionally
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
- Attacker browses published content containing an attribute view.
- Attacker extracts the
data-av-idvalue from the page/DOM. - Attacker sends a POST request to
/api/av/removeUnusedAttributeViewthrough the publish service. - Publish proxy injects a valid
RoleReadertoken. CheckAuthaccepts the request.- The handler passes the attacker-controlled
avIDtomodel.RemoveUnusedAttributeView. - The backend deletes
data/storage/av/<avID>.json.
Proof of Concept
Request:
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:
- 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:
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(...).
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/siyuan-note/siyuan/kernel | all versions | 0.0.0-20260407035653-2f416e5253f1go get github.com/siyuan-note/siyuan/kernel@v0.0.0-20260407035653-2f416e5253f1 |
Affected Products
siyuanb3logDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/siyuan-note/siyuan/kernel, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update github.com/siyuan-note/siyuan/kernel to 0.0.0-20260407035653-2f416e5253f1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-40259 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Frequently Asked Questions
Is CVE-2026-40259 in your dependencies?
Find it across Go, including transitive dependencies.