Nov 14, 2026·9 min read·2 visits
@sveltejs/adapter-node crashes with an uncaught TypeError when handling proxied text/event-stream responses returned directly from fetch(), allowing remote attackers to trigger a complete Denial of Service against affected routes.
A Denial of Service (DoS) vulnerability exists in @sveltejs/adapter-node versions 5.5.7 and 6.0.0-next.0 through 6.0.0. When a SvelteKit server route returns a direct response from a fetch() call with a Content-Type of text/event-stream, the Node.js adapter attempts to mutate the response headers to disable proxy buffering. Because headers returned directly by the WHATWG Fetch API carry an immutable guard, this mutation throws an unhandled TypeError, terminating the underlying Node.js process and denying service to all active and subsequent clients.
SvelteKit is an application framework powered by Svelte that relies on platform-specific adapters to deploy server-rendered applications across different runtime environments. The @sveltejs/adapter-node package compiles SvelteKit applications into standalone Node.js HTTP servers. Within this package, the primary server-side rendering (SSR) request handler (packages/adapter-node/src/handler.js) mediates between Node's native http.IncomingMessage and http.ServerResponse objects and the standard Web Request and Response APIs used internally by SvelteKit.
In @sveltejs/adapter-node version 5.5.7 and pre-release versions prior to 6.0.1, a flaw in response header processing introduces an Uncaught Exception vulnerability (CWE-248). Specifically, the adapter inspects outgoing responses for the text/event-stream media type to automatically append the X-Accel-Buffering: no HTTP header. This mechanism was introduced to prevent reverse proxies such as Nginx from buffering Server-Sent Events (SSE) streams.
When a SvelteKit server endpoint or page handler proxies an upstream SSE stream by returning the result of a fetch() invocation directly, the resulting Web Standard Response object contains an immutable Headers map. The adapter attempts to modify this map in place without checking its mutability state or wrapping the call in an exception handler.
Consequently, any HTTP request routed to an endpoint that proxies a text/event-stream response via fetch() triggers an unhandled TypeError inside the asynchronous SSR handler. In a standard Node.js runtime configuration, an unhandled promise rejection or uncaught exception immediately aborts the process, severing all active connections and taking the application offline.
The root cause of CVE-2026-128123 lies in a mismatch between the assumptions made by @sveltejs/adapter-node and the security invariants enforced by the WHATWG Fetch Standard regarding Headers objects. According to the WHATWG specification, every Headers instance has an associated guard property that governs whether its contents may be modified via methods such as set(), append(), or delete().
Valid guard states include none, request, request-no-cors, response, and immutable. When a developer instantiates a Response manually within a SvelteKit endpoint (for example, new Response(stream, { headers: { 'content-type': 'text/event-stream' } })), the associated Headers guard defaults to none, permitting subsequent mutations by framework middleware and adapters.
Conversely, when a Response object is produced by the native fetch() API—implemented in Node.js via undici—the specification mandates that the response's Headers guard be set to immutable. Any attempt to call a mutator method on an immutable Headers instance must immediately throw a TypeError exception.
In packages/adapter-node/src/handler.js, the ssr function awaits the Response returned by server.respond(). Prior to passing this object to the setResponse() serialization utility, the code checks if response.headers.get('content-type') === 'text/event-stream'. When this condition evaluates to true, the handler executes response.headers.set('x-accel-buffering', 'no').
Because the ssr function executes asynchronously as an HTTP request listener callback and does not wrap this mutation in a try/catch block, the resulting TypeError: Failed to execute 'set' on 'Headers': Headers are immutable propagates to the top-level Node.js event loop as an unhandled rejection, terminating the server process.
The vulnerability was introduced when @sveltejs/adapter-node added automatic support for disabling reverse proxy buffering on Server-Sent Events responses. Examining the vulnerable code path in packages/adapter-node/src/handler.js illustrates how the response headers were mutated directly on the WHATWG Response instance before converting it into a Node.js http.ServerResponse.
// Vulnerable implementation in packages/adapter-node/src/handler.js
const ssr = async (req, res) => {
// ... request preparation logic ...
const response = await server.respond(request, {
getClientAddress: () => {
// ... address resolution ...
},
platform: { req }
});
// Reverse proxies can buffer responses by default (unless prevented by
// `cache-control`), which breaks streaming responses like server-sent events.
// `X-Accel-Buffering: no` opts out of that buffering and is a no-op on proxies
// that don't recognise it. See https://github.com/sveltejs/kit/issues/15790
if (response.headers.get('content-type') === 'text/event-stream') {
// BUG: Throws TypeError if `response` originated directly from fetch()
response.headers.set('x-accel-buffering', 'no');
}
setResponse(res, response);
};In commit 04756a1f7a4f73e34f1934895340a46de84fc164 (and backported via commit 0a2e58cbccdd46cac20aa5af4e2edf17e0da11dd), the maintainers resolved the issue by shifting the header assignment from the immutable Web Response object to Node's native http.ServerResponse (res) instance.
File: packages/adapter-node/src/handler.js
@@ -120,8 +120,10 @@ const ssr = async (req, res) => {
// `cache-control`), which breaks streaming responses like server-sent events.
// `X-Accel-Buffering: no` opts out of that buffering and is a no-op on proxies
// that don't recognise it. See https://github.com/sveltejs/kit/issues/15790
+ // The header is set on the Node response rather than the `Response` object,
+ // whose headers may be immutable (e.g. when returned from `fetch`)
if (response.headers.get('content-type') === 'text/event-stream') {
- response.headers.set('x-accel-buffering', 'no');
+ res.setHeader('x-accel-buffering', 'no');
}
setResponse(res, response);The patch completely resolves the uncaught exception by calling res.setHeader('x-accel-buffering', 'no') prior to invoking setResponse(res, response). The Node.js http.ServerResponse object remains mutable until headers are flushed to the network socket via res.writeHead(). Furthermore, within @sveltejs/adapter-node's setResponse implementation, headers from response.headers are subsequently iterated and applied to res. If the upstream response already explicitly defines an x-accel-buffering header, setResponse will overwrite the default 'no' value on res cleanly without throwing an exception. No variant attack paths involving immutable response headers exist in handler.js as no other post-processing mutations are performed on response.headers.
Exploiting CVE-2026-128123 requires no special privileges or complex payload construction, provided the target application exposes an endpoint that proxies a text/event-stream response using fetch(). This design pattern is common in modern web applications that act as Backend-for-Frontend (BFF) proxies for AI Large Language Model (LLM) token streaming, real-time notification services, or upstream event brokers.
@sveltejs/adapter-node version 5.5.7 or >= 6.0.0-next.0, < 6.0.1.+server.js) or handle hook must return the direct result of a fetch() call without cloning the Response or its Headers.Content-Type: text/event-stream (without additional parameters like ; charset=utf-8, as the vulnerable check performs a strict string equality comparison === 'text/event-stream').The vulnerability can be reproduced using the test case added in the official SvelteKit repository under src/routes/event-stream/proxied/+server.js:
// src/routes/event-stream/proxied/+server.js
export function GET() {
// Returning a fetch() promise directly preserves the 'immutable' Headers guard
return fetch('data:text/event-stream,data: hello%0A%0A');
}To trigger the Denial of Service against a server running this route, an unauthenticated client sends a standard HTTP GET request:
GET /event-stream/proxied HTTP/1.1
Host: vulnerable-sveltekit-app.local:3000
Accept: text/event-stream
Connection: keep-alive
Upon receiving this request, the SvelteKit router executes the GET handler, resolves the fetch() call, and passes the immutable Response back to @sveltejs/adapter-node. The adapter attempts to execute response.headers.set('x-accel-buffering', 'no'), immediately throwing TypeError: Failed to execute 'set' on 'Headers': Headers are immutable and crashing the Node.js runtime.
The primary security impact of CVE-2026-128123 is a high-severity loss of availability. Because Node.js operates on a single-threaded event loop model, an uncaught exception in an asynchronous request handler causes the entire OS process to exit immediately with a non-zero status code.
When the process terminates, the impact extends beyond the single request that triggered the exception. Every concurrent HTTP connection, active WebSocket session, or ongoing Server-Sent Events stream being serviced by that Node.js worker is abruptly severed. Any in-memory state held by the process is lost.
The vulnerability is scored at 5.9 (Medium) under CVSS v3.1 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H). The Attack Complexity (AC) is rated as High (H) because the flaw is not universally triggerable across all @sveltejs/adapter-node deployments; the attacker is dependent on the application developer having implemented a route that returns an uncloned fetch() response with Content-Type: text/event-stream.
> [!NOTE]
> In production environments lacking an external process supervisor (such as systemd, PM2, or Kubernetes liveness/restart policies), a single HTTP request results in permanent downtime until manual intervention occurs. Where automatic restart policies are configured, an attacker can repeatedly send requests at a low rate to keep the service in a continuous crash loop (CrashLoopBackOff).
There is no impact on Confidentiality (C:N) or Integrity (I:N), as the exception occurs prior to response serialization and does not corrupt memory or leak process data.
To remediate CVE-2026-128123, development teams must upgrade @sveltejs/adapter-node to a patched version (5.5.8 for the 5.x release line, or 6.0.1 for the 6.x release line) and rebuild the production deployment artifact.
Run the appropriate package manager command to update the dependency:
# Using npm
npm install @sveltejs/adapter-node@latest
# Using pnpm
pnpm update @sveltejs/adapter-node
# Using yarn
yarn upgrade @sveltejs/adapter-nodeIf an immediate dependency upgrade and full application rebuild cannot be performed, developers can mitigate the vulnerability in application code by wrapping any proxied fetch() response in a newly instantiated Response object and cloning its headers via new Headers(). Constructing a new Headers instance resets the internal WHATWG guard from immutable to none.
// Workaround in +server.js for unpatched @sveltejs/adapter-node versions
export async function GET({ fetch }) {
const upstreamResponse = await fetch('https://api.example.com/events');
// Cloning the headers strips the 'immutable' guard, preventing the TypeError
return new Response(upstreamResponse.body, {
status: upstreamResponse.status,
statusText: upstreamResponse.statusText,
headers: new Headers(upstreamResponse.headers)
});
}In addition to patching the adapter, operators deploying Node.js applications should ensure that process managers (systemd, Docker restart policies, PM2, or Kubernetes pod controllers) are configured to automatically restart terminated processes. While process restarts do not prevent active connections from dropping during an exploit attempt, they prevent a single request from causing permanent application unavailability.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
@sveltejs/adapter-node Svelte | = 5.5.7 | 5.5.8 |
@sveltejs/adapter-node Svelte | >= 6.0.0-next.0, < 6.0.1 | 6.0.1 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-128123 / GHSA-rqw3-x7hx-mm3p |
| Weakness Class | CWE-248 (Uncaught Exception) |
| Attack Vector | Network (HTTP Request to SSE Proxy Route) |
| CVSS v3.1 Score | 5.9 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| Primary Impact | Denial of Service (Node.js Process Crash) |
| Affected Component | @sveltejs/adapter-node (packages/adapter-node/src/handler.js) |
| Exploit Status | PoC in Official Test Suite |
| CISA KEV Status | Not Listed |
The software does not catch or properly handle an exception, leading to unexpected termination or denial of service.
CVE-2026-126347 (GHSA-5cjw-f85j-cv6r) is an open redirect vulnerability (CWE-601) in Tornado, an asynchronous Python networking library and web framework. The flaw resides in tornado.web.RedirectHandler when dynamic URL redirection templates incorporate unvalidated user input. Due to a parser differential between RFC 3986 (used by Python's urllib.parse.urlsplit) and the WHATWG URL Standard (used by modern web browsers), inputs containing three or more leading slashes bypass Tornado 6.6.0's validation logic, allowing unauthenticated attackers to redirect users to arbitrary external domains.
CVE-2026-128122 (GHSA-5wg4-q3ww-283h) is a build-time CRLF and wildcard injection vulnerability in `@sveltejs/adapter-cloudflare`. During the prerendering phase of a SvelteKit build, the adapter writes crawled redirect paths directly into the Cloudflare `_redirects` and `_routes.json` configuration files without output encoding or character validation. An attacker capable of controlling link paths on prerendered pages can inject newline characters, spaces, or wildcard patterns to forge arbitrary edge redirection rules, enabling asset hijacking and Stored Cross-Site Scripting (XSS).
A vulnerability classified under CWE-116 (Improper Encoding or Escaping of Output) and CWE-625 (Permissive Regular Expression) exists in the @sveltejs/adapter-vercel and @sveltejs/adapter-static packages for SvelteKit. During the build-time prerendering phase, pathnames discovered by the SvelteKit link crawler are inserted into the generated Vercel routing configuration (.vercel/output/config.json) without escaping regular expression metacharacters. Because the Vercel edge routing engine evaluates the 'src' property of route entries as regular expressions, an attacker capable of injecting links into prerendered application content can introduce arbitrary regular expression syntax, hijacking application routing and exposing unintended assets or responses.
CVE-2026-128117 (GHSA-mw6w-2fmv-pvw3) is a denial of service (DoS) vulnerability in SvelteKit (@sveltejs/kit) caused by an uncaught URIError exception during request routing. When an application implements a reroute hook that rewrites a request pathname containing invalid percent-encoding into a valid route, SvelteKit subsequently calls decode_pathname on the original unvalidated url.pathname outside of a try...catch block. In deployments utilizing @sveltejs/adapter-node, this unhandled exception terminates the Node.js runtime process.
CVE-2026-126303 (GHSA-8fx7-8jr8-84rv) is an uncontrolled resource consumption vulnerability in the Natural Language Toolkit (NLTK) prior to version 3.10.4. The flaw is located in the SteppingRecursiveDescentParser.parse() method within the nltk.parse.recursivedescent module. Because the parser executes a top-down recursive descent stepping loop without enforcing an execution time deadline or step limit, processing tokens against a left-recursive, self-embedding, or ambiguous Context-Free Grammar (CFG) causes the parser to enter an infinite loop. This results in complete CPU core exhaustion and worker thread starvation, leading to an application-level denial of service.
File Browser versions prior to 2.63.17 contain an authorization and state-management flaw in the BoltDB storage backend. When an authenticated user deletes a shared directory via an HTTP request with a trailing slash (e.g., DELETE /api/resources/a/), the share cleanup routine queries the database index using the unnormalized path before stripping the trailing slash. Because canonical share paths are stored without trailing slashes (e.g., /a), the database prefix query fails to match the parent directory share record. If a directory with the same name is later recreated, the dormant public share hash automatically reactivates and exposes the new directory contents to unauthenticated users.