Oct 23, 2026·5 min read·2 visits
In obsidian-tc prior to version 1.31.4, echoing a valid requestState token causes the server to execute destructive tool calls regardless of whether the user explicitly declined or canceled the prompt.
A high-severity authorization bypass vulnerability in obsidian-tc allows clients to bypass Human-In-The-Loop (HITL) confirmation prompts over Streamable HTTP deployments. Due to incomplete validation of returned prompt responses, an attacker echoing a signed requestState token can force execution of destructive vault operations even if a human user declines or cancels the prompt.
CVE-2026-115477 affects the obsidian-tc Model Context Protocol (MCP) server when deployed over Streamable HTTP with auth.jwtSecret configured. The package exposes governed tools for interacting with Obsidian vaults, including destructive actions like file deletion, frontmatter modification, and workspace updates. Human-in-the-Loop (HITL) authorization gates prevent unauthenticated or unauthorized tool execution by requiring explicit user confirmation before processing destructive requests.
In versions >= 1.13.0 and < 1.31.4, the server failed to validate user approval decisions upon receiving echoed state tokens during the elicitation flow. When a tool call triggered a HITL confirmation gate, the server returned an inputRequired response along with a signed requestState token bound to the request parameters. A client echoing this requestState token could trigger operation execution regardless of whether the user accepted, declined, or canceled the prompt.
This flaw allows an attacker or a malicious client connected to the MCP server to bypass explicit user rejection and force execution of restricted operations. The weakness is tracked under CWE-285 (Improper Authorization) and CWE-863 (Incorrect Authorization).
The root cause stems from a flaw in token interpretation within packages/server/src/mcp/server.ts and packages/server/src/mcp/elicit-form.ts. When an initial tool request requires human approval, the server generates a cryptographic requestState token before presenting the prompt to the user. This token binds key context details, such as tool identifier, vault ID, caller details, and target argument hashes.
Upon client resubmission, the server verified the cryptographic signature and context binding of requestState, but failed to inspect the user decision payload inside inputResponses. The logic assumed that the presence of a cryptographically valid requestState token implied user consent. Consequently, if a user clicked "Decline" or "Cancel", sending { action: "decline" } alongside the valid requestState, the server set ctx.elicitState and treated the checkHitl authorization check as satisfied.
Furthermore, sixteen conditionally gated tools in packages/server/src/vault/hitl.ts checked only ctx.elicitToken while ignoring ctx.elicitState. This misalignment caused legitimate approval flows for conditionally gated tools to fail or re-prompt repeatedly up to the 8-round elicitation limit.
Prior to the patch, packages/server/src/mcp/elicit-form.ts directly copied requestState to the execution context without validating the decision payload contained in inputResponses.
// Vulnerable logic in mcp/server.ts / elicit-form.ts (pre-v1.31.4)
const echoed = extra.mcpReq.requestState?.<ElicitRequestState>();
if (echoed !== undefined) {
// Insecurely populates elicitState without checking user acceptance
ctx = { ...ctx, elicitState: echoed };
}In patch 101c91c656e2c2b88bcf3696a5da035f8c032c71, the server introduces strict validation via resolveElicitConfirmation. The code inspects ctx.mcpReq.inputResponses and requires an explicit positive user action (action === "accept" and approve === true) before setting ctx.elicitState.
// Patched logic in mcp/elicit-form.ts (v1.31.4)
const confirmApproved =
confirmResponse.kind === "elicit" &&
confirmResponse.action === "accept" &&
confirmResponse.content?.approve === true;
if (confirmApproved) {
ctx = { ...ctx, elicitState: echoed };
} else if (confirmResponse.action === "decline" || confirmResponse.action === "cancel") {
// Halts elicitation loop immediately and returns terminal error
return { roundDeclinedOrCancelled: true };
}In addition, packages/server/src/vault/hitl.ts was updated so that requireConfirmation evaluates hitlSatisfiedByState(ctx.elicitState, ...). The patch also introduces a round limit counter inside ElicitRequestState to cap retry offers at 2 rounds.
Exploitation requires network access to a Streamable HTTP obsidian-tc MCP deployment configured with auth.jwtSecret. The attacker must hold low-privilege API access to send tool execution calls to the server.
An attacker initiates execution of a gated tool, such as delete_note or write_note. The server generates an inputRequired response containing a signed requestState token. When the legitimate user receives the prompt and selects "Decline" or "Cancel", the client transmits the user decision alongside the requestState token.
In vulnerable versions, the server processes the incoming payload, ignores the action: "decline" field, and completes the action using the valid requestState token. No public weaponized exploit scripts currently exist in databases, but the flaw can be triggered programmatically by any custom HTTP client.
The vulnerability allows low-privilege authenticated users or compromised agent clients to execute unapproved destructive operations across Obsidian vaults. Impacted operations include permanent file deletion (delete_note), frontmatter destruction (update_frontmatter), note overwrites (write_note), link pruning (prune_hub_links), and workspace changes.
The CVSS v4.0 base score is rated 7.1 (High) with 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. Integrity impact is High (VI:H) because file modifications and deletions bypass user authorization boundaries. Confidentiality impact is None (VC:N) as this flaw does not disclose vault contents directly.
This vulnerability is not currently listed in the CISA Known Exploited Vulnerabilities (KEV) catalog and has no documented active ransomware usage.
The primary fix requires updating obsidian-tc to version 1.31.4 or later using npm install obsidian-tc@latest. The updated version enforces user decision checking and prevents automated execution on declined or cancelled prompts.
If upgrading immediately is not possible, operators can switch the server deployment transport from Streamable HTTP to stdio. The stdio transport uses an out-of-band single-use token mechanism unaffected by this bypass. Alternatively, removing auth.jwtSecret forces the system to revert to CLI-based single-use tokens (obsidian-tc elicit), eliminating state token handling.
> [!NOTE]
> Under the MCP client-driven protocol model (2026-07-28 specification), client applications handle rendering and response generation. A malicious or modified client can directly forge an acceptance response ({ action: "accept", content: { approve: true } }). True user attestation requires transport mechanisms where the server directly prompts the user, such as stdio deployment.
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| Product | Affected Versions | Fixed Version |
|---|---|---|
obsidian-tc The-40-Thieves | >= 1.13.0, < 1.31.4 | 1.31.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-285 / CWE-863 |
| CVSS v4.0 | 7.1 (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) |
| Attack Vector | Network |
| EPSS Score | N/A |
| CISA KEV Status | Not Listed |
| Exploit Status | No Public Weaponized Exploit |
| Impact | High Integrity (Unauthorized Destructive Operations) |
The software does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
A Denial of Service (DoS) vulnerability in pypdf versions prior to 6.21.0 allows remote attackers to cause high CPU and memory resource exhaustion by supplying PDF files containing oversized or deeply nested arrays, dictionaries, or content streams.
An uncontrolled resource consumption and inefficient algorithmic complexity vulnerability in pypdf prior to version 6.21.0 allows remote attackers to trigger application-level denial-of-service (DoS) via crafted PDF form field hierarchies.
Nuxt versions 3.11.0 through 3.21.3 and 4.0.0 through 4.4.3 contain an interpretation conflict between the route rules matching engine and the core page routing mechanism. By submitting percent-encoded request path segments, unauthenticated attackers can bypass route rules, including appMiddleware authorization guards, and access rendered server-side page payloads.
A Cross-Site Scripting (XSS) vulnerability exists in Nuxt when configured with pages disabled (pages: false). ASCII C0 control characters prefixing javascript: URIs bypass internal protocol classification in the fallback router, allowing unauthenticated attackers to execute arbitrary script in the victim's origin context upon user interaction.
A Stored Cross-Site Scripting (XSS) vulnerability exists in Nuxt Server Island rendering logic when utilizing the useHeadSafe composable. Raw head attributes bypass sanitization during island serialization.
Nuxt versions prior to 4.6.2 suffer from a route rule bypass vulnerability caused by an architectural mismatch in case-sensitivity handling between the Nitro server runtime and the Vue page router. An unauthenticated remote attacker can bypass server-enforced route directives—such as redirects, security headers, and SSR controls—by altering character casing in HTTP request paths.