Nov 10, 2026·11 min read·6 visits
React Server Components packages in the 19.x release branches are vulnerable to unauthenticated remote code execution (CVSS v4.0 9.5) via crafted Flight payloads when transporting multiple typed arrays and untrusted values. Users must upgrade to versions 19.0.9, 19.1.10, 19.2.9, or 19.3.1 immediately.
A critical deserialization vulnerability exists in React Server Components (RSC) packages (`react-server-dom-webpack`, `react-server-dom-parcel`, and `react-server-dom-turbopack`) across versions 19.0.0 through 19.3.0. The flaw occurs during the processing of React Flight protocol streams when handling multiple typed arrays sharing a backing ArrayBuffer alongside attacker-controlled inputs. By combining byte-stream buffer detachment semantics with unvalidated thenable invocation and exposed internal response states, an unauthenticated attacker can achieve remote code execution on server runtimes supporting React Server Components.
React Server Components (RSC) enable server-side rendering and streaming of UI component trees and server actions between Node.js or Edge runtimes and client applications. To facilitate this communication, React relies on an internal wire serialization format known as the React Flight protocol. This protocol is implemented across bundler-specific packages: react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack.
The Flight protocol supports streaming complex JavaScript data structures—including promises, server function references (Server Actions), FormData, binary streams, and typed arrays (Uint8Array, Int32Array, etc.)—across HTTP boundaries. During both server-to-client rendering and client-to-server action replies (ReactFlightClient and ReactFlightReplyServer), incoming payloads arrive as segmented chunks identified by prefix codes and IDs. These chunks are lazily parsed and resolved using internal promise structures (ReactPromise and ResolvedModelChunk).
CVE-2026-126187 (classified under CWE-502: Deserialization of Untrusted Data) is an unauthenticated remote code execution vulnerability within the Flight deserialization and stream transport layers. The flaw triggers under specific conditions where a Flight response or reply transports multiple typed arrays alongside an attacker-controlled value. When exploited, the combination of improper ArrayBuffer detachment handling during stream enqueueing, unvalidated thenable execution in server reference binding, and exposed internal parser state allows an attacker to hijack module and function resolution.
> [!NOTE] > Client-only React Single Page Applications (SPAs) that execute purely in the browser without React Server Components or Server Actions do not expose this attack surface and are unaffected.
The root cause of CVE-2026-126187 is a multi-part failure involving native Web Streams buffer detachment semantics, unsafe promise resolution on untrusted models, and inadequate encapsulation of internal Flight parser state. Each weakness interacts during the deserialization of complex multi-chunk payloads to permit arbitrary function invocation.
The primary stream corruption mechanism originates in how React transports typed arrays (Uint8Array, Float64Array, etc.) over byte streams in ReactServerStreamConfigNode.js and ReactServerStreamConfigEdge.js. When a typed array chunk exceeds VIEW_SIZE (2048 or 4096 bytes), React writes the view directly to the underlying ReadableByteStreamController. In standard Web Streams implementations, calling controller.enqueue(view) transfers ownership of the underlying ArrayBuffer and detaches it, immediately reducing its byteLength to zero. If a Flight payload transports multiple typed array views that reference slices of the same backing ArrayBuffer, the first write detaches the shared buffer. Subsequent writes of sibling views reference a detached buffer, writing zero-length or corrupted chunks into the stream and desynchronizing the Flight parser state.
Once stream alignment or chunk resolution is manipulated, three flaws in ReactFlightClient.js and ReactFlightReplyServer.js enable control flow hijacking:
loadServerReference(), the parser processes bound arguments from metaData.bound. Previously, if metaData.bound was present, React passed it directly to Promise.resolve(metaData.bound) or Promise.all([promise, metaData.bound]). Because JavaScript promises automatically invoke the .then() method on any object possessing a then property (a "thenable"), an attacker supplying a crafted object in metaData.bound or defining a model with a then property in createModel() could trigger arbitrary function execution during promise resolution.metaData.bound, the parser extracted the resolved value and applied it directly to the target function via resolvedValue.bind.apply(resolvedValue, boundArgs) without verifying that boundArgs was a genuine Array instance.Response Context: In ReactPromise / ResolvedModelChunk structures, the internal Flight Response controller (which holds module bundler loaders, prefix configurations, and chunk registries) was stored directly on the chunk's public reason property (resolvedChunk.reason = response). During cyclic or corrupted promise resolution, an attacker could traverse properties on the chunk to access the internal Response instance and manipulate module loading metadata.The remediation across commits 61aa00df822d0eb76d0362eb8b6fdd3527693efe and 0c9913ae87283f96e868a8f1c20a3389827632d0 addresses both the stream buffer detachment mechanics and the deserialization gadget chains within the Flight client and reply server.
In packages/react-client/src/ReactFlightClient.js (and corresponding reply server implementations), loadServerReference and createModel were updated to prevent foreign thenable execution and validate bound argument types:
// BEFORE (Vulnerable):
function createModel(response: Response, model: any, parentObject: Object, key: string): any {
return model;
}
function loadServerReference<T>(
response: Response,
metaData: {
id: any,
bound: null | Thenable<Array<any>>,
},
parentObject: Object,
key: string,
): (...Array<any>) => Promise<T> {
const serverReference: ServerReference<T> = resolveServerReference(
response._bundlerConfig,
metaData.id,
);
let promise: null | Thenable<any> = preloadModule(serverReference);
if (!promise) {
if (!metaData.bound) {
const resolvedValue = (requireModule(serverReference): any);
registerBoundServerReference(resolvedValue, metaData.id, null, response._encodeFormAction);
return resolvedValue;
} else {
// VULNERABLE: Calls .then() if metaData.bound is a crafted thenable object
promise = Promise.resolve(metaData.bound);
}
} else if (metaData.bound) {
promise = Promise.all([promise, metaData.bound]);
}
// ... later in resolution handler:
if (metaData.bound) {
// VULNERABLE: Assumes metaData.bound.value is an Array with .slice()
const boundArgs: Array<any> = (metaData.bound: any).value.slice(0);
boundArgs.unshift(null); // this
resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);
}
}// AFTER (Patched in 61aa00df822d0eb76d0362eb8b6fdd3527693efe):
function createModel(response: Response, model: any, parentObject: Object, key: string): any {
// Block function models assigned to 'then' to prevent automatic thenable invocation
if (key === 'then' && typeof model === 'function') {
return null;
}
return model;
}
function loadServerReference<T>(
response: Response,
metaData: {
id: any,
bound: null | Thenable<Array<any>>,
},
parentObject: Object,
key: string,
): (...Array<any>) => Promise<T> {
// Disallow loading server references directly onto a 'then' property
if (key === 'then') {
return (null: any);
}
const id = metaData.id;
if (typeof id !== 'string') {
return (null: any);
}
const serverReference: ServerReference<T> = resolveServerReference(
response._bundlerConfig,
id,
);
const bound = metaData.bound;
let promise: null | Thenable<any> = preloadModule(serverReference);
if (!promise) {
// FIXED: Strictly verify that `bound` is an internal ReactPromise instance
if (bound instanceof ReactPromise) {
promise = Promise.resolve(bound);
} else {
const resolvedValue = (requireModule(serverReference): any);
registerBoundServerReference(resolvedValue, id, bound, response._encodeFormAction);
return resolvedValue;
}
} else if (bound instanceof ReactPromise) {
promise = Promise.all([promise, bound]);
}
// ... later in resolution handler:
if (bound) {
const promiseValue = (bound: any).value;
// FIXED: Validate that resolved bound value is an actual Array before slicing
const boundArgs: Array<any> = isArray(promiseValue)
? promiseValue.slice(0)
: [];
boundArgs.unshift(null); // this
resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);
}
}To prevent attackers from accessing the internal Response object via chunk.reason, the patch introduces an unexported Symbol (RESPONSE_SYMBOL) to wrap the reference:
// Patched in ReactFlightClient.js & ReactFlightReplyServer.js:
type RESPONSE_SYMBOL_TYPE = 'RESPONSE_SYMBOL';
const RESPONSE_SYMBOL: RESPONSE_SYMBOL_TYPE = (Symbol(): any);
// When initializing a ResolvedModelChunk:
resolvedChunk.reason = {[RESPONSE_SYMBOL]: response};
// When reading the response internally during chunk resolution:
const response = chunk.reason[RESPONSE_SYMBOL];Because Symbol() creates a unique, unforgeable key that cannot be represented in JSON or serialized Flight text chunks, untrusted models can no longer read or forge the internal Response context.
In ReactServerStreamConfigNode.js, ReactServerStreamConfigEdge.js, and ReactServerStreamConfigBrowser.js, React now checks whether a typed array view spans the entirety of its underlying buffer (isViewBufferSpanning), whether the buffer is resizable, and whether the runtime supports ArrayBuffer.prototype.detached. If a partial view is used, React clones the slice (view.slice()) before enqueueing, and throws an explicit error if a buffer is already detached:
// Patched stream configuration logic:
if (content.byteLength > VIEW_SIZE) {
// Large chunk. Enqueue directly only if safe from sibling detachment.
const isViewBufferSpanning =
content.byteOffset === 0 &&
content.byteLength === content.buffer.byteLength;
if (!isViewBufferSpanning || content.buffer.resizable) {
// Partial view or resizable buffer: clone so detaching doesn't corrupt other views
return view.slice();
}
if (!supportsArrayBufferDetached) {
return view.slice();
}
}
// During chunk write:
if (chunk.byteLength === 0) {
if (supportsArrayBufferDetached && chunk.buffer.detached) {
throw new Error(
'Cannot transport a detached buffer. This usually happens when trying to transport ' +
'multiple buffer-spanning views on the same underlying buffer. Either reuse the exact ' +
'same view by identity or copy the underlying buffer manually via `view.slice()`.'
);
}
}Additionally, in ReactFlightReplyBackingFormData.js, streaming chunks in multipart FormData replies were namespaced with an 'S' prefix (prefix + 'S' + streamId) to prevent chunk ID collisions between streaming models and one-shot form fields.
Exploiting CVE-2026-126187 requires targeting an application that uses React Server Components or Server Actions through a framework or bundler integration (react-server-dom-webpack, react-server-dom-parcel, or react-server-dom-turbopack). No authentication is required if the vulnerable RSC endpoint or Server Action handler is publicly accessible.
According to the official advisory and patch analysis, successful exploitation depends on specific runtime conditions (reflected in the CVSS v4.0 metric Attack Requirements: Present (AT:P)):
ArrayBuffer alongside attacker-controlled values, or process crafted multipart FormData / Flight reply payloads (ReactFlightReplyServer).ArrayBuffer are serialized into a byte stream without cloning, the first stream write detaches the ArrayBuffer. Subsequent writes emit zero-byte chunks, corrupting the expected byte length of the Flight frame and allowing adjacent attacker-controlled data to be interpreted as Flight protocol control rows (such as $K for FormData, $B for blobs, or server reference metadata).metaData) targeting an exported module function in the bundler registry (_bundlerConfig), or plants a model containing a then property pointing to a resolved server function reference.then property or binds attacker-supplied arguments via loadServerReference and Function.prototype.bind.apply. This allows the attacker to invoke arbitrary module exports within the Node.js or Edge server runtime with controlled arguments.At the time of writing, no public proof-of-concept (PoC) exploits or weaponized modules have been published to ExploitDB, PacketStorm, or GitHub.
CVE-2026-126187 carries a CVSS v4.0 base score of 9.5 (Critical) with the 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.
The vector breakdown highlights the severity of the flaw when the prerequisite application conditions are met:
AV:N) & Privileges Required (PR:N): The vulnerability is exploitable remotely over standard HTTP/HTTPS connections without prior authentication or user interaction (UI:N).AT:P): Exploitation is constrained to applications that use React Server Components and encounter specific payload structures transporting multiple typed arrays alongside attacker-controlled input.VC:H/VI:H/VA:H): Successful exploitation grants arbitrary code execution within the context of the Node.js or Edge server process. An attacker gains full read and write access to application memory, environment variables (including API keys, database connection strings, and signing secrets), and the local filesystem.SC:H/SI:H/SA:N): Because SSR and RSC runtimes typically act as privileged backend tiers connecting to internal microservices and databases, compromising the RSC runtime allows lateral movement and data exfiltration across connected downstream infrastructure.Applications that only use client-side React (react and react-dom without react-server-dom-* packages) are completely unaffected.
The React team has released patched versions across all supported 19.x release branches. Organizations using React Server Components or frameworks built on top of react-server-dom-* packages should upgrade immediately.
Ensure that all instances of react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack in your dependency tree (including transitive dependencies managed by meta-frameworks like Next.js or Parcel) are updated to the following versions:
19.0.9 or later19.1.10 or later19.2.9 or later19.3.1 or later# Example upgrade commands for the 19.3.x release line:
npm install react-server-dom-webpack@^19.3.1
# Or if using Turbopack / Parcel:
npm install react-server-dom-turbopack@^19.3.1
npm install react-server-dom-parcel@^19.3.1> [!IMPORTANT]
> If your application uses a framework that bundles or pins react-server-dom-* internally, use npm ls react-server-dom-webpack (or pnpm why / yarn why) to verify that no vulnerable versions (< 19.0.9, < 19.1.10, < 19.2.9, or < 19.3.1) remain in your lockfile. Use package manager overrides or resolutions if necessary.
If immediate package upgrading is not possible:
TypedArray passed across the Flight boundary owns a dedicated ArrayBuffer by explicitly cloning views via typedArray.slice() prior to returning them.ArrayBuffer.prototype.detached natively so that patched React versions can validate buffer state without incurring unnecessary memory copy overhead.The upstream patches comprehensively address both the memory/stream transport layer and the object deserialization layer. By replacing string-keyed internal state with an unforgeable Symbol (RESPONSE_SYMBOL), strictly enforcing bound instanceof ReactPromise before awaiting bound arguments, rejecting then function properties during model creation, and defensively slicing non-spanning TypedArray views, the fix eliminates both the stream corruption trigger and the underlying thenable gadget chains.
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| Product | Affected Versions | Fixed Version |
|---|---|---|
react-server-dom-webpack Meta / React | >= 19.0.0, < 19.0.9 | 19.0.9 |
react-server-dom-webpack Meta / React | >= 19.1.0, < 19.1.10 | 19.1.10 |
react-server-dom-webpack Meta / React | >= 19.2.0, < 19.2.9 | 19.2.9 |
react-server-dom-webpack Meta / React | >= 19.3.0, < 19.3.1 | 19.3.1 |
react-server-dom-parcel Meta / React | >= 19.0.0, < 19.0.9 | 19.0.9 |
react-server-dom-parcel Meta / React | >= 19.1.0, < 19.1.10 | 19.1.10 |
react-server-dom-parcel Meta / React | >= 19.2.0, < 19.2.9 | 19.2.9 |
react-server-dom-parcel Meta / React | >= 19.3.0, < 19.3.1 | 19.3.1 |
react-server-dom-turbopack Meta / React | >= 19.0.0, < 19.0.9 | 19.0.9 |
react-server-dom-turbopack Meta / React | >= 19.1.0, < 19.1.10 | 19.1.10 |
react-server-dom-turbopack Meta / React | >= 19.2.0, < 19.2.9 | 19.2.9 |
react-server-dom-turbopack Meta / React | >= 19.3.0, < 19.3.1 | 19.3.1 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-126187 (GHSA-xqc2-xf4c-x3h3) |
| Weakness Class | CWE-502: Deserialization of Untrusted Data |
| CVSS v4.0 Score | 9.5 Critical (AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:N) |
| Attack Vector | Network (Unauthenticated HTTP Flight / RSC Payloads) |
| Impact | Unauthenticated Remote Code Execution (RCE) |
| Affected Packages | react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack |
| Patched Versions | 19.0.9, 19.1.10, 19.2.9, 19.3.1 |
| CISA KEV Status | Not Listed |
The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid, allowing attackers to manipulate object structures, invoke arbitrary methods, or trigger unexpected control flow during object reconstruction.
An improper output neutralization vulnerability (CWE-117 / CWE-150) exists in compose-lint version 0.31.0. When compose-lint is invoked on untrusted file paths without a preceding '--' option delimiter, Python's standard argparse module rejects malformed or dash-prefixed paths and writes them directly to standard error without passing through the application's output sanitizer. An attacker submitting a pull request containing crafted directory or file names with carriage returns or Unicode whitespace can inject arbitrary CI workflow commands into GitHub Actions or Azure Pipelines job logs.
CVE-2026-125116 (GHSA-wr5m-5jph-8m96) is a type confusion vulnerability (CWE-843) in Mongoose, the MongoDB object modeling library for Node.js. When resolving discriminator models or schemas via user-supplied discriminator keys (defaulting to `__t`), Mongoose performs direct property indexing on plain JavaScript registry objects without verifying own-property ownership. Supplying inherited `Object.prototype` property names such as `'constructor'` causes Mongoose to resolve built-in functions rather than valid Mongoose constructors, resulting in unhandled runtime exceptions, schema validation bypasses, and persistent collection query denial of service.
A flaw in Mongoose's query condition casting pipeline causes the `sanitizeFilter` configuration option to be silently ignored when defined at the connection level via `mongoose.connect()` or `mongoose.createConnection()`. Because internal query methods inspect `Connection.options` rather than `Connection.config`, applications relying on connection-scoped filter sanitization remain vulnerable to NoSQL query selector injection (CWE-943), permitting authentication bypass and unauthorized data access across Mongoose versions 6.x through 9.x.
CVE-2026-125110 is a path canonicalization mismatch vulnerability in Rack (specifically within the Rack::Static middleware component). When serving static files, Rack normalizes the requested URL path to resolve the physical file on disk, but evaluates configured path-specific header rules against an unnormalized, raw path that has only undergone URL percent-decoding. Unauthenticated remote attackers can exploit this discrepancy by injecting raw or percent-encoded dot-segments into HTTP requests, causing Rack to serve restricted static assets without their intended security headers.
CVE-2026-118776 (also tracked as GHSA-2vgq-wchc-25w7) is an improper input and integrity check validation vulnerability affecting the node-forge cryptographic library prior to version 1.4.1. In vulnerable versions, the PKCS#7 unpadding implementations for Cipher Block Chaining (CBC) and Electronic Codebook (ECB) modes only examined the final byte of the decrypted buffer and permitted padding counts up to four times the cipher block size. This defect allowed malformed ciphertexts containing zero-length padding, oversized padding values, or mismatched preceding padding bytes to pass validation, resulting in silent plaintext truncation of up to four blocks and integrity verification bypass in applications relying on unpadding success.
CVE-2026-118774 (GHSA-9wg5-mj3p-v49q) is an improper cryptographic signature verification vulnerability (CWE-347) in node-forge versions prior to 1.4.1. When verifying an X.509 subordinate certificate via certificate.verify(child) or verifyCertificateChain(), the library erroneously selects the RSA padding scheme (e.g., PKCS#1 v1.5 vs. RSASSA-PSS) and RSASSA-PSS parameters from the issuer certificate rather than from the child certificate being verified. This enables an attacker to relabel a PKCS#1 v1.5-signed certificate as an RSASSA-PSS certificate without invalidating the signature check, bypassing downstream security policies that mandate RSASSA-PSS.