From 85c1c9acff192306eb83f4b53600b2ed4b329cc1 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Tue, 10 Nov 2020 20:09:58 -0500 Subject: [PATCH] Perform hasOwnProperty check in Relay Flight We simulate JSON.stringify in this loop so we should do a has own check. Otherwise we'll include things like constructor properties. This will actually make things throw less even when it should. --- .../ReactFlightDOMRelayServerHostConfig.js | 16 +++++++++----- .../ReactFlightDOMRelay-test.internal.js | 19 ++++++++++++++++ .../ReactFlightNativeRelayServerHostConfig.js | 16 +++++++++----- .../ReactFlightNativeRelay-test.internal.js | 22 +++++++++++++++++++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index 73bc60d4eb896..3758d309efbba 100644 --- a/packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -90,6 +90,8 @@ export function processErrorChunk( }; } +const hasOwnProperty = Object.prototype.hasOwnProperty; + function convertModelToJSON( request: Request, parent: {+[key: string]: ReactModel} | $ReadOnlyArray, @@ -107,12 +109,14 @@ function convertModelToJSON( } else { const jsonObj: {[key: string]: JSONValue} = {}; for (const nextKey in json) { - jsonObj[nextKey] = convertModelToJSON( - request, - json, - nextKey, - json[nextKey], - ); + if (hasOwnProperty.call(json, nextKey)) { + jsonObj[nextKey] = convertModelToJSON( + request, + json, + nextKey, + json[nextKey], + ); + } } return jsonObj; } diff --git a/packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js b/packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js index 035f5c1abd0b8..0048a4f2b562b 100644 --- a/packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js +++ b/packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js @@ -222,4 +222,23 @@ describe('ReactFlightDOMRelay', () => { const model = readThrough(transport); expect(model).toEqual(14); }); + + it('should warn in DEV if a class instance polyfill is passed to a host component', () => { + function Bar() {} + + function Foo() {} + Foo.prototype = Object.create(Bar.prototype); + // This is enumerable which some polyfills do. + Foo.prototype.constructor = Foo; + Foo.prototype.method = function() {}; + + expect(() => { + const transport = []; + ReactDOMFlightRelayServer.render(, transport); + readThrough(transport); + }).toErrorDev( + 'Only plain objects can be passed to client components from server components. ', + {withoutStack: true}, + ); + }); }); diff --git a/packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 3992b670e1fa0..431f3af3fc482 100644 --- a/packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -90,6 +90,8 @@ export function processErrorChunk( }; } +const hasOwnProperty = Object.prototype.hasOwnProperty; + function convertModelToJSON( request: Request, parent: {+[key: string]: ReactModel} | $ReadOnlyArray, @@ -107,12 +109,14 @@ function convertModelToJSON( } else { const jsonObj: {[key: string]: JSONValue} = {}; for (const nextKey in json) { - jsonObj[nextKey] = convertModelToJSON( - request, - json, - nextKey, - json[nextKey], - ); + if (hasOwnProperty.call(json, nextKey)) { + jsonObj[nextKey] = convertModelToJSON( + request, + json, + nextKey, + json[nextKey], + ); + } } return jsonObj; } diff --git a/packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js b/packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js index 48742e6bc1cbf..9b2e8ee8a3151 100644 --- a/packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js +++ b/packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js @@ -124,4 +124,26 @@ describe('ReactFlightNativeRelay', () => { nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(), ).toMatchSnapshot(); }); + + it('should warn in DEV if a class instance polyfill is passed to a host component', () => { + function Bar() {} + + function Foo() {} + Foo.prototype = Object.create(Bar.prototype); + // This is enumerable which some polyfills do. + Foo.prototype.constructor = Foo; + Foo.prototype.method = function() {}; + + expect(() => { + const transport = []; + ReactNativeFlightRelayServer.render( + , + transport, + ); + readThrough(transport); + }).toErrorDev( + 'Only plain objects can be passed to client components from server components. ', + {withoutStack: true}, + ); + }); });