Governed, Except by Default: Three obsidian-tc CVEs Sat Fixed but Undisclosed for Three Months
NVD published three CVEs against obsidian-tc within seconds of each other on 12 October 2026 (18:17:23Z–18:17:30Z), sourced to security-advisories@github.com — the maintainers themselves. The product bills itself as a “governed, agent-ready” Model Context Protocol server for Obsidian vaults: 163 tools across 31 domains, multi-vault native, TypeScript and Rust, AGPL-3.0, running since May 2026. All three records name versions through 1.3.0, fixed in 1.3.1. Release 1.3.1 shipped on 3 July 2026. The vulnerabilities were therefore fixed, tagged and downloadable for more than three months before any public advisory told operators of the most popular configuration — the documented loopback default — that it was exposed.
- CVE-2026-77366 (9.9 critical, CWE-639, through 1.3.0). Tool dispatch never compared the caller-controlled
input.vaultvalue against the vault bound to the caller: the JWT carried no vault claim and the folder ACL was a single global instance. Any valid token could therefore select any configured vault and read, write or delete its contents. Fixed by binding tokens and request context to a vault and rejecting mismatchedinput.vaultvalues before scope checks. - CVE-2026-77363 (8.8 high, CWE-346, through 1.3.0). The Streamable HTTP transport validated neither Host nor Origin and enabled no DNS-rebinding protection. Under the documented loopback configuration with
auth.modeset tonone, a malicious web page could send requests to the local MCP endpoint and receive unrestricted vault access through thehttp-localcaller. Fixed by enabling host, origin and DNS-rebinding validation. - CVE-2026-77364 (6.5 medium,
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N, CWE-200, through 1.3.0). UndefinedreadPaths/writePathsallowed all paths,strictReadDefaultdid not protect direct requests, and the.obsidian,.gitand.trashcontrol directories were excluded only during enumeration. An authenticated caller could directly read files like.obsidian/plugins/*/data.json, exposing plugin API keys and Obsidian Sync passwords. Fixed by denying the control directories by default and enforcingstrictReadDefaulton direct request paths.
The commit messages are the story
All three fixes were read first-hand from the repository, and their commit messages deserve quoting because they describe governance marketed but not implemented. The vault-binding fix (PR #72, 38b4992, merged 1 July, “bind HTTP tokens to a single vault”) states that only resources/read guarded against the gap while “the ~90 vault tools resolved a caller-supplied vault against any configured vault.” The DNS-rebinding fix (PR #74, 6428bfe, “DNS-rebinding / cross-origin protection on HTTP transport”) explains the mechanism precisely: the configuration “only fail-closes a NON-loopback bind, which does nothing against a browser hitting the loopback bind,” and under auth.mode: ’none’ the loopback default carries full wildcard scopes. The ACL fix (PR #73, 74de320, “fail-closed ACL defaults + honor strictReadDefault”) is the bluntest: it delivers “the ‘folder ACL, safe by default’ contract the README advertises but the default config did not enforce,” and notes that strictReadDefault “was previously stripped by validation before FolderAcl ever saw it” — operators who set the flag got no protection and no error.
Each of those sentences describes a distinct failure mode worth naming. The vault bypass is authorisation checked against the wrong principal: the token says who you are but never which vault you may touch, so the vault argument becomes a confused deputy the caller steers. The rebinding flaw is a default that is safe only against the threat it names: binding loopback stops remote attackers, and the documented no-auth loopback mode then hands the browser — which is local — the keys. The ACL flaw is defence applied at enumeration but not at access: listing hid the control directories while direct reads served them, the oldest TOCTOU-shaped asymmetry in file-serving code, plus a setting silently discarded by schema validation. Three advisories, three layers, one theme: the governance existed in the README before it existed in the request path.
A drive-by against a second brain
The 8.8 deserves its score, and the pairing with the 9.9 is what makes the batch consequential rather than merely embarrassing. Either flaw alone is serious; together they compose. A drive-by page that reaches the loopback endpoint inherits wildcard scopes (77363), and wildcard scopes with a caller-steered vault argument reach every configured vault (77366) — including, via 77364-class paths, the .obsidian directories holding plugin API keys and Sync credentials. An Obsidian vault is close to the worst object this composition could target: years of notes, credentials pasted into scratch files, API keys in plugin data, synced across devices. The site traced the same drive-by shape last month in a DNS-rebinding analysis, and the MCP transport lesson from last week’s mcp-go record applies here too — the Streamable HTTP edge keeps arriving without the controls plain HTTP endpoints learned a decade ago. What is new here is the asset behind the edge: not a debugger or a build tool, but personal knowledge.
The hundred-day gap, and what followed
The fixes merged on 1 July; v1.3.1 released 3 July; the repository has since shipped all the way to v1.32.0 (3 October) and was pushed as recently as 13 October. The CVEs date to 12 October. Anyone running 1.3.0 or earlier between July and October had a fixed version available and no advisory signal telling them the upgrade was security-relevant — the NVD publication lag, not the patch lag, was the exposure window. Credit where due: the advisories are maintainer-filed, technically precise, and paired with tests (cross-vault-binding.test.ts, acl-fail-closed.test.ts, http-roundtrip.test.ts landed in the same commits). The repository now carries seven advisories, four of them without CVEs — a declined human-in-the-loop confirmation that still completed the destructive call, memory-lifecycle tools overwriting notes they did not own, a symlink read-ACL scope bypass, bridge tools leaking denied-note data through unfiltered sibling fields — which reads as a team still working through its own attack surface in public. One further detail from the commit metadata: all three fixes list Claude Opus 4.8 as co-author. The remediation of an agent-accessible vulnerability surface was itself agent-assisted; judge the output, which is good, not the provenance.
What to do
- Upgrade past 1.3.1. Anything at or below 1.3.0 carries a 9.9 cross-vault bypass, a drive-by vault-access flaw in the documented default configuration, and direct reads of control directories. Current is 1.32.0; anything after 1.3.1 contains the three fixes.
- Do not run
auth.mode: noneon a loopback HTTP transport you browse from. The fixed default enables DNS-rebinding protection (transports.http.enableDnsRebindingProtection, default true, withallowedHosts/allowedOrigins), but the pre-fix lesson generalises: any MCP server on 127.0.0.1 without auth is one malicious page away from full tool access. Bind loopback and authenticate, or keep the server off HTTP entirely (stdio). - Mint one token per vault. Post-fix behaviour confines a claimless token to the server default vault; multi-vault HTTP deployments need per-vault tokens with the
vaultclaim. Audit existing tokens: any token minted before the fix was effectively a master key. - Rotate what the vault held. If a pre-1.3.1 HTTP endpoint was ever reachable from a browser, assume
.obsidianplugin secrets and Sync credentials were readable. Rotate plugin API keys and the Sync password, not just the server tokens.
Verification note. All three CVE identifiers, NVD publication timestamps (12 October 2026, 18:17:23Z–18:17:30Z), CVSS 3.1 scores (8.8, 6.5, 9.9), CWE assignments, affected-through versions and the security-advisories@github.com source were read first-hand from the NVD 2.0 API, including the full 6.5 vector quoted above. Fix pull-request numbers (72, 73, 74), merge timestamps (1 July 2026), commit SHAs (38b4992, 74de320, 6428bfe), the quoted commit-message passages, the test files added, the four additional CVE-less advisory summaries and the Claude Opus 4.8 co-authorship were read first-hand from the GitHub API for The-40-Thieves/obsidian-tc. Release tags and dates (v1.3.1 on 3 July 2026, v1.32.0 on 3 October 2026), star/fork counts, licence, repository description and push date were read first-hand from the GitHub API. Brave Search corroboration found only CVE-mirror pages and no analytical coverage. We did not install obsidian-tc, mint a token, or send requests to any deployment; the composition scenario and remediation guidance are our editorial analysis. We have no evidence of exploitation and imply none.
Sources:
- NVD — CVE-2026-77366, cross-vault authorisation bypass via
input.vault(9.9 critical, CWE-639, through 1.3.0) - NVD — CVE-2026-77363, Streamable HTTP without Origin/DNS-rebinding protection (8.8, CWE-346, through 1.3.0)
- NVD — CVE-2026-77364, fail-open folder ACL and control-directory exposure (6.5, CWE-200, through 1.3.0)
- GHSA-84c9-qqpq-2pq5 — cross-vault authorisation bypass via
input.vault(critical) - GHSA-pqh8-4pj7-32xx — HTTP transport lacks Origin/DNS-rebinding protection (critical)
- GHSA-cvp8-vvgf-69q4 — fail-open default folder ACL, control-directory secrets readable (medium)
- The-40-Thieves/obsidian-tc
v1.3.1— the release carrying all three fixes (3 July 2026)