Skip to content

Commit b5f5a66

Browse files
committed
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.
1 parent 16e6dad commit b5f5a66

File tree

4 files changed

+69
-12
lines changed

4 files changed

+69
-12
lines changed

packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export function processErrorChunk(
9090
};
9191
}
9292

93+
const hasOwnProperty = Object.prototype.hasOwnProperty;
94+
9395
function convertModelToJSON(
9496
request: Request,
9597
parent: {+[key: string]: ReactModel} | $ReadOnlyArray<ReactModel>,
@@ -107,12 +109,14 @@ function convertModelToJSON(
107109
} else {
108110
const jsonObj: {[key: string]: JSONValue} = {};
109111
for (const nextKey in json) {
110-
jsonObj[nextKey] = convertModelToJSON(
111-
request,
112-
json,
113-
nextKey,
114-
json[nextKey],
115-
);
112+
if (hasOwnProperty.call(json, nextKey)) {
113+
jsonObj[nextKey] = convertModelToJSON(
114+
request,
115+
json,
116+
nextKey,
117+
json[nextKey],
118+
);
119+
}
116120
}
117121
return jsonObj;
118122
}

packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,29 @@ describe('ReactFlightDOMRelay', () => {
222222
const model = readThrough(transport);
223223
expect(model).toEqual(14);
224224
});
225+
226+
it('should warn in DEV if a class instance polyfill is passed to a host component', () => {
227+
function Bar() {
228+
}
229+
230+
function Foo() {
231+
}
232+
Foo.prototype = Object.create(Bar.prototype);
233+
// This is enumerable which some polyfills do.
234+
Foo.prototype.constructor = Foo;
235+
Foo.prototype.method = function() {};
236+
237+
expect(() => {
238+
const transport = [];
239+
ReactDOMFlightRelayServer.render(
240+
<input value={new Foo()} />,
241+
transport,
242+
);
243+
readThrough(transport);
244+
}).toErrorDev(
245+
'Only plain objects can be passed to client components from server components. ',
246+
{withoutStack: true},
247+
);
248+
});
249+
225250
});

packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export function processErrorChunk(
9090
};
9191
}
9292

93+
const hasOwnProperty = Object.prototype.hasOwnProperty;
94+
9395
function convertModelToJSON(
9496
request: Request,
9597
parent: {+[key: string]: ReactModel} | $ReadOnlyArray<ReactModel>,
@@ -107,12 +109,14 @@ function convertModelToJSON(
107109
} else {
108110
const jsonObj: {[key: string]: JSONValue} = {};
109111
for (const nextKey in json) {
110-
jsonObj[nextKey] = convertModelToJSON(
111-
request,
112-
json,
113-
nextKey,
114-
json[nextKey],
115-
);
112+
if (hasOwnProperty.call(json, nextKey)) {
113+
jsonObj[nextKey] = convertModelToJSON(
114+
request,
115+
json,
116+
nextKey,
117+
json[nextKey],
118+
);
119+
}
116120
}
117121
return jsonObj;
118122
}

packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,28 @@ describe('ReactFlightNativeRelay', () => {
124124
nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
125125
).toMatchSnapshot();
126126
});
127+
128+
it('should warn in DEV if a class instance polyfill is passed to a host component', () => {
129+
function Bar() {
130+
}
131+
132+
function Foo() {
133+
}
134+
Foo.prototype = Object.create(Bar.prototype);
135+
// This is enumerable which some polyfills do.
136+
Foo.prototype.constructor = Foo;
137+
Foo.prototype.method = function() {};
138+
139+
expect(() => {
140+
const transport = [];
141+
ReactNativeFlightRelayServer.render(
142+
<input value={new Foo()} />,
143+
transport,
144+
);
145+
readThrough(transport);
146+
}).toErrorDev(
147+
'Only plain objects can be passed to client components from server components. ',
148+
{withoutStack: true},
149+
);
150+
});
127151
});

0 commit comments

Comments
 (0)