Feb 19, 2026·6 min read·34 visits
If you enabled 'experimental.remoteFunctions' in SvelteKit < 2.52.2, your server is vulnerable to DoS. A malformed binary form payload can force the server into an expensive recursive type coercion loop.
An experimental feature in SvelteKit introduced a severe Denial of Service vulnerability via the binary form deserialization logic. By exploiting how the 'devalue' library handles object references and type coercion, an attacker can trigger massive CPU consumption.
Developers love experimental features. It's the software equivalent of an 'Uncharted Territory' sign—irresistible. SvelteKit introduced remoteFunctions to allow seamless RPC-like calls between the client and server. To make this efficient, they needed something better than JSON. Enter devalue, a library that handles complex types like Map, Set, and cyclic references that JSON.stringify would choke on.
But here's the kicker: to support file uploads within this remote function architecture, SvelteKit implemented a custom binary deserializer for application/x-sveltekit-formdata. This deserializer reads a stream of bytes and reconstructs the form data, including file metadata. It's clever, efficient, and, as it turns out, completely trusting of the input it receives.
The vulnerability lies in how this deserializer reconstructs File objects. It assumes the metadata provided by the client (name, size, type) is well-formed. Spoiler alert: in the world of security, assuming input is well-formed is like leaving your front door unlocked because you live in a 'nice neighborhood'.
The root cause here is a classic JavaScript foot-gun: Implicit Type Coercion. When the deserialize_binary_form function processes the incoming byte stream, it encounters a definition for a File. It expects an array containing metadata: [name, type, size, lastModified, index].
The code then attempts to verify that the file data doesn't exceed the content length of the request. It does this with a simple arithmetic check:
// The code blindly trusts 'size' is a number
if (files_start_offset + file_offsets[index] + size > content_length) {
throw deserialize_error('file data overflow');
}See the problem? In JavaScript, the + operator and > comparison are not strictly typed. If size is not a number, the engine will try to make it a number. If an attacker provides a complex object (like a deeply nested array or a proxy to a huge BigInt) instead of a simple integer, the JavaScript engine triggers valueOf() or toString().
Because devalue allows for defining complex, recursive object graphs, an attacker can craft a payload where size references a massive, self-referential structure. The engine burns 100% CPU trying to coerce this monstrosity into a primitive number just to evaluate that if statement.
Let's look at the diff. It's a perfect example of "we forgot to check types." The fix, introduced in version 2.52.2, essentially says: "I don't care what you think you are, you better be a number."
Here is the vulnerable logic versus the patched logic in packages/kit/src/runtime/form-utils.js:
Before (Vulnerable):
File: ([name, type, size, last_modified, index]) => {
// Yolo. Let's do math on whatever 'size' is.
if (files_start_offset + file_offsets[index] + size > content_length) {
throw deserialize_error('file data overflow');
}
return new File(/*...*/);
}After (Patched):
File: ([name, type, size, last_modified, index]) => {
// The bouncer checks ID at the door.
if (
typeof name !== 'string' ||
typeof type !== 'string' ||
typeof size !== 'number' ||
typeof last_modified !== 'number' ||
typeof index !== 'number'
) {
throw deserialize_error('invalid file metadata');
}
if (files_start_offset + file_offsets[index] + size > content_length) {
throw deserialize_error('file data overflow');
}
// ...
}The fix is boring, effectively just a block of typeof checks. But boring is safe. Boring is reliable.
To exploit this, we don't need a buffer overflow or a ROP chain. We just need to understand devalue serialization format. We construct a payload where the size property of a file definition points to a recursive array structure.
Here is a conceptual breakdown of the attack vector:
size) is a reference to that array.remoteFunctions.When the server parses this, it hits the size > content_length check. The engine tries to convert that recursive array to a number. It hangs.
// Conceptual PoC Payload Generator
const payload = JSON.stringify([
// Index 0: The trap. A recursive structure.
[...Array(100)].map(() => 0), // Base array
// ... complex cross-references ...
// The Trigger: A File object definition
// [name, type, size (pointing to recursive structure), lastMod, index]
['malicious.txt', 'text/plain', ['BigInt', 1e100], 123456789, 0]
]);The actual PoC found in the regression tests uses a mix of deep arrays and BigInt coercions to maximize the CPU spin. Since Node.js is single-threaded for the event loop, one such request effectively kills the server for everyone else.
This is a Denial of Service (DoS) vulnerability. In a microservices environment, losing one node might be annoying. But for a monolithic SvelteKit application, this is fatal.
Because the exhaustion happens synchronously during the request parsing phase (before your business logic even runs), you cannot catch this with standard application-level try/catch blocks easily. The event loop locks up. Health checks fail. Kubernetes kills the pod. The pod restarts. The attacker sends another request.
It's an asymmetric attack: The attacker spends almost zero resources sending a small JSON payload, while your server spends 100% CPU trying to figure out if [Array(10000)] > 100.
The remediation is straightforward. If you are using @sveltejs/kit versions prior to 2.52.2, you have two options:
2.52.2 immediately. This includes the patch that enforces strict type checking on deserialized file metadata.svelte.config.js. If you have experimental: { remoteFunctions: true }, set it to false or remove it. This feature is opt-in, so if you didn't explicitly turn it on, you are safe.Always treat experimental flags as exactly what they are: code that hasn't been battle-hardened yet.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
@sveltejs/kit Svelte | < 2.52.2 | 2.52.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 (Uncontrolled Resource Consumption) |
| Attack Vector | Network (Remote) |
| CVSS Score | 7.5 (High) |
| Impact | Denial of Service (CPU Exhaustion) |
| Exploit Status | PoC Available |
| Affected Component | remoteFunctions / devalue deserializer |
PyJWT versions 2.8.0 through 2.12.1 are vulnerable to an unauthenticated Denial of Service (DoS) attack. When verifying detached JSON Web Signatures (JWS) using the unencoded-payload option (RFC 7797, b64=false), the library eagerly decodes the payload segment before verifying the header configuration or the cryptographic signature. This behavior enables a remote, unauthenticated attacker to inject an arbitrarily large payload segment, triggering excessive CPU and memory resource consumption prior to signature validation.
Nodemailer prior to version 8.0.9 contains a security control bypass vulnerability. Transport-level configuration parameters designed to restrict local file system access and remote URL requests are not propagated to all content-resolution execution paths. This failure allows unauthorized local file inclusion and server-side request forgery when the application utilizes specific transports or processing flags.
GHSA-268h-hp4c-crq3 is a Carriage Return Line Feed (CRLF) injection vulnerability in the Nodemailer npm package affecting versions up to and including 8.0.8. The library allows arbitrary email header injection when parsing user-controlled comments within list headers (such as List-Unsubscribe or List-ID). This occurs because list headers bypass standard validation by utilizing an internal 'prepared' flag, causing unsanitized newlines to be emitted directly into the outgoing RFC822 mail stream. This exploit allows remote attackers to inject custom, unauthorized mail headers, disrupting signature checks, bypassing filters, or spoofing parameters.
A logic flaw in PyJWT's PyJWKClient class allows remote unauthenticated attackers to trigger a complete authentication outage. By transmitting a volume of JWTs containing randomized, non-existent Key ID (kid) values, attackers force synchronous outbound JWKS resolution queries. When these queries fail or time out, a defect in the error cleanup code overwrites the local cache of valid signing keys with None, causing a denial of service.
A high-severity type-confusion path traversal vulnerability (CVE-2026-49982 / GHSA-7c78-jf6q-g5cm) exists in the node-tmp package version 0.2.6. The vulnerability allows remote attackers to bypass path validation checks by passing non-string data types such as Arrays or duck-typed Objects into options like prefix, postfix, or template. Because the library relies on the .includes() method without verifying the input type, standard array checks evaluate differently than string checks. Downstream string coercion subsequently restores the traversal sequence, allowing files and directories to be created outside the designated temporary directory root. This can result in arbitrary file writes and potential local file execution depending on application context.
CVE-2026-47347 is an open redirect vulnerability affecting multiple TYPO3 CMS versions. The issue resides in GeneralUtility::sanitizeLocalUrl, where an insufficient blocklist validation implementation fails to prevent browsers from normalizing malformed relative paths into external protocol-relative redirections. Attackers can exploit this to conduct phishing, session hijacking, or credential harvesting campaigns.