Nov 14, 2026·9 min read·3 visits
An unhandled URIError exception in SvelteKit's internal_respond routing pipeline allows unauthenticated remote attackers to crash Node.js servers using @sveltejs/adapter-node by sending malformed percent-encoded URLs to endpoints rewritten by a reroute hook.
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.
SvelteKit (@sveltejs/kit) is the official full-stack application framework for Svelte. Its server-side request pipeline processes incoming HTTP requests, resolves routes against the application manifest, executes server hooks, and renders responses. As part of its routing architecture, SvelteKit provides a reroute hook (src/hooks.js or src/hooks.server.js) that executes prior to route resolution, allowing developers to dynamically translate or normalize URL pathnames (such as mapping localized URLs or vanity paths to internal routes).
CVE-2026-128117 (also tracked as GHSA-mw6w-2fmv-pvw3) is an Uncaught Exception vulnerability (CWE-248) within the internal_respond function located in packages/kit/src/runtime/server/respond.js. The flaw occurs when a client submits an HTTP request containing an invalid percent-encoded byte sequence in the URL pathname, and the application's reroute hook rewrites that malformed pathname into a valid destination path.
While SvelteKit wraps the URI decoding of the rewritten path (resolved_path) inside an exception-handling try...catch block, it later invokes decode_pathname(url.pathname) on the original, unmodified request pathname outside of any try...catch guard to check if a prerendered asset matches the route. Because the original pathname still contains invalid percent-encoded sequences, the underlying JavaScript decodeURI call throws a synchronous URIError: URI malformed exception.
In production deployments running on @sveltejs/adapter-node, this uncaught exception propagates to the top level of the Node.js event loop. By default, Node.js terminates the entire server process upon encountering an unhandled exception or unhandled promise rejection, resulting in an immediate denial of service for all active and subsequent connections.
The root cause of CVE-2026-128117 lies in inconsistent exception boundary scoping during pathname normalization in packages/kit/src/runtime/server/respond.js. When an HTTP request enters internal_respond(request, state), SvelteKit extracts the request URL and invokes options.hooks.reroute({ url: new URL(url) }) if configured. If the hook returns nothing, resolved_path defaults to url.pathname.
Next, SvelteKit attempts to percent-decode resolved_path using the internal helper decode_pathname(), which invokes standard ECMAScript URI decoding functions. Because client-supplied URLs frequently contain malformed percent sequences (such as truncated hex escapes like % or %a, or invalid UTF-8 byte sequences like %c0%ae or %ff), SvelteKit places resolved_path = decode_pathname(resolved_path) inside a try...catch block. In a standard application without a reroute hook—or when reroute returns a string that preserves the malformed sequence—decode_pathname(resolved_path) throws a URIError. The catch block intercepts the error, sets resolved_path = null, and delegates the request to handle(), which safely responds with an HTTP 400 Bad Request.
However, a state discrepancy arises when reroute inspects url.pathname (for example, matching a suffix or regular expression) and returns a sanitized, valid path string such as '/'. In this scenario, resolved_path contains '/', while url.pathname retains the original malformed byte sequence. When decode_pathname(resolved_path) executes inside the try...catch block, it succeeds without throwing an error because '/' is valid.
Immediately following the try...catch block, SvelteKit evaluates a conditional statement to determine whether the rerouted path points to a prerendered page:
if (
resolved_path !== decode_pathname(url.pathname) &&
!state.prerendering?.fallback &&
has_prerendered_path(manifest, resolved_path)
)The developer assumed that if execution survived the try...catch block, pathname decoding was generally safe, overlooking the fact that reroute decouples resolved_path from url.pathname. Invoking decode_pathname(url.pathname) directly inside the if condition exposes the unprotected execution context to the raw client input, triggering an unhandled URIError.
The vulnerability resides in packages/kit/src/runtime/server/respond.js within the internal_respond function. Prior to the patch, only resolved_path was decoded inside the try...catch block, while url.pathname was decoded inline during the prerendered route comparison.
// Vulnerable code in packages/kit/src/runtime/server/respond.js
export async function internal_respond(request, state) {
// ... [omitted setup logic] ...
/** @type {PageNodes | undefined} */
let page_nodes;
try {
// Only resolved_path is protected by the try...catch boundary.
// If reroute rewrote the malformed path to a valid string, this succeeds.
resolved_path = decode_pathname(resolved_path);
} catch {
resolved_path = null;
return await handle();
}
// try to serve the rerouted prerendered resource if it exists
if (
// VULNERABILITY: url.pathname still contains the raw, malformed percent-encoding.
// Calling decode_pathname() here throws an uncaught URIError.
resolved_path !== decode_pathname(url.pathname) &&
!state.prerendering?.fallback &&
has_prerendered_path(resolved_path)
) {
// ... [prerendered asset handling] ...
}
}In commit 7993d5ebab017cb1be898a0b1c57371c5da06557 (SvelteKit 3.x) and backport commit 0a2e58cbccdd46cac20aa5af4e2edf17e0da11dd (SvelteKit 2.x), the maintainers resolved the issue by declaring a dedicated decoded_pathname variable and moving decode_pathname(url.pathname) inside the try...catch block.
--- a/packages/kit/src/runtime/server/respond.js
+++ b/packages/kit/src/runtime/server/respond.js
@@ -295,17 +295,21 @@ export async function internal_respond(request, state) {
/** @type {PageNodes | undefined} */
let page_nodes;
+ /** @type {string} */
+ let decoded_pathname;
+
try {
resolved_path = decode_pathname(resolved_path);
+ // `reroute` can turn a malformed pathname into a valid one, so this must be decoded separately
+ decoded_pathname = decode_pathname(url.pathname);
} catch {
resolved_path = null;
return await handle();
}
// try to serve the rerouted prerendered resource if it exists
if (
- // the resolved path has been decoded so it should be compared to the decoded url pathname
- resolved_path !== decode_pathname(url.pathname) &&
+ resolved_path !== decoded_pathname &&
!state.prerendering?.fallback &&
has_prerendered_path(resolved_path)
) {> [!NOTE]
> Fix Completeness Assessment: The patch is complete with respect to this code path. By decoding both resolved_path and url.pathname inside the try...catch block before any downstream logic executes, any URIError raised by either string immediately routes execution into the catch handler (resolved_path = null; return await handle();), which returns a standard HTTP 400 Bad Request response.
Exploiting CVE-2026-128117 requires no authentication or session state, only network access to the HTTP/HTTPS listener of the target SvelteKit application. However, the attack complexity is rated High (AC:H) because two specific environmental prerequisites must be satisfied:
@sveltejs/adapter-node (or a custom Node.js server integration that does not trap unhandled promise rejections at the process level).reroute hook (src/hooks.js or src/hooks.server.js) that matches an incoming URL containing a malformed URI sequence and returns a valid URI string.Consider a SvelteKit application with a reroute hook that normalizes paths ending in a specific suffix or matching a prefix pattern:
// src/hooks.js
export const reroute = ({ url }) => {
if (url.pathname.endsWith('/reroute/malformed')) {
return '/';
}
};To trigger the vulnerability, an attacker transmits an HTTP GET request containing an invalid UTF-8 percent-encoded sequence (such as %c0%ae, an overlong UTF-8 encoding of ., or a standalone invalid byte like %ff) placed in the segment of the path ignored or stripped by reroute:
GET /%c0%ae/reroute/malformed HTTP/1.1
Host: target-sveltekit-server.local
User-Agent: Mozilla/5.0
Accept: */*
When the vulnerable server processes this request:
URL parser accepts /%c0%ae/reroute/malformed without throwing an error because new URL() does not percent-decode the pathname during construction.reroute({ url }) evaluates url.pathname.endsWith('/reroute/malformed'), which returns true, and rewrites resolved_path to '/'.decode_pathname('/') succeeds inside the try...catch block.decode_pathname('/%c0%ae/reroute/malformed') executes outside the try...catch block, throwing URIError: URI malformed.1.CVE-2026-128117 carries a CVSS v3.1 base score of 5.9 (Medium) with the vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H. The security impact is strictly confined to system availability (A:H), with no direct impact on confidentiality (C:N) or integrity (I:N). Because URIError terminates execution immediately before route rendering or data loading occurs, attackers cannot leverage this flaw to bypass authentication, read memory, or modify application state.
The severity of the availability impact depends heavily on the deployment architecture and runtime adapter:
@sveltejs/adapter-node: High impact. Because a single Node.js process typically serves all concurrent HTTP requests in a standalone container or virtual machine, an unhandled exception crashes the entire process. All in-flight requests are dropped simultaneously. If the process is not supervised by a process manager (such as systemd, PM2, or Kubernetes with an active restart policy), the application remains offline indefinitely. Even with automatic restarts, an attacker can send a low-rate stream of single HTTP requests (e.g., one request every few seconds) to hold the server in a continuous crash loop (CrashLoopBackOff).adapter-vercel, adapter-cloudflare, adapter-netlify, adapter-auto): Minimal impact. Serverless platforms isolate execution contexts or catch unhandled exceptions per invocation at the platform runtime wrapper layer, returning an HTTP 500 Internal Server Error for the malformed request without affecting other concurrent users.Application developers should upgrade @sveltejs/kit to the patched versions immediately:
@sveltejs/kit@2.70.4 or later.@sveltejs/kit@3.1.1 or later.Update the dependency in package.json and refresh your lockfile using your package manager:
npm install -D @sveltejs/kit@latest
# or
pnpm update @sveltejs/kit
# or
yarn upgrade @sveltejs/kitIf an immediate framework upgrade cannot be performed in production, organizations can neutralize the vulnerability using any of the following mitigations:
reroute: Wrap the reroute hook logic in src/hooks.js with an explicit decodeURI(url.pathname) validation check. If decodeURI throws a URIError, return undefined (or url.pathname) so that resolved_path retains the malformed pathname and gets caught by SvelteKit's existing try...catch block:// src/hooks.js
export const reroute = ({ url }) => {
try {
decodeURI(url.pathname);
} catch {
// Do not rewrite malformed paths; let SvelteKit's try/catch return HTTP 400
return undefined;
}
// Safe to execute custom reroute rules below
if (url.pathname.endsWith('/reroute/malformed')) {
return '/';
}
};Edge Proxy / WAF Normalization: Place a reverse proxy (such as NGINX, Envoy, Cloudflare, or AWS ALB/WAF) in front of the @sveltejs/adapter-node instance configured to strictly validate URI percent-encoding and reject malformed HTTP requests at the network edge before forwarding traffic upstream.
Process Supervision: Ensure all @sveltejs/adapter-node processes run under a process supervisor (systemd, PM2, Docker restart: unless-stopped, or Kubernetes restartPolicy: Always) to guarantee automatic recovery in the event of an unexpected process exit.
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/kit sveltejs | < 2.70.4 | 2.70.4 |
@sveltejs/kit sveltejs | >= 3.0.0, < 3.1.1 | 3.1.1 |
| Attribute | Detail |
|---|---|
| CVE / GHSA ID | CVE-2026-128117 / GHSA-mw6w-2fmv-pvw3 |
| Weakness Class | CWE-248 (Uncaught Exception) |
| 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) |
| Affected Package | @sveltejs/kit (< 2.70.4, >= 3.0.0 < 3.1.1) |
| Vulnerable Function | internal_respond() in packages/kit/src/runtime/server/respond.js |
| Primary Impact | Remote Denial of Service (Node.js process termination in adapter-node) |
| Exploit Status | Proof of Concept Available |
| CISA KEV Status | Not Listed |
An exception is thrown during execution that is not caught by an application exception handler, causing the program to terminate abnormally or enter an unstable state.
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-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.
An uncontrolled memory allocation vulnerability exists in ag-psd versions prior to 31.0.0. When parsing Adobe Photoshop Document (PSD) files, the readPsd() function reads image dimensions and color depth directly from the file header and allocates off-heap typed arrays without validating whether the requested memory size is proportional to the input stream or bounded by operational thresholds. An unauthenticated attacker can supply a crafted 52-byte PSD payload that triggers an immediate 3.35 GB memory allocation, resulting in process termination via the operating system Out-Of-Memory (OOM) killer.
CVE-2026-126297 (GHSA-v8f3-6phw-6mh9) is an uncontrolled resource consumption and algorithmic complexity vulnerability in the Natural Language Toolkit (NLTK) prior to version 3.10.4. Located in the Semantic Role Labeling (SRL) parser within `nltk/corpus/reader/conll.py`, the flaw allows an attacker to trigger quadratic O(N^2) CPU processing overhead by providing crafted CoNLL-formatted corpus inputs containing decoy span columns or nested full-width verb spans. Applications parsing untrusted linguistic corpora can experience prolonged CPU exhaustion and application-level Denial of Service (DoS).
CVE-2026-126298 (also tracked as GHSA-r53h-rw34-8h97) is an algorithmic complexity Denial of Service (DoS) vulnerability in the Natural Language Toolkit (NLTK) Python library affecting all versions prior to 3.10.4. The vulnerability resides in the nltk.tree.transforms.chomsky_normal_form function and its corresponding public method Tree.chomsky_normal_form(). When transforming a parse tree into Chomsky Normal Form using the default right-factoring strategy, the function repeatedly invokes list.pop(0) inside a loop iterating over all children of a node. In CPython, removing the initial element of a standard list requires an O(N) memory shift operation, causing the overall binarization algorithm to degrade to O(N^2) quadratic time complexity relative to the width of a node. Furthermore, converting horizontal node width into vertical tree depth bypasses initial parsing depth bounds, triggering subsequent stack exhaustion (RecursionError) when recursive methods traverse the mutated structure. Unauthenticated remote attackers who can submit bracketed tree strings to applications invoking this transformation can exhaust single-threaded CPU resources or induce unhandled application exceptions.