[Flight] Port ReplyServer traversal guards to FlightClient (#37144)
Additional defense-in-depth in case consumers pass untrusted input into Flight Client. Flight Client generally assumes trusted input. We'll reserve these kind of fixes for Flight Client in case the untrusted input leads to catastrophic vulnerabilities e.g. prototype pollutions that can be used for remote code executions.
Sebastian "Sebbie" Silbermann committed
Jul 29, 2026 at 18:17 UTC
6cb4322d65f4c68daa183df24b5f81668cedab14
1 file changed
+26
-8
packages/react-client/src/ReactFlightClient.js
+26
-8
@@ -98,6 +98,8 @@ import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack'
98
99
import hasOwnProperty from 'shared/hasOwnProperty';
100
101
+import getPrototypeOf from 'shared/getPrototypeOf';
102
+
103
import {injectInternals} from './ReactFlightClientDevToolsHook';
104
105
import {OMITTED_PROP_ERROR} from 'shared/ReactFlightPropertyAccess';
@@ -157,6 +159,9 @@ const HALTED = 'halted'; // DEV-only. Means it never resolves even if connection
159
160
const __PROTO__ = '__proto__';
161
162
+const ObjectPrototype = Object.prototype;
163
+const ArrayPrototype = Array.prototype;
164
+
165
type PendingChunk<T> = {
166
status: 'pending',
167
value: null | Array<InitializationReference | (T => mixed)>,
@@ -2170,7 +2175,18 @@ function getOutlinedModel<T>(
2175
}
2176
}
2177
}
2173
- value = value[path[i]];
2178
+ const name = path[i];
2179
+ if (
2180
+ typeof value === 'object' &&
2181
+ value !== null &&
2182
+ (getPrototypeOf(value) === ObjectPrototype ||
2183
+ getPrototypeOf(value) === ArrayPrototype) &&
2184
+ hasOwnProperty.call(value, name)
2185
+ ) {
2186
+ value = value[name];
2187
+ } else {
2188
+ throw new Error('Invalid reference.');
2189
+ }
2190
}
2191
2192
while (
@@ -5382,14 +5398,16 @@ function reviveModel(
5398
}
5399
// Plain object
5400
for (const k in value) {
5385
- if (k === __PROTO__) {
5386
- delete (value as any)[k];
5387
- } else {
5388
- const walked = reviveModel(response, (value as any)[k], value, k);
5389
- if (walked !== undefined) {
5390
- (value as any)[k] = walked;
5391
- } else {
5401
+ if (hasOwnProperty.call(value, k)) {
5402
+ if (k === __PROTO__) {
5403
delete (value as any)[k];
5404
+ } else {
5405
+ const walked = reviveModel(response, (value as any)[k], value, k);
5406
+ if (walked !== undefined) {
5407
+ (value as any)[k] = walked;
5408
+ } else {
5409
+ delete (value as any)[k];
5410
+ }
5411
}
5412
}
5413
}