Deserialization/Thenable handling
Description
The commit adds guards in React Flight server code to ignore keys named 'then' when processing server references and model creation during Flight reply serialization/deserialization. Specifically, loadServerReference returns null if key === 'then', and createModel returns null if the key is 'then' and the value is a function. This mitigates potential deserialization/thenable abuse where a crafted payload containing a 'then' property could be misinterpreted as a promise/thenable, leading to unintended behavior or security risk. The fix prevents treating such keys/functions as valid flight payload data and instead nulls them, reducing exposure to promise-related abuse during Flight data handling.
Commit Details
Author: Sebastian Markbåge
Date: 2025-12-05 00:05 UTC
Message:
[Flight] Never parse "then" functions (#35289)
AFAIK this is not needed to prevent any exploit but we don't really need
this. We allow functions on pretty much any other object anyway but
never on the "then" property since those would be serialized as Promises
by the client anyway.
Triage Assessment
Vulnerability Type: Deserialization/Thenable handling
Confidence: MEDIUM
Reasoning:
The commit adds guards to prevent handling of objects with a then property (specifically when serializing/deserializing flight data). By returning null for then-bearing keys and then functions, it mitigates potential abuse via crafted payloads that could exploit thenables during deserialization/serialization. This is a defensive security measure related to deserialization/thenable handling rather than a pure bug fix or feature.
Verification Assessment
Vulnerability Type: Deserialization/Thenable handling
Confidence: MEDIUM
Affected Versions: 19.2.0 - 19.2.3
Code Diff
diff --git a/packages/react-server/src/ReactFlightReplyServer.js b/packages/react-server/src/ReactFlightReplyServer.js
index 39734dae7ca2..742cae6d2012 100644
--- a/packages/react-server/src/ReactFlightReplyServer.js
+++ b/packages/react-server/src/ReactFlightReplyServer.js
@@ -437,6 +437,11 @@ function loadServerReference<A: Iterable<any>, T>(
if (typeof id !== 'string') {
return (null: any);
}
+ if (key === 'then') {
+ // This should never happen because we always serialize objects with then-functions
+ // as "thenable" which reduces to ReactPromise with no other fields.
+ return (null: any);
+ }
const serverReference: ServerReference<T> =
resolveServerReference<$FlowFixMe>(response._bundlerConfig, id);
// We expect most servers to not really need this because you'd just have all
@@ -976,7 +981,17 @@ function extractIterator(response: Response, model: Array<any>): Iterator<any> {
return model[Symbol.iterator]();
}
-function createModel(response: Response, model: any): any {
+function createModel(
+ response: Response,
+ model: any,
+ parentObject: Object,
+ key: string,
+): any {
+ if (key === 'then' && typeof model === 'function') {
+ // This should never happen because we always serialize objects with then-functions
+ // as "thenable" which reduces to ReactPromise with no other fields.
+ return null;
+ }
return model;
}