@samitouri / QOS-React / commits / dfb6a40335

[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() {}}),