hijack dynamic document appending in evalling code (#1052)

This commit is contained in:
Kuitos 2020-11-05 17:03:05 +08:00 committed by GitHub
parent 798f3437b9
commit 3fa008be96
4 changed files with 49 additions and 22 deletions

View File

@ -4,7 +4,7 @@
*/ */
import { isBoundedFunction } from '../../utils'; import { isBoundedFunction } from '../../utils';
import { documentAttachProxyMap } from '../common'; import { getCurrentRunningSandboxProxy } from '../common';
import ProxySandbox from '../proxySandbox'; import ProxySandbox from '../proxySandbox';
beforeAll(() => { beforeAll(() => {
@ -201,26 +201,42 @@ test('hasOwnProperty should always returns same reference', () => {
expect(proxy.testA.hasOwnProperty).toBe(proxy.testB.hasOwnProperty); expect(proxy.testA.hasOwnProperty).toBe(proxy.testB.hasOwnProperty);
}); });
test('document accessing should modify the attachDocProxySymbol value every time', () => { test('document and eval accessing should modify the attachDocProxySymbol value every time', () => {
const proxy1 = new ProxySandbox('doc-access-test1').proxy; const proxy1 = new ProxySandbox('doc-access-test1').proxy;
const proxy2 = new ProxySandbox('doc-access-test2').proxy; const proxy2 = new ProxySandbox('doc-access-test2').proxy;
const proxy3 = new ProxySandbox('eval-access-test1').proxy;
const proxy4 = new ProxySandbox('eval-access-test2').proxy;
const d1 = proxy1.document; const d1 = proxy1.document;
expect(documentAttachProxyMap.get(d1)).toBe(proxy1); expect(getCurrentRunningSandboxProxy()).toBe(proxy1);
const d2 = proxy2.document; const d2 = proxy2.document;
expect(documentAttachProxyMap.get(d2)).toBe(proxy2); expect(getCurrentRunningSandboxProxy()).toBe(proxy2);
expect(d1).toBe(d2); expect(d1).toBe(d2);
expect(d1).toBe(document); expect(d1).toBe(document);
// @ts-ignore
const eval1 = proxy3.eval;
expect(getCurrentRunningSandboxProxy()).toBe(proxy3);
// @ts-ignore
const eval2 = proxy4.eval;
expect(getCurrentRunningSandboxProxy()).toBe(proxy4);
expect(eval1).toBe(eval2);
// eslint-disable-next-line no-eval
expect(eval1).toBe(eval);
}); });
test('document attachDocProxySymbol mark should be remove before next tasl', (done) => { test('document attachDocProxySymbol mark should be remove before next task', (done) => {
const { proxy } = new ProxySandbox('doc-symbol'); const { proxy } = new ProxySandbox('doc-symbol');
// just access
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const d1 = proxy.document; const d1 = proxy.document;
expect(documentAttachProxyMap.get(d1)).toBe(proxy); expect(getCurrentRunningSandboxProxy()).toBe(proxy);
setTimeout(() => { setTimeout(() => {
expect(documentAttachProxyMap.get(d1)).toBeUndefined(); expect(getCurrentRunningSandboxProxy()).toBeNull();
done(); done();
}); });
}); });

View File

@ -5,7 +5,14 @@
import { isBoundedFunction, isCallable, isConstructable } from '../utils'; import { isBoundedFunction, isCallable, isConstructable } from '../utils';
export const documentAttachProxyMap = new WeakMap<HTMLDocument, WindowProxy>(); let currentRunningSandboxProxy: WindowProxy | null;
export function getCurrentRunningSandboxProxy() {
return currentRunningSandboxProxy;
}
export function setCurrentRunningSandboxProxy(proxy: WindowProxy | null) {
currentRunningSandboxProxy = proxy;
}
const functionBoundedValueMap = new WeakMap<Function, Function>(); const functionBoundedValueMap = new WeakMap<Function, Function>();
export function getTargetValue(target: any, value: any): any { export function getTargetValue(target: any, value: any): any {

View File

@ -4,7 +4,7 @@
*/ */
import { Freer } from '../../../interfaces'; import { Freer } from '../../../interfaces';
import { documentAttachProxyMap } from '../../common'; import { getCurrentRunningSandboxProxy } from '../../common';
import { import {
ContainerConfig, ContainerConfig,
isHijackingTag, isHijackingTag,
@ -27,11 +27,9 @@ function patchDocumentCreateElement() {
): HTMLElement { ): HTMLElement {
const element = rawDocumentCreateElement.call(this, tagName, options); const element = rawDocumentCreateElement.call(this, tagName, options);
if (isHijackingTag(tagName)) { if (isHijackingTag(tagName)) {
// 这里使用document来获取比this更加健壮因为之前set的时候是传入的document const currentRunningSandboxProxy = getCurrentRunningSandboxProxy();
// 因为document不一定是原生的document这种情况出现在qiankun本身就在另一个沙箱下运行的情况而那个沙箱可能连document都重写了。 if (currentRunningSandboxProxy) {
const attachProxy = documentAttachProxyMap.get(document); const proxyContainerConfig = proxyAttachContainerConfigMap.get(currentRunningSandboxProxy);
if (attachProxy) {
const proxyContainerConfig = proxyAttachContainerConfigMap.get(attachProxy);
if (proxyContainerConfig) { if (proxyContainerConfig) {
elementAttachContainerConfigMap.set(element, proxyContainerConfig); elementAttachContainerConfigMap.set(element, proxyContainerConfig);
} }

View File

@ -5,7 +5,7 @@
*/ */
import { SandBox, SandBoxType } from '../interfaces'; import { SandBox, SandBoxType } from '../interfaces';
import { nextTick } from '../utils'; import { nextTick } from '../utils';
import { documentAttachProxyMap, getTargetValue } from './common'; import { getTargetValue, setCurrentRunningSandboxProxy } from './common';
/** /**
* fastest(at most time) unique array method * fastest(at most time) unique array method
@ -50,7 +50,6 @@ const unscopables = {
String: true, String: true,
Boolean: true, Boolean: true,
Math: true, Math: true,
eval: true,
Number: true, Number: true,
Symbol: true, Symbol: true,
parseFloat: true, parseFloat: true,
@ -223,13 +222,20 @@ export default class ProxySandbox implements SandBox {
} }
// mark the symbol to document while accessing as document.createElement could know is invoked by which sandbox for dynamic append patcher // mark the symbol to document while accessing as document.createElement could know is invoked by which sandbox for dynamic append patcher
if (p === 'document') { if (p === 'document' || p === 'eval') {
documentAttachProxyMap.set(document, proxy); setCurrentRunningSandboxProxy(proxy);
// FIXME if you have any other good ideas
// remove the mark in next tick, thus we can identify whether it in micro app or not // remove the mark in next tick, thus we can identify whether it in micro app or not
// this approach is just a workaround, it could not cover all the complex scenarios, such as the micro app runs in the same task context with master in som case // this approach is just a workaround, it could not cover all complex cases, such as the micro app runs in the same task context with master in some case
// fixme if you have any other good ideas nextTick(() => setCurrentRunningSandboxProxy(null));
nextTick(() => documentAttachProxyMap.delete(document)); switch (p) {
case 'document':
return document; return document;
case 'eval':
// eslint-disable-next-line no-eval
return eval;
// no default
}
} }
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise