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

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

CVE-2026-22775

Description

Svelte devalue is a JavaScript library that serializes values into strings when JSON.stringify isn't sufficient for the job. From 5.1.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 ArrayBuffer hydration expecting base64 encoded strings as input, but not checking the assumption before decoding the input. This vulnerability is fixed in 5.6.2.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
devaluenpm
>= 5.1.0, < 5.6.25.6.2

Affected products

1

Patches

1
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) {
    

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

5

News mentions

0

No linked articles in our index yet.