---
id: "CVE-2026-115478"
kind: "vulnerability"
aliases: ["CVE-2026-115478", "GHSA-r3g7-r4hr-m258"]
title: "obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes"
severity: "high"
cvss: 7.1
epss: null
exploited: false
status: "open"
category: "ai-tooling"
products: [{"name": "obsidian-tc", "fixed": ["1.31.4"], "affected": ">= 1.0.1, <= 1.31.3", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["agents-with-memory-write", "ai-tooling", "claude-code", "cwe-459", "cwe-863", "mcp", "npm", "obsidian-tc"]
not_affected_if: ["no-obsidian-tc-installed", "no-write-memory-scope-granted", "version-1.31.4-or-later"]
published_at: "2026-10-23T15:17:30+00:00"
first_seen_at: "2026-10-23T19:11:31+00:00"
updated_at: "2026-10-23T21:57:34+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-r3g7-r4hr-m258", "first_seen_at": "2026-10-23T19:11:31+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-115478", "first_seen_at": "2026-10-23T21:57:34+00:00"}]
---

# obsidian-tc: Memory-lifecycle tools overwrite unowned notes or partially commit lifecycle changes

The obsidian-tc MCP memory tools (create, link, rename, unlink and delete entity) regenerate a note's body from SQLite at a computed path, overwriting any existing note there that the entity does not own, such as a hand-authored note. They can also commit SQLite changes before a refused note write, leaving the memory graph inconsistent with the files on disk.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:obsidian-tc affected >= 1.0.1, <= 1.31.3 fixed 1.31.4

## How to check

Run npm ls obsidian-tc in the project that runs the MCP server and check whether the version is between 1.0.1 and 1.31.3 inclusive.

## Fix

Upgrade obsidian-tc to 1.31.4 or later.
No workaround preserves the tools; disabling the write:memory scope for callers blocks the write path entirely.

## Timeline

- 2026-10-23T19:11:31+00:00 ghsa https://github.com/advisories/GHSA-r3g7-r4hr-m258
- 2026-10-23T21:57:34+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-115478

## From the sources

### Summary
`create_entity`, `link_entities`, `rename_entity`, `unlink_entities` and `delete_entity` regenerate a memory note's body from SQLite at the computed `memory/<type>/<name>.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.

### Details
- `create_entity` and `link_entities` inserted the SQL row or relation before materialization.
- `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.
- `unlink_entities` removed the relation row before discovering the source note's rematerialization would be refused.
- `delete_entity`'s cascade could delete the target and its relations before a neighbour's note was found to be foreign.

The overwrite exists in every released version since `materializeEntity` shipped (`git show v1.0.1:packages/server/src/memory/materialize.ts`).

### Impact
An 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.

### Patches
Fixed 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.

### Workarounds
None that preserve the tools. Disabling the `write:memory` scope for callers prevents the write path entirely.

### Credit
Found by the maintainer while building `obsidian-tc memory import` (PR #978); the vulnerability class was independently confirmed by a cross-vendor adversarial review.

### References
- PR #978 (fix and test), CHANGELOG `## [1.31.4] ### Security`
- `packages/server/src/memory/materialize.ts`, `packages/server/src/tools/m5/memory-lifecycle-tools.ts`

---

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
