From 6cb4322d65f4c68daa183df24b5f81668cedab14 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Wed, 29 Jul 2026 18:17:33 -0400 Subject: [PATCH] [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. --- .../react-client/src/ReactFlightClient.js | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 14936d48e2b..b23b172b02c 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -98,6 +98,8 @@ import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack' import hasOwnProperty from 'shared/hasOwnProperty'; +import getPrototypeOf from 'shared/getPrototypeOf'; + import {injectInternals} from './ReactFlightClientDevToolsHook'; import {OMITTED_PROP_ERROR} from 'shared/ReactFlightPropertyAccess'; @@ -157,6 +159,9 @@ const HALTED = 'halted'; // DEV-only. Means it never resolves even if connection const __PROTO__ = '__proto__'; +const ObjectPrototype = Object.prototype; +const ArrayPrototype = Array.prototype; + type PendingChunk = { status: 'pending', value: null | Array mixed)>, @@ -2170,7 +2175,18 @@ function getOutlinedModel( } } } - value = value[path[i]]; + const name = path[i]; + if ( + typeof value === 'object' && + value !== null && + (getPrototypeOf(value) === ObjectPrototype || + getPrototypeOf(value) === ArrayPrototype) && + hasOwnProperty.call(value, name) + ) { + value = value[name]; + } else { + throw new Error('Invalid reference.'); + } } while ( @@ -5382,14 +5398,16 @@ function reviveModel( } // Plain object for (const k in value) { - if (k === __PROTO__) { - delete (value as any)[k]; - } else { - const walked = reviveModel(response, (value as any)[k], value, k); - if (walked !== undefined) { - (value as any)[k] = walked; - } else { + if (hasOwnProperty.call(value, k)) { + if (k === __PROTO__) { delete (value as any)[k]; + } else { + const walked = reviveModel(response, (value as any)[k], value, k); + if (walked !== undefined) { + (value as any)[k] = walked; + } else { + delete (value as any)[k]; + } } } }