VYPR
High severityOSV Advisory· Published Jan 15, 2026· Updated Jan 15, 2026

devalue vulnerable to denial of service due to memory exhaustion in devalue.parse

CVE-2026-22774

Description

Svelte devalue is a JavaScript library that serializes values into strings when JSON.stringify isn't sufficient for the job. From 5.3.0 to 5.6.1, certain inputs can cause devalue.parse to consume excessive CPU time and/or memory, potentially leading to denial of service in systems that parse input from untrusted sources. This affects applications using devalue.parse on externally-supplied data. The root cause is the typed array hydration expecting an ArrayBuffer as input, but not checking the assumption before creating the typed array. This vulnerability is fixed in 5.6.2.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
devaluenpm
>= 5.3.0, < 5.6.25.6.2

Affected products

1

Patches

2
11755849fa06

Merge commit from fork

https://github.com/sveltejs/devalueElliott JohnsonJan 15, 2026via ghsa
4 files changed · +61 3
  • .changeset/slow-banks-love.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +"devalue": patch
    +---
    +
    +fix: validate input for `ArrayBuffer` parsing
    
  • .changeset/tender-cities-check.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +"devalue": patch
    +---
    +
    +fix: more helpful errors for inputs causing stack overflows
    
  • src/parse.js+28 1 modified
    @@ -33,6 +33,13 @@ export function unflatten(parsed, revivers) {
     
     	const hydrated = Array(values.length);
     
    +	/**
    +	 * A set of values currently being hydrated with custom revivers,
    +	 * used to detect invalid cyclical dependencies
    +	 * @type {Set<number> | null}
    +	 */
    +	let hydrating = null;
    +
     	/**
     	 * @param {number} index
     	 * @returns {any}
    @@ -70,7 +77,18 @@ export function unflatten(parsed, revivers) {
     						// so we need to munge it into the format expected by a custom reviver
     						i = values.push(value[1]) - 1;
     					}
    -					return (hydrated[index] = reviver(hydrate(i)));
    +
    +					hydrating ??= new Set();
    +
    +					if (hydrating.has(i)) {
    +						throw new Error('Invalid circular reference');
    +					}
    +
    +					hydrating.add(i);
    +					hydrated[index] = reviver(hydrate(i));
    +					hydrating.delete(i);
    +
    +					return hydrated[index];
     				}
     
     				switch (type) {
    @@ -125,6 +143,12 @@ export function unflatten(parsed, revivers) {
     					case 'Float64Array':
     					case 'BigInt64Array':
     					case 'BigUint64Array': {
    +						if (values[value[1]][0] !== 'ArrayBuffer') {
    +							// without this, if we receive malformed input we could
    +							// end up trying to hydrate in a circle
    +							throw new Error('Invalid data');
    +						}
    +
     						const TypedArrayConstructor = globalThis[type];
     						const buffer = hydrate(value[1]);
     						if (!(buffer instanceof ArrayBuffer)) {
    @@ -142,6 +166,9 @@ export function unflatten(parsed, revivers) {
     
     					case 'ArrayBuffer': {
     						const base64 = value[1];
    +						if (typeof base64 !== 'string') {
    +							throw new Error('Invalid ArrayBuffer encoding');
    +						}
     						const arraybuffer = decode64(base64);
     						hydrated[index] = arraybuffer;
     						break;
    
  • test/test.js+23 2 modified
    @@ -745,6 +745,11 @@ const invalid = [
     		json: '[["Int8Array", 1], { "length": 2 }, 1000000000]',
     		message: 'Invalid input, expected ArrayBuffer but got object'
     	},
    +	{
    +		name: 'ArrayBuffer with non-string value',
    +		json: '[["ArrayBuffer", { "length": 100 }]]',
    +		message: 'Invalid ArrayBuffer encoding'
    +	},
     	{
     		name: 'empty string',
     		json: '',
    @@ -802,13 +807,29 @@ const invalid = [
     		name: 'bad index',
     		json: '[{"0":1,"toString":"push"},"hello"]',
     		message: 'Invalid input'
    +	},
    +	{
    +		name: 'TypedArray self-reference',
    +		json: '[["Uint8Array", 0]]',
    +		message: 'Invalid data'
    +	},
    +	{
    +		name: 'custom reviver self-reference',
    +		json: '[["Custom", 0]]',
    +		revivers: { Custom: (v) => v },
    +		message: 'Invalid circular reference'
    +	},
    +	{
    +		name: 'mutual TypedArray reference',
    +		json: '[["Uint8Array", 1], ["Uint8Array", 0]]',
    +		message: 'Invalid data'
     	}
     ];
     
    -for (const { name, json, message } of invalid) {
    +for (const { name, json, message, revivers } of invalid) {
     	uvu.test(`parse error: ${name}`, () => {
     		assert.throws(
    -			() => parse(json),
    +			() => parse(json, revivers),
     			(error) => {
     				const match = error.message === message;
     				if (!match) {
    
e46afa64dd2b

Merge commit from fork

https://github.com/sveltejs/devalueElliott JohnsonJan 15, 2026via ghsa
3 files changed · +15 1
  • .changeset/swift-planes-fry.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +"devalue": patch
    +---
    +
    +fix: validate input for typed arrays
    
  • src/parse.js+5 1 modified
    @@ -126,7 +126,11 @@ export function unflatten(parsed, revivers) {
     					case 'BigInt64Array':
     					case 'BigUint64Array': {
     						const TypedArrayConstructor = globalThis[type];
    -						const typedArray = new TypedArrayConstructor(hydrate(value[1]));
    +						const buffer = hydrate(value[1]);
    +						if (!(buffer instanceof ArrayBuffer)) {
    +							throw new Error(`Invalid input, expected ArrayBuffer but got ${typeof buffer}`);
    +						}
    +						const typedArray = new TypedArrayConstructor(buffer);
     
     						hydrated[index] =
     							value[2] !== undefined
    
  • test/test.js+5 0 modified
    @@ -740,6 +740,11 @@ for (const [name, tests] of Object.entries(fixtures)) {
     }
     
     const invalid = [
    +	{
    +		name: 'typed array with non-ArrayBuffer input',
    +		json: '[["Int8Array", 1], { "length": 2 }, 1000000000]',
    +		message: 'Invalid input, expected ArrayBuffer but got object'
    +	},
     	{
     		name: 'empty string',
     		json: '',
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.