Mar 6, 2026·5 min read·49 visits
Mercurius versions prior to 16.8.0 fail to apply `queryDepth` limits to GraphQL subscriptions over WebSockets. Attackers can exploit this to send deeply nested queries that exhaust server resources.
A logic vulnerability in the Mercurius GraphQL adapter for Fastify allows attackers to bypass query depth limits using WebSocket subscriptions. While standard HTTP queries are validated against the configured `queryDepth`, subscription operations received via the WebSocket transport layer skip this check. This oversight allows unauthenticated remote attackers to submit arbitrarily nested queries, potentially leading to Denial of Service (DoS) via CPU and memory exhaustion when the subscription events are resolved.
Mercurius is a popular GraphQL adapter for the Fastify web framework, designed for high performance. To protect against resource exhaustion attacks—common in GraphQL due to the graph-like nature of data—Mercurius provides a queryDepth configuration option. This setting limits the maximum nesting level of incoming queries, preventing clients from requesting exponentially complex data structures.
However, a discrepancy exists in how Mercurius handles different transport protocols. While the HTTP request handler correctly invokes the depth validation logic before execution, the WebSocket handler (used for GraphQL Subscriptions) fails to enforce this check. Consequently, the queryDepth protection is effectively bypassed for any operation submitted via a persistent WebSocket connection.
This vulnerability is classified as CWE-863 (Incorrect Authorization) because the system fails to authorize the resource consumption level (depth) for a specific communication channel, despite the policy being defined globally.
The root cause lies in the architectural separation between HTTP route handling and WebSocket connection management within the Mercurius codebase. Validation logic was not centralized but rather applied independently in each transport handler.
In the HTTP path (lib/routes.js), the request processing pipeline explicitly calls the internal validation utility to check the Abstract Syntax Tree (AST) of the incoming query against the queryDepth limit. If the limit is exceeded, the request is rejected immediately with a 400 error.
In contrast, the WebSocket path is handled by lib/subscription-connection.js. The SubscriptionConnection class manages the lifecycle of WebSocket clients. When a client sends a start (or subscribe) message, the handler parses the payload and initiates the subscription iterator. Prior to version 16.8.0, this flow did not include a call to the queryDepth validator. The parameters for the depth limit were not even passed down to the subscription connection context, making validation impossible within that scope.
The fix required plumbing the queryDepthLimit configuration option down to the SubscriptionConnection class and enforcing the check during the message handling phase.
Vulnerable Code Path (Conceptual):
In lib/subscription-connection.js, the message handler simply proceeded to execution:
// Pre-patch logic
handleMessage (message) {
switch (message.type) {
case GQL_START:
// ... parsing logic ...
this.executeSubscription(id, payload, query, variables, operationName)
break;
}
}Patched Code Path:
The commit introduces the validation step explicitly. The queryDepth function is imported and called before execution proceeds.
// lib/subscription-connection.js
// [1] Import the validator
const queryDepth = require('./query-depth')
// [2] Inside the message handler
if (this.queryDepthLimit) {
// Validate the parsed document definitions
const queryDepthErrors = queryDepth(document.definitions, this.queryDepthLimit)
if (queryDepthErrors.length > 0) {
// [3] Halt execution if depth is exceeded
const err = new MER_ERR_GQL_VALIDATION()
err.errors = queryDepthErrors
this.sendMessage(GQL_ERROR, id, { payload: err })
return
}
}Additionally, index.js and lib/routes.js were updated to ensure queryDepthLimit is passed into the SubscriptionConnection constructor during initialization.
Exploiting this vulnerability requires establishing a WebSocket connection using a standard GraphQL subscription protocol (e.g., graphql-ws or subscriptions-transport-ws). No authentication is strictly required unless the endpoint itself enforces it at the connection level, but the depth check bypass occurs regardless of privilege level.
Attack Scenario:
User type with a friends field that returns a list of User types).ws://target.com/graphql.subscription {
userUpdates {
friends {
friends {
friends {
... # repeated 100+ times
name
}
}
}
}
}The primary impact is Denial of Service (DoS). While the CVSS v4.0 score is rated Low (2.7), this likely reflects the specific environmental metrics applied in the context (Availability: Low). In a real-world production environment, uncontrolled recursion can easily crash a Node.js process due to heap allocation failures or block the event loop for extended periods.
Metric Breakdown:
This vulnerability is particularly dangerous because subscriptions are long-lived. A successful attack does not require a flood of requests; a single expensive subscription can persistently drain resources every time an event is published.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N/E:U| Product | Affected Versions | Fixed Version |
|---|---|---|
mercurius mercurius-js | < 16.8.0 | 16.8.0 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-30241 |
| CVSS v4.0 | 2.7 (Low) |
| CWE | CWE-863 (Incorrect Authorization) |
| Attack Vector | Network (WebSocket) |
| Impact | Denial of Service |
| Exploit Status | PoC Available (Regression Test) |
The software does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.
An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.
CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.
CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.
CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.
CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.