[Fast Refresh] Fix crashes caused by rogue Proxies (#20030) (#20039)
Kai Riemann committed
Oct 17, 2020 at 18:47 UTC
dfb6a403357d5ac2c0ea87aa6e5a4f947de342f8
2 files changed
+33
-2
packages/react-refresh/src/ReactFreshRuntime.js
+12
-2
@@ -178,6 +178,16 @@ function cloneSet<T>(set: Set<T>): Set<T> {
178
return clone;
179
}
180
181
+// This is a safety mechanism to protect against rogue getters and Proxies.
182
+function getProperty(object, property) {
183
+ try {
184
+ return object[property];
185
+ } catch (err) {
186
+ // Intentionally ignore.
187
+ return undefined;
188
+ }
189
+}
190
+
191
export function performReactRefresh(): RefreshUpdate | null {
192
if (!__DEV__) {
193
throw new Error(
@@ -322,7 +332,7 @@ export function register(type: any, id: string): void {
332
333
// Visit inner types because we might not have registered them.
334
if (typeof type === 'object' && type !== null) {
325
- switch (type.$$typeof) {
335
+ switch (getProperty(type, '$$typeof')) {
336
case REACT_FORWARD_REF_TYPE:
337
register(type.render, id + '$render');
338
break;
@@ -676,7 +686,7 @@ export function isLikelyComponentType(type: any): boolean {
686
}
687
case 'object': {
688
if (type != null) {
679
- switch (type.$$typeof) {
689
+ switch (getProperty(type, '$$typeof')) {
690
case REACT_FORWARD_REF_TYPE:
691
case REACT_MEMO_TYPE:
692
// Definitely React components.
packages/react-refresh/src/__tests__/ReactFresh-test.js
+21
@@ -3612,11 +3612,26 @@ describe('ReactFresh', () => {
3612
const useStore = () => {};
3613
expect(ReactFreshRuntime.isLikelyComponentType(useStore)).toBe(false);
3614
expect(ReactFreshRuntime.isLikelyComponentType(useTheme)).toBe(false);
3615
+ const rogueProxy = new Proxy(
3616
+ {},
3617
+ {
3618
+ get(target, property) {
3619
+ throw new Error();
3620
+ },
3621
+ },
3622
+ );
3623
+ expect(ReactFreshRuntime.isLikelyComponentType(rogueProxy)).toBe(false);
3624
3625
// These seem like function components.
3626
const Button = () => {};
3627
expect(ReactFreshRuntime.isLikelyComponentType(Button)).toBe(true);
3628
expect(ReactFreshRuntime.isLikelyComponentType(Widget)).toBe(true);
3629
+ const ProxyButton = new Proxy(Button, {
3630
+ get(target, property) {
3631
+ return target[property];
3632
+ },
3633
+ });
3634
+ expect(ReactFreshRuntime.isLikelyComponentType(ProxyButton)).toBe(true);
3635
const anon = (() => () => {})();
3636
anon.displayName = 'Foo';
3637
expect(ReactFreshRuntime.isLikelyComponentType(anon)).toBe(true);
@@ -3624,8 +3639,14 @@ describe('ReactFresh', () => {
3639
// These seem like class components.
3640
class Btn extends React.Component {}
3641
class PureBtn extends React.PureComponent {}
3642
+ const ProxyBtn = new Proxy(Btn, {
3643
+ get(target, property) {
3644
+ return target[property];
3645
+ },
3646
+ });
3647
expect(ReactFreshRuntime.isLikelyComponentType(Btn)).toBe(true);
3648
expect(ReactFreshRuntime.isLikelyComponentType(PureBtn)).toBe(true);
3649
+ expect(ReactFreshRuntime.isLikelyComponentType(ProxyBtn)).toBe(true);
3650
expect(
3651
ReactFreshRuntime.isLikelyComponentType(
3652
createReactClass({render() {}}),