Skip to content

Commit

Permalink
Release (#301)
Browse files Browse the repository at this point in the history
* chore: 🤖 version

* fix: 🐛 eval should not be bind (#302)

* fix: 🐛 eval should not be bind

✅ Closes: alibaba/ice#4294

* fix: 🐛 eventlistener should be store in this.eventListeners (#296)

* fix: 🐛 eventlistener should be store in this.eventListeners

✅ Closes: #295

* test: 💍 add more tests

✅ Closes: #295

* refactor: 💡 temporary separate to sandbox (#304)

* docs: ✏️ update changelog
  • Loading branch information
maoxiaoke authored May 20, 2021
1 parent 6163ab1 commit 16b6465
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 10 deletions.
6 changes: 6 additions & 0 deletions packages/icestark-sandbox/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 1.1.2

- [fix] hijacked eventListener were not been removed after sandbox unload. ([#295](https://github.com/ice-lab/icestark/issues/295))
- [fix] never bind `eval` in sandbox. ([#4294](https://github.com/alibaba/ice/issues/4294))
- [refact] misspelling of Sandbox types.

## 1.1.1

- [fix] falsy values except `undefined` would be trapped by proxy window. ([#156](https://github.com/ice-lab/icestark/issues/156))
Expand Down
2 changes: 1 addition & 1 deletion packages/icestark-sandbox/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ice/sandbox",
"version": "1.1.1",
"version": "1.1.3",
"description": "sandbox for execute scripts",
"main": "lib/index.js",
"scripts": {
Expand Down
26 changes: 19 additions & 7 deletions packages/icestark-sandbox/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ export interface SandboxProps {
multiMode?: boolean;
}

export interface SandboxContructor {
export interface SandboxConstructor {
new(): Sandbox;
}

// check window contructor function, like Object Array
// check window constructor function, like Object Array
function isConstructor(fn) {
// generator function and has own prototype properties
const hasConstructor = fn.prototype && fn.prototype.constructor === fn && Object.getOwnPropertyNames(fn.prototype).length > 1;
// unnecessary to call toString if it has contructor function
// unnecessary to call toString if it has constructor function
const functionStr = !hasConstructor && fn.toString();
const upperCaseRegex = /^function\s+[A-Z]/;

Expand Down Expand Up @@ -67,8 +67,9 @@ export default class Sandbox {

// hijack addEventListener
proxyWindow.addEventListener = (eventName, fn, ...rest) => {
const listeners = this.eventListeners[eventName] || [];
listeners.push(fn);
this.eventListeners[eventName] = (this.eventListeners[eventName] || []);
this.eventListeners[eventName].push(fn);

return originalAddEventListener.apply(originalWindow, [eventName, fn, ...rest]);
};
// hijack removeEventListener
Expand Down Expand Up @@ -96,11 +97,11 @@ export default class Sandbox {
set(target: Window, p: PropertyKey, value: any): boolean {
// eslint-disable-next-line no-prototype-builtins
if (!originalWindow.hasOwnProperty(p)) {
// recorde value added in sandbox
// record value added in sandbox
propertyAdded[p] = value;
// eslint-disable-next-line no-prototype-builtins
} else if (!originalValues.hasOwnProperty(p)) {
// if it is already been setted in orignal window, record it's original value
// if it is already been setted in original window, record it's original value
originalValues[p] = originalWindow[p];
}
// set new value to original window in case of jsonp, js bundle which will be execute outof sandbox
Expand Down Expand Up @@ -140,6 +141,17 @@ export default class Sandbox {
}

const value = originalWindow[p];

/**
* use `eval` indirectly if you bind it. And if eval code is not being evaluated by a direct call,
* then initialise the execution context as if it was a global execution context.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
* https://262.ecma-international.org/5.1/#sec-10.4.2
*/
if (p === 'eval') {
return value;
}

if (isWindowFunction(value)) {
// fix Illegal invocation
return value.bind(originalWindow);
Expand Down
62 changes: 60 additions & 2 deletions packages/icestark-sandbox/tests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import '@testing-library/jest-dom/extend-expect';
import Sandbox from '../src/index';

const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

describe('sandbox: excapeSandbox', () => {
const sandbox = new Sandbox({});
const delay = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));
Expand Down Expand Up @@ -35,7 +38,7 @@ describe('sandbox: default props', () => {
});
});

describe('sandbox: access contructor', () => {
describe('sandbox: access constructor', () => {
const sandbox = new Sandbox();

test('execute global functions', () => {
Expand Down Expand Up @@ -72,4 +75,59 @@ describe('sandbox: falsy values should be trapped.', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect((window as any).a).toBe(undefined);
});
});
});

describe('sandbox: eventListener and setTimeout should be trapped', () => {
/**
* for some reason, set `setTimeout: false` to enable communicate with global.
*/
const sandbox = new Sandbox({ multiMode: false });

test('trap eventListener and setTimeout', async () => {
sandbox.execScriptInSandbox(`
window.count = 0;
window.addEventListener('popstate', (event) => {
console.warn('sandbox: onPopState count', count);
count += 1;
});
history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.pushState({page: 3}, "title 3", "?page=3");
history.back();
window.id = setTimeout(() => {
expect(count).toEqual(1);
}, 100)
`);

await delay(1000);
expect((window as any).count).toEqual(1);
sandbox.clear();
history.back();
await delay(1000);
expect((window as any).count).toEqual(undefined);
});
});

describe('eval in sandbox', () => {
const sandbox = new Sandbox({ multiMode: true });

test('execution context is not global execution context', () => {
let error = null;
try {
sandbox.execScriptInSandbox(
`
function bar (value) {
eval('console.log(value);');
}
bar(1);
`
);
} catch (e) {
error = e.message;
}

expect(error).toBe(null);
});
});

0 comments on commit 16b6465

Please sign in to comment.