GHSA-673q-mjm6-r5xr: Unauthenticated RCE in Next.js "use cache" Functions
GHSA-673q-mjm6-r5xr is rated Critical, CVSS v4.0 9.5, in the GitHub Advisory Database: an unauthenticated attacker can run code on a Next.js 16 server that uses App Router with Cache Components and has a use cache function returning multiple typed arrays. The database review classifies the flaw as deserialization of untrusted data (CWE-502) in the React Server Components transport protocol that Next.js bundles. Fixed in Next.js 16.4.1, with no workaround available.
TL;DR
- An unauthenticated attacker can achieve remote code execution on a Next.js 16 application that enables Cache Components and has a
use cachefunction returning multiple typed arrays. The GitHub Advisory Database review classifies the flaw as CWE-502, deserialization of untrusted data. - All Next.js 16 releases from 16.0.0 through 16.4.0 are affected. The GitHub Advisory Database rates it Critical, CVSS v4.0 9.5, exploitable over the network without authentication or user interaction.
- No public proof of concept exists, no exploitation in the wild is known as of October 23, 2026, and the issue is not in the CISA Known Exploited Vulnerabilities catalog.
- Upgrade to Next.js 16.4.1 now. Vercel states there is no workaround and recommends upgrading immediately even if your application does not have
use cachefunctions returning typed arrays.
Am I affected?
You are affected if your application runs Next.js 16.0.0 through 16.4.0 with the App Router and Cache Components enabled. Cache Components are the Next.js 16 caching model where functions marked with the use cache directive store and restore their results across requests. The advisory's trigger condition is a use cache function that returns multiple typed arrays, such as Uint8Array or Float64Array values.
| Branch | Affected versions | Fixed version |
|---|---|---|
| Next.js 16.x | 16.0.0 – 16.4.0 | 16.4.1 |
Check what is installed:
# Installed Next.js version
npm ls next
node -e "console.log(require('next/package.json').version)"
# Lock file check (package-lock.json v2/v3 records the package under "node_modules/next")
grep -A1 '"node_modules/next"' package-lock.json
# Container image check
docker run --rm <your-image> node -e "console.log(require('next/package.json').version)"
Then check whether your code opts into the affected feature. Cache Components are an opt-in configuration, and use cache appears directly in server code:
# Cache Components enabled in the Next.js config
grep -n "cacheComponents" next.config.*
# Any use of the cache directive across the project, including src/ and shared libraries
grep -rn "use cache" --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" --exclude-dir=node_modules --exclude-dir=.next .
A single function returning two or more typed arrays meets the published trigger condition.
How do I fix it?
Upgrade Next.js to 16.4.1 or later:
npm install next@16.4.1
# or: pnpm add next@16.4.1 | yarn add next@16.4.1
There is no workaround. Vercel's advisory is explicit: upgrade immediately, even if your application does not have such use cache functions. The same release also fixes the unauthenticated Image Optimization RCE published the same day (GHSA-2464-fhr4-w9xw, caused by the upstream librsvg flaw CVE-2026-96889), so one upgrade addresses both.
What is the root cause?
Next.js runs on React Server Components (RSC), where the server renders component output into a stream of serialized chunks, usually called flight data. Reading that stream back is deserialization: the runtime walks the chunks, resolves references, and rebuilds objects, promises, streams, typed arrays and server function references. Under Cache Components, the return value of a use cache function is stored and restored through this same transport, so the deserializer handles data whose origin is a cache entry. The advisory classifies the flaw as CWE-502, deserialization of untrusted data.
Vercel has not published the precise exploit path, so the analysis below comes from reading the fix. The patch, commit a4cea6a titled "Improve Server Component transport protocol", replaces the bundled react-server-dom-webpack and react-server-dom-turbopack runtimes across all twelve build variants with versions that harden the deserialization boundary in four places.
First, the old deserializer resolved every model entry without checking whether a property named then was about to make an ordinary object look like a promise. JavaScript treats any object with a callable then property as a promise (a thenable), and code that awaits or chains such an object calls that function. The new code refuses the confusion at parse time:
// after the fix, in the bundled react-server-dom client and server runtimes
function createModel(response, model, parentObject, key) {
return "then" === key && "function" === typeof model ? null : model;
}
Before the fix, createModel returned the model unchanged, so a deserialized object carrying a then function stayed live in the rebuilt object graph and could be invoked wherever the runtime or application code treated it as a promise.
Second, server reference resolution, which maps a serialized reference to a real server function and can end in requireModule loading code, now validates its input:
function loadServerReference(response, metaData, parentObject, key) {
if ("then" === key) return null;
var id = metaData.id;
if ("string" !== typeof id) return null;
...
Before the fix, metaData.id reached resolveServerReference and requireModule without a type check.
Third, the runtime stopped exposing its internal response object through the chunk graph. Old code stored the live response object directly as chunk.reason on every resolved model chunk; a property walk over rebuilt objects could reach it, and through it the deserialization machinery. The new code hides the response behind a runtime-generated symbol on a holder object, so ordinary property traversal cannot reach it:
// before: new ReactPromise("resolved_model", value, response)
// after:
var RESPONSE_SYMBOL = Symbol();
function createResolvedModelChunk(response, value) {
var holder = {};
return new ReactPromise("resolved_model", value,
((holder[RESPONSE_SYMBOL] = response), holder));
}
Fourth, the transport now separates what previously shared one keyspace. Serialized payloads travel as form-data entries keyed by a prefix plus a chunk id. The changes, one per concern:
- The old deserializer resolved internal state with a raw query against every entry:
response._formData.data.get(response._prefix + id), and typed-array reads did the same viaresponse._formData.data.get(key).arrayBuffer(). - The new code buckets entries by kind on arrival: stream chunks under a key prefixed with the field prefix plus "S", string chunks and binary blobs each in their own map, and typed-array reads go through
getBackingBlob(response._formData, key). - On the serialization side, the client writes stream entries under
formFieldPrefix + "S" + streamIdinstead of the bareformFieldPrefix + streamId. - Typed-array serialization copies a typed array larger than 4096 bytes (2048 bytes in the browser builds) when its view does not start at the beginning of its backing buffer, does not span the whole buffer, or the buffer is resizable.
In one sentence: the deserializer trusted the structure of entries it read back, accepting objects that masquerade as promises, server references with arbitrary identifier types, and entries whose kind was determined only by which key they happened to carry, and the patch adds a type and kind check at each of those trust points.
How is it exploited?
No proof of concept is public, and Vercel has not documented the exact request path, so what follows is the exploitation class the advisory and the patch describe, not a step-by-step recipe.
- The application opts into Cache Components and defines a
use cachefunction that returns multiple typed arrays. Each typed array becomes a separate binary chunk in the serialized payload, which is the condition Vercel calls out as required. - An unauthenticated request reaches the application. The CVSS vector (network, no privileges, no user interaction) and the CWE-502 classification describe the attack surface: the attacker's input reaches data that the flight deserializer processes.
- Inside that data, the attacker supplies entries crafted against the trust gaps the patch closes: an object with a
thenfunction that the runtime will call as a promise; or a server reference whoseidis not a string but resolves to attacker-chosen content; or an entry that rides the flat keyspace so the deserializer treats it as an internal chunk. - The deserializer resolves the crafted entry. Through server reference resolution, the path can reach
requireModule, the function that maps a serialized reference to real server code. - The attacker-chosen reference executes with the privileges of the Next.js server process: remote code execution.
The typed-array condition fits this path directly: the fix's serialization half corrects how typed arrays whose views do not span their whole backing buffer are emitted, and its deserialization half changes how their backing blobs are looked up, which is the code that runs when several typed arrays share one payload.
Attack flow
Cache Components route"] --> B["use cache function returns
multiple typed arrays"] B --> C["Flight protocol serializes the result
as chunked form-data entries"] C --> D["Attacker-influenced entry shares the
flat keyspace with internal chunks"] D --> E["Deserializer resolves the entry as an
internal model or server reference"] E --> F["thenable confusion or unvalidated
reference id is accepted"] F --> G["requireModule resolves the
reference, code executes"]
The missing checks are at D and E: before the fix, nothing validated an entry's kind or a server reference's identifier type before resolution.
What can an attacker do?
Successful exploitation runs arbitrary code as the Next.js server process. That process typically holds deployment secrets, database connections and service credentials, and it can read and rewrite everything the application can reach. The CVSS v4.0 vector (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:N) scores confidentiality and integrity high on both the vulnerable system and subsequent systems, and availability high on the vulnerable system only.
The conditions an attacker needs, as far as the advisory states them:
- The application runs Next.js 16.0.0 through 16.4.0 with App Router and Cache Components enabled.
- The application has a
use cachefunction that returns multiple typed arrays. - The attack is unauthenticated and needs no user interaction, but attack requirements are marked present in the CVSS vector, which matches the feature-dependent trigger condition above.
Vercel's instruction to upgrade even without such functions suggests the boundary should not be treated as a reliable shield, since application code changes and the transport is shared infrastructure.
Why does the fix work?
The patch commit a4cea6a replaces the bundled React Server Components runtime on every build target, server and client, webpack and turbopack, development and production. Each of the four changes closes one trust gap at the deserialization boundary:
-function createModel(response, model) {
- return model;
+function createModel(response, model, parentObject, key) {
+ return "then" === key && "function" === typeof model ? null : model;
}
function loadServerReference(response, metaData, parentObject, key) {
+ if ("then" === key) return null;
+ var id = metaData.id;
+ if ("string" !== typeof id) return null;
...
A then function in deserialized data is dropped, so no rebuilt object can masquerade as a promise. A server reference must carry a string identifier before anything resolves or requires it. The response object is out of reach of property traversal. And the entry buckets plus the corrected typed-array serialization mean a chunk's kind follows from where it is stored, not from an attacker's choice of key.
Is the fix complete? It closes every gap the patch touches, across all twelve bundled runtime variants, which is the entire surface Next.js ships. What cannot be verified from public material is whether the original report used exactly one of these paths or a combination; Vercel published no exploit details. The remediation covers the class either way, because every path into the deserializer now passes the added checks.
How do I check if I was compromised?
No indicators of compromise have been published for this advisory. Absent public guidance, look for these general signals:
- Unexpected POST traffic to routes under Cache Components, especially requests with form-data bodies whose field names carry serialization prefixes rather than normal form fields.
- Cache handler entries whose keys or payloads look malformed or contain chunk ids that do not correspond to legitimate cache writes, if your cache handler logs keys.
- Server-side stack traces from the flight deserializer, such as errors resolving server references or initializing model chunks, which do not match your own code.
- Any evidence of code execution around the time of such errors, including new files, outbound connections from the server process, or modified environment state.
If you find suspicious activity, treat the server process as fully compromised: rotate every secret it could read, including deployment tokens, database credentials and API keys, and rebuild the deployment from a known-good state.
Timeline
- October 14, 2026: Vercel published GHSA-673q-mjm6-r5xr and released Next.js 16.4.1, which contains the Server Component transport fix (commit a4cea6a, authored October 14, 2026).
- October 22, 2026: The advisory was published and reviewed in the GitHub Advisory Database.
- October 23, 2026: This post. No proof of concept is public, no exploitation in the wild is known, and the issue is not in the CISA KEV catalog.
FAQ
Is there a CVE for this?
As of October 23, 2026 the advisory GHSA-673q-mjm6-r5xr has no CVE identifier; it is tracked by its GHSA id in the GitHub Advisory Database.
Do I need Cache Components enabled to be affected?
The advisory describes the vulnerability in applications using App Router with Cache Components and a use cache function returning multiple typed arrays. However, Vercel tells all users to upgrade immediately even without such functions, and 16.4.1 also fixes the Image Optimization RCE, so the practical answer is to upgrade either way.
What are typed arrays and why do several of them matter?
A typed array, like Uint8Array, is a fixed-length binary view over a block of memory. In the flight protocol each one is serialized as its own chunk with its own key, so a function returning several of them produces a payload with many chunk entries, which is the shape the published trigger condition describes.
Can I just remove the typed arrays from my cached functions?
That removes the published trigger condition from your code, but it is not a fix: Vercel states no workaround exists and asks for an immediate upgrade, and your application can regain the condition through any code change. Upgrade to 16.4.1.
Who found and fixed this?
GitHub user evolutionstorm reported the issue, credited as finder in the GitHub Advisory Database entry. The fix was authored by Vercel staff in the bundled React Server Components runtime, and the advisory was published by Vercel's security team.
How popular is the affected software?
The next package recorded 44,586,009 downloads in the week of October 16-22, 2026 on npm, making this one of the most deployed web frameworks in the JavaScript ecosystem.
Scan your Next.js applications with Ostorlab to surface the affected 16.x version and use cache usage across your repositories.
References
- Vercel advisory GHSA-673q-mjm6-r5xr: https://github.com/vercel/next.js/security/advisories/GHSA-673q-mjm6-r5xr (checked October 23, 2026)
- React advisory GHSA-xqc2-xf4c-x3h3, the same typed-array RCE in react-server-dom-webpack, react-server-dom-turbopack and react-server-dom-parcel, fixed in 19.0.9, 19.1.10, 19.2.9 and 19.3.1: https://github.com/react/react/security/advisories/GHSA-xqc2-xf4c-x3h3 (checked October 26, 2026)
- GitHub Advisory Database entry (severity, CVSS v4.0 9.5 and CWE-502 confirmed October 24, 2026): https://github.com/advisories/GHSA-673q-mjm6-r5xr
- Fix commit a4cea6a, "Improve Server Component transport protocol": https://github.com/vercel/next.js/commit/a4cea6aec5da89d116b8960e113189cf51745d2f (checked October 23, 2026)
- Next.js 16.4.1 release notes: https://github.com/vercel/next.js/releases/tag/v16.4.1 (checked October 23, 2026)
- npm download counts for
next(44,586,009 downloads, week of October 16-22, 2026): https://api.npmjs.org/downloads/point/last-week/next (checked October 24, 2026)