Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Fast Refresh] Fix crashes caused by rogue Proxies (facebook#20030) (f…
Browse files Browse the repository at this point in the history
kri authored and koto committed Jun 15, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 9a3eb68 commit 2c30d1e
Showing 2 changed files with 33 additions and 2 deletions.
14 changes: 12 additions & 2 deletions packages/react-refresh/src/ReactFreshRuntime.js
Original file line number Diff line number Diff line change
@@ -178,6 +178,16 @@ function cloneSet<T>(set: Set<T>): Set<T> {
return clone;
}

// This is a safety mechanism to protect against rogue getters and Proxies.
function getProperty(object, property) {
try {
return object[property];
} catch (err) {
// Intentionally ignore.
return undefined;
}
}

export function performReactRefresh(): RefreshUpdate | null {
if (!__DEV__) {
throw new Error(
@@ -322,7 +332,7 @@ export function register(type: any, id: string): void {

// Visit inner types because we might not have registered them.
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
switch (getProperty(type, '$$typeof')) {
case REACT_FORWARD_REF_TYPE:
register(type.render, id + '$render');
break;
@@ -676,7 +686,7 @@ export function isLikelyComponentType(type: any): boolean {
}
case 'object': {
if (type != null) {
switch (type.$$typeof) {
switch (getProperty(type, '$$typeof')) {
case REACT_FORWARD_REF_TYPE:
case REACT_MEMO_TYPE:
// Definitely React components.
21 changes: 21 additions & 0 deletions packages/react-refresh/src/__tests__/ReactFresh-test.js
Original file line number Diff line number Diff line change
@@ -3612,20 +3612,41 @@ describe('ReactFresh', () => {
const useStore = () => {};
expect(ReactFreshRuntime.isLikelyComponentType(useStore)).toBe(false);
expect(ReactFreshRuntime.isLikelyComponentType(useTheme)).toBe(false);
const rogueProxy = new Proxy(
{},
{
get(target, property) {
throw new Error();
},
},
);
expect(ReactFreshRuntime.isLikelyComponentType(rogueProxy)).toBe(false);

// These seem like function components.
const Button = () => {};
expect(ReactFreshRuntime.isLikelyComponentType(Button)).toBe(true);
expect(ReactFreshRuntime.isLikelyComponentType(Widget)).toBe(true);
const ProxyButton = new Proxy(Button, {
get(target, property) {
return target[property];
},
});
expect(ReactFreshRuntime.isLikelyComponentType(ProxyButton)).toBe(true);
const anon = (() => () => {})();
anon.displayName = 'Foo';
expect(ReactFreshRuntime.isLikelyComponentType(anon)).toBe(true);

// These seem like class components.
class Btn extends React.Component {}
class PureBtn extends React.PureComponent {}
const ProxyBtn = new Proxy(Btn, {
get(target, property) {
return target[property];
},
});
expect(ReactFreshRuntime.isLikelyComponentType(Btn)).toBe(true);
expect(ReactFreshRuntime.isLikelyComponentType(PureBtn)).toBe(true);
expect(ReactFreshRuntime.isLikelyComponentType(ProxyBtn)).toBe(true);
expect(
ReactFreshRuntime.isLikelyComponentType(
createReactClass({render() {}}),

0 comments on commit 2c30d1e

Please sign in to comment.