Skip to content

Commit 018ea6c

Browse files
committed
[Fast Refresh] Fix crashes caused by rogue Proxies (facebook#20030)
1 parent 2eb3181 commit 018ea6c

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

packages/react-refresh/src/ReactFreshRuntime.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,16 @@ function cloneSet<T>(set: Set<T>): Set<T> {
178178
return clone;
179179
}
180180

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+
181191
export function performReactRefresh(): RefreshUpdate | null {
182192
if (!__DEV__) {
183193
throw new Error(
@@ -322,7 +332,7 @@ export function register(type: any, id: string): void {
322332

323333
// Visit inner types because we might not have registered them.
324334
if (typeof type === 'object' && type !== null) {
325-
switch (type.$$typeof) {
335+
switch (getProperty(type, '$$typeof')) {
326336
case REACT_FORWARD_REF_TYPE:
327337
register(type.render, id + '$render');
328338
break;
@@ -676,7 +686,7 @@ export function isLikelyComponentType(type: any): boolean {
676686
}
677687
case 'object': {
678688
if (type != null) {
679-
switch (type.$$typeof) {
689+
switch (getProperty(type, '$$typeof')) {
680690
case REACT_FORWARD_REF_TYPE:
681691
case REACT_MEMO_TYPE:
682692
// Definitely React components.

packages/react-refresh/src/__tests__/ReactFresh-test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3612,20 +3612,38 @@ describe('ReactFresh', () => {
36123612
const useStore = () => {};
36133613
expect(ReactFreshRuntime.isLikelyComponentType(useStore)).toBe(false);
36143614
expect(ReactFreshRuntime.isLikelyComponentType(useTheme)).toBe(false);
3615+
const rogueProxy = new Proxy({}, {
3616+
get(target, property) {
3617+
throw new Error();
3618+
}
3619+
});
3620+
expect(ReactFreshRuntime.isLikelyComponentType(rogueProxy)).toBe(false);
36153621

36163622
// These seem like function components.
36173623
const Button = () => {};
36183624
expect(ReactFreshRuntime.isLikelyComponentType(Button)).toBe(true);
36193625
expect(ReactFreshRuntime.isLikelyComponentType(Widget)).toBe(true);
3626+
const ProxyButton = new Proxy(Button, {
3627+
get(target, property) {
3628+
return target[property];
3629+
}
3630+
});
3631+
expect(ReactFreshRuntime.isLikelyComponentType(ProxyButton)).toBe(true);
36203632
const anon = (() => () => {})();
36213633
anon.displayName = 'Foo';
36223634
expect(ReactFreshRuntime.isLikelyComponentType(anon)).toBe(true);
36233635

36243636
// These seem like class components.
36253637
class Btn extends React.Component {}
36263638
class PureBtn extends React.PureComponent {}
3639+
const ProxyBtn = new Proxy(Btn, {
3640+
get(target, property) {
3641+
return target[property];
3642+
}
3643+
});
36273644
expect(ReactFreshRuntime.isLikelyComponentType(Btn)).toBe(true);
36283645
expect(ReactFreshRuntime.isLikelyComponentType(PureBtn)).toBe(true);
3646+
expect(ReactFreshRuntime.isLikelyComponentType(ProxyBtn)).toBe(true);
36293647
expect(
36303648
ReactFreshRuntime.isLikelyComponentType(
36313649
createReactClass({render() {}}),

0 commit comments

Comments
 (0)