Jan 16, 2026·6 min read·39 visits
The `devalue` library, a staple in the Svelte ecosystem for serializing JS data, failed to validate types during hydration. By passing a massive integer length instead of an `ArrayBuffer` reference to a TypedArray constructor, an attacker can trick the server into allocating gigabytes of memory instantly. A separate vector allows for infinite recursion via circular references. Both lead to a process crash (DoS).
A critical Denial of Service (DoS) vulnerability in the `devalue` library allows attackers to trigger massive memory allocations or stack overflows via malformed JSON input, crashing Node.js servers.
JavaScript Object Notation (JSON) is great, but it’s famously stupid. It doesn't know what a Map is, it chokes on Set, it panics at undefined, and if you show it a circular reference, it curls up into a ball and dies. Enter devalue. It’s the smarter, cooler cousin of JSON.stringify, designed to handle all the weird, messy bits of JavaScript data structures. It is heavily used in the Svelte ecosystem, particularly in SvelteKit, to transport server-side state to the client.
But here's the catch: when you build a tool that promises to reconstruct complex object graphs from simple strings, you are essentially writing an interpreter. And as any battle-scarred security researcher knows, parsers are where the demons live. If you trust the input string to tell you how to build the object in memory, you better be absolutely certain that the blueprint isn't asking you to build a skyscraper on a swamp.
The developers of devalue assumed that the input would mostly be well-formed data generated by their own library. But in the world of web security, assuming your input is "friendly" is like leaving your front door unlocked because you live in a "nice neighborhood." Spoiler alert: the internet is not a nice neighborhood.
The core of the vulnerability lies in how JavaScript handles TypedArray constructors (like Int8Array, Uint32Array, etc.). These constructors are polymorphic—they behave drastically differently depending on what you feed them. If you pass an existing ArrayBuffer, they create a view over that memory (cheap). If you pass a number or an object with a length property, they allocate new memory (expensive).
During the "unflattening" (hydration) phase, devalue reconstructs objects by looking up references in an array. When it encounters a TypedArray, it expects the referenced value to be a base64-decoded ArrayBuffer. The code looked something like this:
// Simplified vulnerable logic
const wrapper = new TypedArrayConstructor(hydrate(value[1]));The flaw is obvious to a cynical eye: hydrate(value[1]) recursively resolves the argument. If an attacker controls the input, they can make value[1] resolve to anything. Instead of a nice, safe buffer, what if we provide a plain object like { "length": 2000000000 }?
JavaScript sees an object with a length, shrugs, and says, "Okay, you want a new Int8Array with 2 billion zeros? Coming right up!" The Node.js process immediately attempts to allocate 2GB of RAM. If the server doesn't have it (or if the attacker sends a few concurrent requests), the process hits an Out-of-Memory (OOM) condition and crashes hard.
Let's look at the smoking gun. In the vulnerable versions (pre-5.6.2), the unflatten logic was entirely too trusting. It walked the dependency tree without checking if the branches were actually capable of supporting weight.
Here is the essence of the vulnerable hydration step:
// PRE-PATCH: Blindly trusting the index
case 'Int8Array':
case 'Uint8Array':
// ... other types ...
// value[1] is just an index pointer. We assume it points to a buffer.
return new constructors[type](hydrate(value[1]));And here is the fix implemented in version 5.6.2. The maintainers had to introduce strict type enforcement. They stopped assuming and started verifying. Notice the double-check:
// PATCHED: Trust, but verify
case 'Int8Array':
case 'Uint8Array':
// ...
const bufferIndex = value[1];
// 1. Verify the target is actually labeled as an ArrayBuffer
if (values[bufferIndex][0] !== 'ArrayBuffer') {
throw new Error('Invalid data');
}
// 2. Hydrate it and proceed
return new constructors[type](hydrate(bufferIndex));Additionally, they patched a recursion bug. Previously, you could define a Uint8Array that pointed to itself as its own buffer. The parser would loop infinitely trying to resolve the buffer, blowing the stack. The fix introduced a hydrating Set to track active nodes in the graph, throwing an error if a cycle is detected during resolution.
You don't need a complex fuzzing rig to exploit this. You just need to understand the data structure devalue uses. It flattens the object graph into an array. We can craft a JSON payload where index 0 is our malicious Int8Array, and index 1 is the payload that triggers the allocation.
Here is the "Allocation Attack" Proof of Concept:
// The Exploit Payload
const maliciousPayload = `[
["Int8Array", 1],
{ "length": 2147483647 }
]`;
import { parse } from 'devalue';
// BOOM. Process terminates with fatal error: JavaScript heap out of memory
parse(maliciousPayload);When devalue parses this:
["Int8Array", 1] at index 0.new Int8Array(hydrate(1)).hydrate(1) returns the object at index 1: { length: 2147483647 }.new Int8Array({ length: ... }) triggers a massive allocation.We can also trigger a Stack Overflow with even less code using a self-referential cycle:
// The Infinite Recursion Payload
const stackKiller = `[
["Uint8Array", 0]
]`;
// BOOM. Maximum call stack size exceeded.
parse(stackKiller);In the recursion case, the Uint8Array at index 0 claims its buffer is at index 0. The parser dives into index 0 to resolve the buffer, sees it's a Uint8Array that needs a buffer from index 0, and down the rabbit hole we go.
Why does this matter? "It's just a crash," you might say. But in modern architecture, especially server-side rendering (SSR) contexts like SvelteKit, availability is king. If an unauthenticated attacker can crash your Node.js process with a single HTTP request containing a tiny JSON body, they can take your entire platform offline effortlessly.
Imagine a scenario where an application accepts user state via a cookie or a POST body that gets deserialized on the server. An attacker scripts a loop to send one of these packets every time the server restarts. Effectively, they create a permanent denial of service condition. No complex authentication bypass is needed; just a raw, brute-force resource exhaustion.
This vulnerability scores a CVSS 7.5 (High) because it requires zero privileges and no user interaction. It is a direct shot at the server's jugular.
The fix is straightforward: Update devalue to version 5.6.2 or higher. If you are using SvelteKit, check your lockfiles and ensure the transitive dependency is updated. The patch adds the necessary guardrails to ensure that TypedArray inputs are actually backed by ArrayBuffers and that circular references don't turn into infinite loops.
Beyond this specific patch, this is a lesson in input validation. Never trust a serialization format to validate logic. If you are accepting complex serialized objects from the client, consider implementing size limits on the request body. While that won't stop the logic flaw (the exploit payload is tiny), it's good hygiene.
Additionally, consider using ulimit or container resource limits to ensure that if a process does try to eat all the RAM, it gets killed quickly and restarted without taking down the entire host machine.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
devalue Svelte | >= 5.1.0, < 5.6.2 | 5.6.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-405 (Asymmetric Resource Consumption) |
| Attack Vector | Network (Remote) |
| CVSS | 7.5 (High) |
| Impact | Denial of Service (DoS) |
| Exploit Status | PoC Available |
| Affected Component | devalue.parse() |
Asymmetric Resource Consumption (Amplification)
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.