CVE-2026-92708: Your SSR Page Can Ship 8KB of Someone Else's Memory

We planted a string in Node's Buffer pool, serialized an unrelated two-byte Buffer, and got it back. The two bytes came out as 10,976 characters of output carrying 8,192 bytes of memory nobody asked for, canary included.

That's CVE-2026-92708, published on 2026-09-18 against the npm package devalue. You almost certainly have never heard of devalue and almost certainly have it installed, because it's what SvelteKit, Nuxt and Astro use to ship server data into the page.

TL;DR

devalue versions 5.1.0 to 5.9.2 serialize a typed array by emitting its entire backing ArrayBuffer, not just the visible bytes. A small Node Buffer is carved out of a process-wide shared pool, so serializing a 2-byte Buffer emits the whole 8KB pool into your HTML. Fixed in 5.9.3, released 2026-09-18. The part worth your attention: as of 2026-09-21 npm audit returns nothing for the vulnerable version, because the CVE record does not contain the word "npm" anywhere.

We reproduced it

Everything below is our own run on Node v22.22.2, not a restatement of the advisory.

devalue 5.9.2, reproduced 2026-09-21
const { stringify, uneval } = require('devalue');

const secret = Buffer.from('SUPERSECRET-CANARY-' + 'X'.repeat(40));
const small  = Buffer.from('hi');

console.log(secret.buffer === small.buffer);  // true
console.log(small.buffer.byteLength);         // 8192

const out = stringify({ b: small });
console.log(out.length);                      // 10976

The two Buffers share a backing store. small is two bytes; its ArrayBuffer is 8,192. Base64-decoding the payload out of that output returns 8,192 bytes, and the canary string is sitting in it.

uneval was worse: 20,252 characters for the same two bytes.

The workaround in the advisory does work. On the same vulnerable 5.9.2, wrapping the Buffer in new Uint8Array(...) first drops the output to 49 characters, because that copy lands in an exactly-sized buffer of its own.

The advisory says 64KB. We measured 8KB.

The advisory text, and therefore every aggregator page that copied it, says "up to 64 KB of unrelated process memory." We could not reproduce that on a default Node install, and we don't think you can either.

Buffer.poolSize is 8192. It has been 8192 for the life of the API. Every pooled allocation we measured backed onto an 8,192-byte ArrayBuffer:

AllocationBacking ArrayBuffer
Buffer.from('hello')8192
Buffer.allocUnsafe(5)8192
fs.readFileSync(smallFile)8192
Buffer.alloc(5)5 (not pooled)

8KB of somebody else's memory in your HTML is plenty bad. We're flagging the discrepancy because a number that appears on a dozen pages looks corroborated when it's actually one source repeated, and because that last row matters: Buffer.alloc is not pooled, so it is not part of this.

One more correction worth making, since it will otherwise propagate. The advisory describes the risk in terms of "a public page whose load() returns a small Buffer" in "SvelteKit or Nuxt." load() is SvelteKit's API. Nuxt doesn't have one. The affected surface is any server-side data fetch that hands a Buffer to the serializer.

What actually ends up in the pool

The advisory frames the payoff as "another user's request body or Authorization header." We went looking for that and want to be careful about it.

Raw HTTP body chunks and socket reads in Node 22 arrive in exactly-sized, non-pooled buffers. Request bytes do not enter the shared pool just by showing up. They enter it when some code path calls Buffer.from() or Buffer.allocUnsafe() on them, which frameworks and middleware do constantly, so cross-request disclosure is entirely plausible. We did not reproduce it end to end inside a running SvelteKit app, so we're not going to claim it as measured.

What we did reproduce is the file vector, and it's the one to worry about. fs.readFileSync on a small file returns a pooled Buffer. Read a 3-byte file, serialize it, ship 8KB of whatever the process touched recently.

The leak fires on the serialize path, so none of the existing guards help. Every prior devalue advisory covers parse/unflatten and hardening against untrusted input. This one runs when you serialize your own trusted data on the way out. There is no malicious input to filter and nothing to validate. It happens on every render that touches a Buffer.

Why your scanner is quiet

This is the part we'd have wanted to know, and it's checkable in one command.

Against the endpoint npm audit uses, 2026-09-21
$ curl -s -X POST https://registry.npmjs.org/-/npm/v1/security/advisories/bulk \
    -H 'Content-Type: application/json' -d '{"devalue":["5.9.2"]}'
{}

# control: the same endpoint, a different version of the same package
$ curl -s -X POST https://registry.npmjs.org/-/npm/v1/security/advisories/bulk \
    -H 'Content-Type: application/json' -d '{"devalue":["5.9.0"]}'
{"devalue":[{"id":1237790,"title":"Svelte devalue: DoS via malformed input", ...}]}

5.9.2 is a version the CVE names as affected. The feed returns an empty object. The control proves the endpoint works and that devalue is in it.

Three other paths are blind the same way, all checked on 2026-09-21:

  • OSV. Querying api.osv.dev/v1/query for npm devalue at 5.9.2 returns no match for this CVE. The same query at 5.1.0 returns seven older advisories, so the package is well represented.
  • The GitHub Advisory Database. github.com/advisories/GHSA-j22f-vq7h-c4qm is a 404. This is what Dependabot reads.
  • Snyk. Its page for devalue 5.9.2 reports no direct vulnerabilities.

The advisory is not hidden. The repository-scoped page at github.com/sveltejs/devalue/security/advisories/GHSA-j22f-vq7h-c4qm loads fine, titled "stringify/uneval serialize shared memory," High, 7.5, affected >= 5.1.0, <= 5.9.2, patched 5.9.3. The maintainer did the work. It just hasn't propagated into the database the tools query.

The root cause is upstream of all of them

We pulled the raw CVE record from CVE Project's cvelistV5 repository. Its entire affected block is this:

CVE-2026-92708.json, containers.cna.affected
[{
  "vendor": "sveltejs",
  "product": "devalue",
  "versions": [{ "version": ">= 5.1.0, < 5.9.3", "status": "affected" }]
}]

No packageName. No purl. No cpes. No collectionURL. The version is a free-text string rather than a structured range. The substring "npm" does not appear anywhere in the record.

So a scanner keyed on package plus version has nothing to key on. OSV's converter, given no ecosystem, fell back to describing the fix as git commit SHAs and dumped its guesses into an unresolved_ranges field, one of which reads {"introduced": "Uint8Array"} because it scraped that word out of the prose. NVD lists the record as "Received" with no CPE configurations.

The cleanest proof that this is a metadata failure and not a tooling failure: devalue had a second advisory the same week. GHSA-9rgm-9g3h-6x36, the DoS issue, published 2026-09-17, same package and same maintainer. It's in the global GitHub database, it has a proper npm SEMVER range, and npm audit reports it. One record had an ecosystem and one didn't.

This is a dated measurement, not a permanent property of these tools. GitHub routinely ingests CVE records within days. Everything above was true on 2026-09-21 and may be false by the time you read it, which is the point: for a window of at least three days, a CVSS 7.5 memory-disclosure bug in a dependency of three major frameworks was invisible to every automated check a normal team runs. Re-run the commands rather than trusting this paragraph.

Are you affected?

We checked the published dependencies of each framework on 2026-09-21, against the current release of each:

FrameworkLatestDeclares devalue
nuxt4.5.2^5.9.0
@sveltejs/kit2.70.3^5.8.1
astro7.3.3^5.8.1
next16.3.5not a dependency

If you built on Next.js, which is what most AI builders scaffold, this one isn't yours. Close the tab.

For everyone else, the thing to understand is that this is a lockfile problem. Those caret ranges resolve to the newest 5.x, which is now patched, so anyone running npm install today gets the fix without trying. The exposure is in lockfiles already on disk. The affected range runs from 5.1.0 all the way to 5.9.2, so any lockfile whose devalue entry was resolved before 2026-09-18 pins a vulnerable version. None of the three frameworks has published a release since the fix landed either, so nothing is going to drag you forward on its own.

1

Read your resolved version. npm ls devalue. Anything from 5.1.0 to 5.9.2 is affected. This is the whole check.

2

Update it. npm update devalue gets you 5.9.4. If your lockfile is stubborn, npm install devalue@^5.9.3 and let the framework's caret range absorb it.

3

Decide whether to rotate anything. Only if you were serving SSR pages that passed a Buffer or a small file read into page data. The leaked bytes are whatever that process touched recently, which means there is no log to check and no clean way to know what went out. That uncertainty is the reason to rotate rather than a reason to wait.

One thing 5.9.3 does not fix

The patch special-cases Node Buffers. We tested a plain Uint8Array view over a larger shared ArrayBuffer on 5.9.3:

devalue 5.9.3, still emits the whole buffer
const ab = new ArrayBuffer(4096);
new Uint8Array(ab).fill(65);
const view = new Uint8Array(ab, 100, 4);   // a 4-byte view

stringify({ v: view }).length;             // 5515
// [{"v":1},["Uint8Array",2,100,4],["ArrayBuffer","QUFBQUFB...

A four-byte view produced 5,515 characters, and you can see the whole 4KB buffer base64'd into the output. That's deliberate: devalue preserves buffer-sharing semantics for genuine typed arrays, so round-tripping keeps the relationship intact. It's the right call for the library and a trap for you, because if your own code hands devalue a view over a big shared buffer, upgrading changed nothing.

Worth knowing too that 5.9.3 carries seven patch fixes, of which two are security fixes: the Buffer one, and a separate prototype-pollution fix rejecting non-string null-prototype keys in parse. Its release notes don't mention a CVE, so "is this a security release?" is not answerable from the changelog.

What we take from this

The interesting failure here isn't in devalue. A serializer preserving buffer identity is a defensible design that met an allocator optimization nobody was thinking about, which is how most of these go.

The failure is that the safety net had a hole in a shape nobody checks. Running npm audit and getting a clean result is the single most common way a founder concludes their dependencies are fine. For three days, on a real CVSS 7.5, that clean result was produced by a missing field in a JSON file.

If you didn't write most of your code, the honest lesson is narrower than "audit everything." It's that a green check means "nothing matched," and matching depends on metadata that a human typed into a form.

What is CVE-2026-92708?

An information-disclosure flaw in the npm package devalue, published 2026-09-18. Versions 5.1.0 through 5.9.2 serialize a typed array by emitting its entire backing ArrayBuffer instead of just the visible bytes. Because a small Node Buffer is carved out of a shared pool, serializing one emits the whole pool. Fixed in 5.9.3.

Does this affect Next.js?

No. We checked the published dependencies of each framework on 2026-09-21. The current releases of nuxt, SvelteKit and Astro all declare devalue as a direct dependency. next@16.3.5 does not depend on devalue at all.

Will npm audit or Dependabot warn me about this?

As of 2026-09-21, no. We queried the same advisory endpoint npm audit uses and it returned nothing for devalue 5.9.2, while returning results for other versions. The CVE record has no npm package identifier in it at all, so a scanner keyed on package and version has nothing to match. This may change once the advisory propagates.

I ran npm install today. Am I safe?

Probably, and by accident. The frameworks request a caret range that now resolves to a patched 5.9.x, so a fresh install picks up the fix. The exposure sits in lockfiles already on disk: the affected range is 5.1.0 to 5.9.2, so anything resolved before 2026-09-18 pins a vulnerable version. Run npm ls devalue and read the number.

Does upgrading to 5.9.3 fix every case?

It fixes Node Buffers, which is the case in the CVE. We tested a plain Uint8Array view over a larger shared ArrayBuffer on 5.9.3 and it still emits the entire backing buffer, by design, because devalue preserves buffer-sharing semantics for real typed arrays. If your own code hands devalue a view over a big shared buffer, upgrading does not cover you.

See What Your App Actually Exposes

A clean dependency audit is one signal, not a verdict. Run a free scan and see what your deployed app gives away.

Vulnerability Guides

CVE-2026-92708: Your SSR Page Can Ship 8KB of Someone Else's Memory