🐛 proxy sandbox should not overwrite read-only fields. (#2123)

This commit is contained in:
xioozq 2022-09-26 20:22:20 +08:00 committed by GitHub
parent 3a365382da
commit 0aaf55a77f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 8 deletions

View File

@ -6,7 +6,6 @@ import './index.less';
* 以下分别是 React Vue 的示例可切换尝试 * 以下分别是 React Vue 的示例可切换尝试
*/ */
import render from './render/ReactRender'; import render from './render/ReactRender';
// import render from './render/VueRender'; // import render from './render/VueRender';
/** /**

View File

@ -3,7 +3,7 @@
* @since 2020-03-31 * @since 2020-03-31
*/ */
import { isBoundedFunction } from '../../utils'; import { isBoundedFunction, isPropertyReadonly } from '../../utils';
import { getCurrentRunningApp } from '../common'; import { getCurrentRunningApp } from '../common';
import ProxySandbox from '../proxySandbox'; import ProxySandbox from '../proxySandbox';
@ -302,6 +302,24 @@ test('bounded function should not be rebounded', () => {
expect(isBoundedFunction(proxy.fn1)).toBeTruthy(); expect(isBoundedFunction(proxy.fn1)).toBeTruthy();
}); });
test('readonly property should not be overwrite', () => {
const proxy = new ProxySandbox('bound-fn-test').proxy as any;
proxy.normalField = 'normalFieldValue';
Object.defineProperties(proxy, {
readOnlyField: {
value: 'readOnlyFieldValue',
configurable: false,
enumerable: true,
writable: false,
},
});
expect(isPropertyReadonly(proxy, 'normalField')).toBeFalsy();
expect(isPropertyReadonly(proxy, 'readOnlyField')).toBeTruthy();
});
test('the prototype should be kept while we create a function with prototype on proxy', () => { test('the prototype should be kept while we create a function with prototype on proxy', () => {
const proxy = new ProxySandbox('new-function').proxy as any; const proxy = new ProxySandbox('new-function').proxy as any;

View File

@ -3,7 +3,7 @@
* @since 2020-04-13 * @since 2020-04-13
*/ */
import { isBoundedFunction, isCallable, isConstructable } from '../utils'; import { isBoundedFunction, isCallable, isConstructable, isPropertyReadonly } from '../utils';
type AppInstance = { name: string; window: WindowProxy }; type AppInstance = { name: string; window: WindowProxy };
let currentRunningApp: AppInstance | null = null; let currentRunningApp: AppInstance | null = null;
@ -21,14 +21,14 @@ export function setCurrentRunningApp(appInstance: { name: string; window: Window
} }
const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>(); const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>();
export function getTargetValue(target: any, value: any, p?: any): any {
export function getTargetValue(target: any, value: any): any {
/* /*
isCallable && !isBoundedFunction && !isConstructable window.consolewindow.atob Illegal invocation isCallable && !isPropertyReadonly && !isBoundedFunction && !isConstructable window.consolewindow.atob Illegal invocation
prototype prototype
@warning edge case lodash.isFunction iframe top window @warning edge case lodash.isFunction iframe top window
@warning configurable及writable都为false的readonly属性proxy必须返回原值
*/ */
if (isCallable(value) && !isBoundedFunction(value) && !isConstructable(value)) { if (isCallable(value) && !isPropertyReadonly(target, p) && !isBoundedFunction(value) && !isConstructable(value)) {
const cachedBoundFunction = functionBoundedValueMap.get(value); const cachedBoundFunction = functionBoundedValueMap.get(value);
if (cachedBoundFunction) { if (cachedBoundFunction) {
return cachedBoundFunction; return cachedBoundFunction;

View File

@ -262,7 +262,7 @@ export default class ProxySandbox implements SandBox {
proxyFetch('https://qiankun.com'); proxyFetch('https://qiankun.com');
*/ */
const boundTarget = useNativeWindowForBindingsProps.get(p) ? nativeGlobal : globalContext; const boundTarget = useNativeWindowForBindingsProps.get(p) ? nativeGlobal : globalContext;
return getTargetValue(boundTarget, value); return getTargetValue(boundTarget, value, p);
}, },
// trap in operator // trap in operator

View File

@ -88,6 +88,37 @@ export const isCallable = (fn: any) => {
return callable; return callable;
}; };
/**
* isPropertyReadonly
* @param target
* @param p
* @returns boolean
*/
const propertyReadonlyCacheMap = new WeakMap<any, Record<PropertyKey, boolean>>();
export function isPropertyReadonly(target: any, p?: PropertyKey): boolean {
// 异常返回
if (!target || !p) {
return false;
}
// 取缓存
const targetPropertiesFromCache = propertyReadonlyCacheMap.get(target) || {};
if (typeof targetPropertiesFromCache[p] === 'boolean') {
return targetPropertiesFromCache[p];
}
// 计算
const propertyDescriptor = Object.getOwnPropertyDescriptor(target, p);
const result = propertyDescriptor?.configurable === false && propertyDescriptor?.writable === false;
// 写缓存
targetPropertiesFromCache[p] = result;
propertyReadonlyCacheMap.set(target, targetPropertiesFromCache);
return result;
}
const boundedMap = new WeakMap<CallableFunction, boolean>(); const boundedMap = new WeakMap<CallableFunction, boolean>();
export function isBoundedFunction(fn: CallableFunction) { export function isBoundedFunction(fn: CallableFunction) {