{"id":"CVE-2026-115478","aliases":["EUVD-2026-109083","GHSA-r3g7-r4hr-m258"],"status":"published","title":"obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes","description":"obsidian-tc is a governed, agent-ready Model Context Protocol server for Obsidian vaults. From 1.0.1 until 1.31.4, `create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate memory notes from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path without reliably preserving note ownership or the consistency of a failed lifecycle operation. During shared materialization, a hand-authored note or an orphan left by a different entity whose name sanitized to the same path could have its entire body overwritten on the next write naming that type and name, with non-owned frontmatter preserved. `rename_entity` also seeded the destination path with a direct atomic write without validating ownership and could overwrite a foreign note there. Separately, several tools mutated SQLite before fallible filesystem work: `create_entity` and `link_entities` inserted the row or relation before materialization; `rename_entity` updated the entity, including a retired status, before updating its note; `unlink_entities` removed a relation before rematerializing the source note; and `delete_entity`'s cascade removed the target and its relations before rematerializing neighbouring notes. A later filesystem failure could therefore leave a row or relation changed and the memory graph inconsistent with the notes on disk even when the operation failed. An authorized caller with the required memory-tool permissions could, without intending to, destroy the body of a vault note outside the entity being written; `delete_entity` additionally requires its deletion permission and destructive confirmation, which this memory flaw does not bypass. Snapshots-before-write through `restore_note` mitigate the overwrite only where snapshots are enabled and the note had previously been written through obsidian-tc. This issue is fixed in version 1.31.4.","severity":{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00323,"percentile":0.25735,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-459","CWE-863"],"published":"2026-10-23T14:06:28Z","modified":"2026-11-01T02:30:42Z","fixes":[{"ecosystem":"npm","package":"obsidian-tc","fixed_in":["1.31.4"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"obsidian-tc","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.0.1"},{"last_affected":"1.31.3"},{"fixed":"1.31.4"}]}]},{"source":"osv","ecosystem":"npm","package":"obsidian-tc","ranges":[{"type":"SEMVER","events":[{"introduced":"1.0.1"},{"fixed":"1.31.4"}]}]}],"products":[{"vendor":"the-40-thieves","product":"obsidian-tc"}],"references":[{"url":"https://github.com/The-40-Thieves/obsidian-tc/security/advisories/GHSA-r3g7-r4hr-m258","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/The-40-Thieves/obsidian-tc/pull/978","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/The-40-Thieves/obsidian-tc/commit/99f7914ca0fbeed92e7c4d39cc8c3e5830dab133","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/The-40-Thieves/obsidian-tc/blob/v1.31.4/CHANGELOG.md","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/The-40-Thieves/obsidian-tc/releases/tag/v1.31.4","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-115478","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-r3g7-r4hr-m258"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115478.json","tags":["advisory"]},{"url":"https://github.com/The-40-Thieves/obsidian-tc","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-115478","title":"obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes","description":"obsidian-tc is a governed, agent-ready Model Context Protocol server for Obsidian vaults. From 1.0.1 until 1.31.4, `create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate memory notes from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path without reliably preserving note ownership or the consistency of a failed lifecycle operation. During shared materialization, a hand-authored note or an orphan left by a different entity whose name sanitized to the same path could have its entire body overwritten on the next write naming that type and name, with non-owned frontmatter preserved. `rename_entity` also seeded the destination path with a direct atomic write without validating ownership and could overwrite a foreign note there. Separately, several tools mutated SQLite before fallible filesystem work: `create_entity` and `link_entities` inserted the row or relation before materialization; `rename_entity` updated the entity, including a retired status, before updating its note; `unlink_entities` removed a relation before rematerializing the source note; and `delete_entity`'s cascade removed the target and its relations before rematerializing neighbouring notes. A later filesystem failure could therefore leave a row or relation changed and the memory graph inconsistent with the notes on disk even when the operation failed. An authorized caller with the required memory-tool permissions could, without intending to, destroy the body of a vault note outside the entity being written; `delete_entity` additionally requires its deletion permission and destructive confirmation, which this memory flaw does not bypass. Snapshots-before-write through `restore_note` mitigate the overwrite only where snapshots are enabled and the note had previously been written through obsidian-tc. This issue is fixed in version 1.31.4.","status":"published","published":"2026-10-23T14:06:28.128Z","modified":"2026-10-30T17:46:48.838Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high","assessor":"cna"}],"cwes":["CWE-459","CWE-863"],"updated_at":"2026-11-02T14:20:01.618694Z"}],"euvd":[{"id":"EUVD-2026-109083","description":"obsidian-tc is a governed, agent-ready Model Context Protocol server for Obsidian vaults. From 1.0.1 until 1.31.4, `create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate memory notes from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path without reliably preserving note ownership or the consistency of a failed lifecycle operation. During shared materialization, a hand-authored note or an orphan left by a different entity whose name sanitized to the same path could have its entire body overwritten on the next write naming that type and name, with non-owned frontmatter preserved. `rename_entity` also seeded the destination path with a direct atomic write without validating ownership and could overwrite a foreign note there. Separately, several tools mutated SQLite before fallible filesystem work: `create_entity` and `link_entities` inserted the row or relation before materialization; `rename_entity` updated the entity, including a retired status, before updating its note; `unlink_entities` removed a relation before rematerializing the source note; and `delete_entity`'s cascade removed the target and its relations before rematerializing neighbouring notes. A later filesystem failure could therefore leave a row or relation changed and the memory graph inconsistent with the notes on disk even when the operation failed. An authorized caller with the required memory-tool permissions could, without intending to, destroy the body of a vault note outside the entity being written; `delete_entity` additionally requires its deletion permission and destructive confirmation, which this memory flaw does not bypass. Snapshots-before-write through `restore_note` mitigate the overwrite only where snapshots are enabled and the note had previously been written through obsidian-tc. This issue is fixed in version 1.31.4.","status":"published","published":"2026-10-23T14:06:28Z","modified":"2026-10-30T17:46:48Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T16:59:03.071815Z"}],"ghsa":[{"id":"GHSA-r3g7-r4hr-m258","title":"obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes","description":"### Summary\n`create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate a memory note's body from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path. When a note already existed at that path and was not owned by the entity being written (a hand-authored note, or an orphan left by a different entity whose name sanitized to the same path), its entire body was overwritten on the next write naming that `(type, name)`, keeping only its frontmatter. Separately, several of these tools inserted or removed their SQLite row before the fallible filesystem step, so a refused note write could leave a relation gone, a retired status committed, or the wrong entity deleted.\n\n### Details\n- `create_entity` and `link_entities` inserted the SQL row or relation before materialization.\n- `rename_entity` seeded the destination path with a direct atomic write that bypassed the ownership check, and for a status-only rename could commit a retired status with the note write refused.\n- `unlink_entities` removed the relation row before discovering the source note's rematerialization would be refused.\n- `delete_entity`'s cascade could delete the target and its relations before a neighbour's note was found to be foreign.\n\nThe overwrite exists in every released version since `materializeEntity` shipped (`git show v1.0.1:packages/server/src/memory/materialize.ts`).\n\n### Impact\nAn authorized caller with memory-write scope (any agent using the M5 memory tools) can, without intending to, destroy the body of a vault note outside the entity it is writing, and can leave the memory graph inconsistent with the notes on disk. Snapshots-before-write (`restore_note`) mitigate the overwrite only where snapshots are enabled and the note had been written through obsidian-tc before.\n\n### Patches\nFixed in 1.31.4 at the shared primitive: `materializeEntity` refuses to write over a note whose `obsidian_tc_id` is missing or belongs to another entity; a new `assertNoteOwnership` pre-check runs before any SQLite mutation in the three lifecycle tools, so a refusal is a no-op; `create_entity` and `link_entities` roll back the row they had inserted. Verified by `test/m5-memory-ownership.test.ts`, which covers all five call sites and the rollback.\n\n### Workarounds\nNone that preserve the tools. Disabling the `write:memory` scope for callers prevents the write path entirely.\n\n### Credit\nFound by the maintainer while building `obsidian-tc memory import` (PR #978); the vulnerability class was independently confirmed by a cross-vendor adversarial review.\n\n### References\n- PR #978 (fix and test), CHANGELOG `## [1.31.4] ### Security`\n- `packages/server/src/memory/materialize.ts`, `packages/server/src/tools/m5/memory-lifecycle-tools.ts`\n\n---","status":"published","published":"2026-10-23T18:47:25Z","modified":"2026-10-23T18:47:40Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-459","CWE-863"],"updated_at":"2026-11-02T14:41:35.723785Z"}],"nvd":[{"id":"CVE-2026-115478","description":"obsidian-tc is a governed, agent-ready Model Context Protocol server for Obsidian vaults. From 1.0.1 until 1.31.4, `create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate memory notes from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path without reliably preserving note ownership or the consistency of a failed lifecycle operation. During shared materialization, a hand-authored note or an orphan left by a different entity whose name sanitized to the same path could have its entire body overwritten on the next write naming that type and name, with non-owned frontmatter preserved. `rename_entity` also seeded the destination path with a direct atomic write without validating ownership and could overwrite a foreign note there. Separately, several tools mutated SQLite before fallible filesystem work: `create_entity` and `link_entities` inserted the row or relation before materialization; `rename_entity` updated the entity, including a retired status, before updating its note; `unlink_entities` removed a relation before rematerializing the source note; and `delete_entity`'s cascade removed the target and its relations before rematerializing neighbouring notes. A later filesystem failure could therefore leave a row or relation changed and the memory graph inconsistent with the notes on disk even when the operation failed. An authorized caller with the required memory-tool permissions could, without intending to, destroy the body of a vault note outside the entity being written; `delete_entity` additionally requires its deletion permission and destructive confirmation, which this memory flaw does not bypass. Snapshots-before-write through `restore_note` mitigate the overwrite only where snapshots are enabled and the note had previously been written through obsidian-tc. This issue is fixed in version 1.31.4.","status":"published","published":"2026-10-23T15:17:30.023Z","modified":"2026-10-30T18:17:22.357Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"}],"cwes":["CWE-459","CWE-863"],"updated_at":"2026-11-02T14:13:03.126833Z"}],"osv":[{"id":"CVE-2026-115478","title":"obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes","description":"obsidian-tc is a governed, agent-ready Model Context Protocol server for Obsidian vaults. From 1.0.1 until 1.31.4, `create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate memory notes from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path without reliably preserving note ownership or the consistency of a failed lifecycle operation. During shared materialization, a hand-authored note or an orphan left by a different entity whose name sanitized to the same path could have its entire body overwritten on the next write naming that type and name, with non-owned frontmatter preserved. `rename_entity` also seeded the destination path with a direct atomic write without validating ownership and could overwrite a foreign note there. Separately, several tools mutated SQLite before fallible filesystem work: `create_entity` and `link_entities` inserted the row or relation before materialization; `rename_entity` updated the entity, including a retired status, before updating its note; `unlink_entities` removed a relation before rematerializing the source note; and `delete_entity`'s cascade removed the target and its relations before rematerializing neighbouring notes. A later filesystem failure could therefore leave a row or relation changed and the memory graph inconsistent with the notes on disk even when the operation failed. An authorized caller with the required memory-tool permissions could, without intending to, destroy the body of a vault note outside the entity being written; `delete_entity` additionally requires its deletion permission and destructive confirmation, which this memory flaw does not bypass. Snapshots-before-write through `restore_note` mitigate the overwrite only where snapshots are enabled and the note had previously been written through obsidian-tc. This issue is fixed in version 1.31.4.","status":"published","published":"2026-10-23T14:06:28.128Z","modified":"2026-11-01T02:30:42.837201751Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-459","CWE-863"],"updated_at":"2026-11-02T15:44:56.675758Z"},{"id":"GHSA-r3g7-r4hr-m258","title":"obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes","description":"### Summary\n`create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate a memory note's body from SQLite at the computed `memory/\u003ctype\u003e/\u003cname\u003e.md` path. When a note already existed at that path and was not owned by the entity being written (a hand-authored note, or an orphan left by a different entity whose name sanitized to the same path), its entire body was overwritten on the next write naming that `(type, name)`, keeping only its frontmatter. Separately, several of these tools inserted or removed their SQLite row before the fallible filesystem step, so a refused note write could leave a relation gone, a retired status committed, or the wrong entity deleted.\n\n### Details\n- `create_entity` and `link_entities` inserted the SQL row or relation before materialization.\n- `rename_entity` seeded the destination path with a direct atomic write that bypassed the ownership check, and for a status-only rename could commit a retired status with the note write refused.\n- `unlink_entities` removed the relation row before discovering the source note's rematerialization would be refused.\n- `delete_entity`'s cascade could delete the target and its relations before a neighbour's note was found to be foreign.\n\nThe overwrite exists in every released version since `materializeEntity` shipped (`git show v1.0.1:packages/server/src/memory/materialize.ts`).\n\n### Impact\nAn authorized caller with memory-write scope (any agent using the M5 memory tools) can, without intending to, destroy the body of a vault note outside the entity it is writing, and can leave the memory graph inconsistent with the notes on disk. Snapshots-before-write (`restore_note`) mitigate the overwrite only where snapshots are enabled and the note had been written through obsidian-tc before.\n\n### Patches\nFixed in 1.31.4 at the shared primitive: `materializeEntity` refuses to write over a note whose `obsidian_tc_id` is missing or belongs to another entity; a new `assertNoteOwnership` pre-check runs before any SQLite mutation in the three lifecycle tools, so a refusal is a no-op; `create_entity` and `link_entities` roll back the row they had inserted. Verified by `test/m5-memory-ownership.test.ts`, which covers all five call sites and the rollback.\n\n### Workarounds\nNone that preserve the tools. Disabling the `write:memory` scope for callers prevents the write path entirely.\n\n### Credit\nFound by the maintainer while building `obsidian-tc memory import` (PR #978); the vulnerability class was independently confirmed by a cross-vendor adversarial review.\n\n### References\n- PR #978 (fix and test), CHANGELOG `## [1.31.4] ### Security`\n- `packages/server/src/memory/materialize.ts`, `packages/server/src/tools/m5/memory-lifecycle-tools.ts`\n\n---","status":"published","published":"2026-10-23T18:47:25Z","modified":"2026-10-23T19:00:40.500298795Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-459","CWE-863"],"updated_at":"2026-11-02T15:46:55.122811Z"}]}}
