♻️ refactor the global whitelist recovery logic and fix the unit test (#2291)
This commit is contained in:
parent
c14ae3f083
commit
b5ad5a4e43
|
|
@ -409,34 +409,50 @@ it('native window function calling should always be bound with window', () => {
|
||||||
expect(proxy.nativeWindowFunction()).toBe('success');
|
expect(proxy.nativeWindowFunction()).toBe('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should restore window properties (primitive values) that in whitelisted variables', () => {
|
describe('variables in whitelist', () => {
|
||||||
const original = {
|
it('should restore window properties (primitive values) that in whitelisted variables', () => {
|
||||||
iframeReady: () => {},
|
const original = {
|
||||||
};
|
iframeReady: function t1() {},
|
||||||
window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = original;
|
};
|
||||||
|
window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = original;
|
||||||
|
|
||||||
const sandbox = new ProxySandbox('whitelist-variables');
|
const sandbox = new ProxySandbox('whitelist-variables');
|
||||||
const { proxy } = sandbox;
|
const { proxy } = sandbox;
|
||||||
sandbox.active();
|
sandbox.active();
|
||||||
proxy.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = undefined;
|
proxy.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = undefined;
|
||||||
sandbox.inactive();
|
expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toBe(undefined);
|
||||||
proxy.expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toBe(original);
|
sandbox.inactive();
|
||||||
});
|
expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toBe(original);
|
||||||
|
|
||||||
it('should restore window properties (object descriptors) that in whitelisted variables', () => {
|
|
||||||
const original = {
|
|
||||||
iframeReady: () => {},
|
|
||||||
};
|
|
||||||
Object.defineProperty(window, '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__', {
|
|
||||||
value: original,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const sandbox = new ProxySandbox('whitelist-variables');
|
it('should restore window properties (object descriptors) that in whitelisted variables', () => {
|
||||||
const { proxy } = sandbox;
|
const original = {
|
||||||
sandbox.active();
|
iframeReady: function t2() {},
|
||||||
proxy.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = {};
|
};
|
||||||
sandbox.inactive();
|
Object.defineProperty(window, '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__', {
|
||||||
proxy.expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toBe(original);
|
value: original,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sandbox = new ProxySandbox('whitelist-variables');
|
||||||
|
const { proxy } = sandbox;
|
||||||
|
sandbox.active();
|
||||||
|
proxy.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = {};
|
||||||
|
expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toEqual({});
|
||||||
|
sandbox.inactive();
|
||||||
|
expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toBe(original);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete global context while it is undefined before sandbox start', () => {
|
||||||
|
delete window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
|
||||||
|
const sandbox = new ProxySandbox('whitelist-variables');
|
||||||
|
const { proxy } = sandbox;
|
||||||
|
sandbox.active();
|
||||||
|
proxy.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = {};
|
||||||
|
expect(window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__).toEqual({});
|
||||||
|
sandbox.inactive();
|
||||||
|
expect('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__' in window).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should work with nest sandbox', () => {
|
describe('should work with nest sandbox', () => {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ function uniq(array: Array<string | symbol>) {
|
||||||
const rawObjectDefineProperty = Object.defineProperty;
|
const rawObjectDefineProperty = Object.defineProperty;
|
||||||
|
|
||||||
const variableWhiteListInDev =
|
const variableWhiteListInDev =
|
||||||
process.env.NODE_ENV === 'development' || window.__QIANKUN_DEVELOPMENT__
|
process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development' || window.__QIANKUN_DEVELOPMENT__
|
||||||
? [
|
? [
|
||||||
// for react hot reload
|
// for react hot reload
|
||||||
// see https://github.com/facebook/create-react-app/blob/66bf7dfc43350249e2f09d138a20840dae8a0a4a/packages/react-error-overlay/src/index.js#L180
|
// see https://github.com/facebook/create-react-app/blob/66bf7dfc43350249e2f09d138a20840dae8a0a4a/packages/react-error-overlay/src/index.js#L180
|
||||||
|
|
@ -34,7 +34,7 @@ const variableWhiteListInDev =
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
// who could escape the sandbox
|
// who could escape the sandbox
|
||||||
const variableWhiteList: PropertyKey[] = [
|
const globalVariableWhiteList: string[] = [
|
||||||
// FIXME System.js used a indirect call with eval, which would make it scope escape to global
|
// FIXME System.js used a indirect call with eval, which would make it scope escape to global
|
||||||
// To make System.js works well, we write it back to global window temporary
|
// To make System.js works well, we write it back to global window temporary
|
||||||
// see https://github.com/systemjs/systemjs/blob/457f5b7e8af6bd120a279540477552a07d5de086/src/evaluate.js#L106
|
// see https://github.com/systemjs/systemjs/blob/457f5b7e8af6bd120a279540477552a07d5de086/src/evaluate.js#L106
|
||||||
|
|
@ -132,6 +132,9 @@ export default class ProxySandbox implements SandBox {
|
||||||
sandboxRunning = true;
|
sandboxRunning = true;
|
||||||
latestSetProp: PropertyKey | null = null;
|
latestSetProp: PropertyKey | null = null;
|
||||||
|
|
||||||
|
// the descriptor of global variables in whitelist before it been modified
|
||||||
|
globalWhitelistPrevDescriptor: { [p in typeof globalVariableWhiteList[number]]: PropertyDescriptor | undefined } = {};
|
||||||
|
|
||||||
active() {
|
active() {
|
||||||
if (!this.sandboxRunning) activeSandboxCount++;
|
if (!this.sandboxRunning) activeSandboxCount++;
|
||||||
this.sandboxRunning = true;
|
this.sandboxRunning = true;
|
||||||
|
|
@ -144,11 +147,15 @@ export default class ProxySandbox implements SandBox {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--activeSandboxCount === 0) {
|
if (process.env.NODE_ENV === 'test' || --activeSandboxCount === 0) {
|
||||||
variableWhiteList.forEach((p) => {
|
// reset the global value to the prev value
|
||||||
const descriptor = this.globalWhiteList[p];
|
globalVariableWhiteList.forEach((p) => {
|
||||||
if (descriptor && descriptor.writable) {
|
const descriptor = this.globalWhitelistPrevDescriptor[p];
|
||||||
|
if (descriptor) {
|
||||||
Object.defineProperty(this.globalContext, p, descriptor);
|
Object.defineProperty(this.globalContext, p, descriptor);
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
delete this.globalContext[p];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -157,16 +164,10 @@ export default class ProxySandbox implements SandBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
globalContext: typeof window;
|
globalContext: typeof window;
|
||||||
/** the whitelisted original global variables */
|
|
||||||
globalWhiteList: Record<PropertyKey, PropertyDescriptor | undefined>;
|
|
||||||
|
|
||||||
constructor(name: string, globalContext = window) {
|
constructor(name: string, globalContext = window) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.globalContext = globalContext;
|
this.globalContext = globalContext;
|
||||||
this.globalWhiteList = variableWhiteList.reduce((acc, key) => {
|
|
||||||
acc[key] = Object.getOwnPropertyDescriptor(globalContext, key);
|
|
||||||
return acc;
|
|
||||||
}, {} as ProxySandbox['globalWhiteList']);
|
|
||||||
this.type = SandBoxType.Proxy;
|
this.type = SandBoxType.Proxy;
|
||||||
const { updatedValueSet } = this;
|
const { updatedValueSet } = this;
|
||||||
|
|
||||||
|
|
@ -179,24 +180,18 @@ export default class ProxySandbox implements SandBox {
|
||||||
set: (target: FakeWindow, p: PropertyKey, value: any): boolean => {
|
set: (target: FakeWindow, p: PropertyKey, value: any): boolean => {
|
||||||
if (this.sandboxRunning) {
|
if (this.sandboxRunning) {
|
||||||
this.registerRunningApp(name, proxy);
|
this.registerRunningApp(name, proxy);
|
||||||
// We must kept its description while the property existed in globalContext before
|
// We must keep its description while the property existed in globalContext before
|
||||||
if (!target.hasOwnProperty(p) && globalContext.hasOwnProperty(p)) {
|
if (!target.hasOwnProperty(p) && globalContext.hasOwnProperty(p)) {
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(globalContext, p);
|
const descriptor = Object.getOwnPropertyDescriptor(globalContext, p);
|
||||||
const { writable, configurable, enumerable } = descriptor!;
|
const { writable, configurable, enumerable } = descriptor!;
|
||||||
if (writable) {
|
Object.defineProperty(target, p, { configurable, enumerable, writable, value });
|
||||||
Object.defineProperty(target, p, {
|
|
||||||
configurable,
|
|
||||||
enumerable,
|
|
||||||
writable,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
|
||||||
target[p] = value;
|
target[p] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variableWhiteList.indexOf(p) !== -1) {
|
// sync the property to globalContext
|
||||||
|
if (typeof p === 'string' && globalVariableWhiteList.indexOf(p) !== -1) {
|
||||||
|
this.globalWhitelistPrevDescriptor[p] = Object.getOwnPropertyDescriptor(globalContext, p);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
globalContext[p] = value;
|
globalContext[p] = value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,12 +88,6 @@ export function isCallable(fn: any) {
|
||||||
return callable;
|
return callable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* isPropertyReadonly
|
|
||||||
* @param target
|
|
||||||
* @param p
|
|
||||||
* @returns boolean
|
|
||||||
*/
|
|
||||||
const frozenPropertyCacheMap = new WeakMap<any, Record<PropertyKey, boolean>>();
|
const frozenPropertyCacheMap = new WeakMap<any, Record<PropertyKey, boolean>>();
|
||||||
export function isPropertyFrozen(target: any, p?: PropertyKey): boolean {
|
export function isPropertyFrozen(target: any, p?: PropertyKey): boolean {
|
||||||
if (!target || !p) {
|
if (!target || !p) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user