Skip to content

Commit

Permalink
feat: release 1.5.0
Browse files Browse the repository at this point in the history
release/1.5.0
  • Loading branch information
imsobear authored Apr 7, 2020
2 parents 5d3dade + 808bd1f commit 423fb54
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 30 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ice/stark",
"version": "1.4.2",
"version": "1.5.0",
"description": "Icestark is a JavaScript library for multiple projects, Ice workbench solution.",
"scripts": {
"build": "rm -rf lib && tsc",
Expand Down Expand Up @@ -38,6 +38,7 @@
"react": ">=15.0.0"
},
"dependencies": {
"@ice/sandbox": "^1.0.0",
"lodash.isequal": "^4.5.0",
"path-to-regexp": "^1.7.0",
"url-parse": "^1.1.9"
Expand Down
39 changes: 39 additions & 0 deletions packages/icestark-sandbox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# @ice/sandbox

> icestark sandbox solution. [icestark docs](https://ice.work/docs/icestark/about).
[![NPM version](https://img.shields.io/npm/v/@ice/sandbox.svg?style=flat)](https://npmjs.org/package/@ice/sandbox) [![Package Quality](https://npm.packagequality.com/shield/@ice%2Fsandbox.svg)](https://packagequality.com/#?package=@ice%2Fsandbox) [![build status](https://img.shields.io/travis/ice-lab/icestark.svg?style=flat-square)](https://travis-ci.org/ice-lab/icestark) [![Test coverage](https://img.shields.io/codecov/c/github/ice-lab/icestark.svg?style=flat-square)](https://codecov.io/gh/ice-lab/icestark) [![NPM downloads](http://img.shields.io/npm/dm/@ice/sandbox.svg?style=flat)](https://npmjs.org/package/@ice/sandbox) [![David deps](https://img.shields.io/david/ice-lab/icestark.svg?style=flat-square)](https://david-dm.org/ice-lab/icestark)

## Installation

```bash
$ npm install @ice/sandbox --save
```

## Usage

```js
import Sandbox from '@ice/sandbox';

const sandbox = new Sandbox();

// execute scripts in sandbox
sandbox.execScriptInSandbox('window.a = 1;console.log(window.a);');

// clear side effects added by sandbox, such as addEventListener, setInterval
sandbox.clear();
```

## Inspiration

`@ice/sandbox` is inspired by [tc39/proposal-realms](https://github.com/tc39/proposal-realms), [realms-shim](https://github.com/Agoric/realms-shim) and [qiankun sandbox](https://github.com/umijs/qiankun).

## Contributors

Feel free to report any questions as an [issue](https://github.com/ice-lab/icestark/issues/new), we'd love to have your helping hand on `icestark`.

If you're interested in `icestark`, see [CONTRIBUTING.md](https://github.com/alibaba/ice/blob/master/.github/CONTRIBUTING.md) for more information to learn how to get started.

## License

[MIT](LICENSE)
41 changes: 41 additions & 0 deletions packages/icestark-sandbox/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@ice/sandbox",
"version": "1.0.0",
"description": "sandbox for execute scripts",
"main": "lib/index.js",
"scripts": {
"build": "rm -rf lib && tsc",
"watch": "tsc -w",
"prepublishOnly": "npm run test && npm run build",
"test": "NODE_ENV=unittest jest",
"coverage": "codecov"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ice-lab/icestark.git"
},
"keywords": [
"sandbox",
"icestark"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/ice-lab/icestark/issues"
},
"homepage": "https://github.com/ice-lab/icestark#readme",
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"devDependencies": {
"jest": "^25.2.7",
"ts-jest": "^25.3.1",
"typescript": "^3.8.3"
},
"jest": {
"coverageDirectory": "./coverage/",
"collectCoverage": true,
"preset": "ts-jest"
}
}
141 changes: 141 additions & 0 deletions packages/icestark-sandbox/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// check window contructor function, like Object Array
function isConstructor(fn) {
const functionStr = fn.toString();
const upperCaseRegex = /^function\s+[A-Z]/;

return (
// generator function and has own prototype properties
(fn.prototype && fn.prototype.constructor === fn && Object.getOwnPropertyNames(fn.prototype).length > 1) ||
// upper case
upperCaseRegex.test(functionStr) ||
// ES6 class, window function do not have this case
functionStr.slice(0, 5) === 'class'
);
}

// get function from original window, such as scrollTo, parseInt
function isWindowFunction(func) {
return func && typeof func === 'function' && !isConstructor(func);
}

export default class Sandbox {
private sandbox: Window;

private eventListeners = {};

private timeoutIds: number[] = [];

private intervalIds: number[] = [];

public sandboxDisabled: boolean;

constructor() {
if (!window.Proxy) {
console.warn('proxy sandbox is not support by current browser');
this.sandboxDisabled = true;
}
this.sandbox = null;
}

createProxySandbox() {
const proxyWindow = Object.create(null) as Window;
const originalWindow = window;
const originalAddEventListener = window.addEventListener;
const originalRemoveEventListener = window.removeEventListener;
const originalSetInerval = window.setInterval;
const originalSetTimeout = window.setTimeout;
// hijack addEventListener
proxyWindow.addEventListener = (eventName, fn, ...rest) => {
const listeners = this.eventListeners[eventName] || [];
listeners.push(fn);
return originalAddEventListener.apply(originalWindow, [eventName, fn, ...rest]);
};
// hijack removeEventListener
proxyWindow.removeEventListener = (eventName, fn, ...rest) => {
const listeners = this.eventListeners[eventName] || [];
if (listeners.includes(fn)) {
listeners.splice(listeners.indexOf(fn), 1);
}
return originalRemoveEventListener.apply(originalWindow, [eventName, fn, ...rest]);
};
// hijack setTimeout
proxyWindow.setTimeout = (...args) => {
const timerId = originalSetTimeout(...args);
this.timeoutIds.push(timerId);
return timerId;
};
// hijack setInterval
proxyWindow.setInterval = (...args) => {
const intervalId = originalSetInerval(...args);
this.intervalIds.push(intervalId);
return intervalId;
};

const sandbox = new Proxy(proxyWindow, {
set(target: Window, p: PropertyKey, value: any): boolean {
// eslint-disable-next-line no-param-reassign
target[p] = value;
return true;
},
get(target: Window, p: PropertyKey): any {
if (p === Symbol.unscopables) {
return undefined;
}
if (['top', 'window', 'self', 'globalThis'].includes(p as string)) {
return sandbox;
}
const targetValue = target[p];
if (targetValue) {
// case of addEventListener, removeEventListener, setTimeout, setInterval setted in sandbox
return targetValue;
} else {
const value = originalWindow[p];
if (isWindowFunction(value)) {
// fix Illegal invocation
return value.bind(originalWindow);
} else {
// case of window.clientWidth、new window.Object()
return value;
}
}
},
has(target: Window, p: PropertyKey): boolean {
return p in target || p in originalWindow;
},
});
this.sandbox = sandbox;
}

execScriptInSandbox(script: string): void {
if (!this.sandboxDisabled) {
// create sandbox before exec script
if (!this.sandbox) {
this.createProxySandbox();
}
try {
const execScript = `with (sandbox) {;${script}\n}`;
// eslint-disable-next-line no-new-func
const code = new Function('sandbox', execScript);
// run code with sandbox
code(this.sandbox);
} catch (error) {
console.error(`error occurs when execute script in sandbox: ${error}`);
throw error;
}
}
}

clear() {
if (!this.sandboxDisabled) {
// remove event listeners
Object.keys(this.eventListeners).forEach((eventName) => {
(this.eventListeners[eventName] || []).forEach(listener => {
window.removeEventListener(eventName, listener);
});
});
// clear timeout
this.timeoutIds.forEach(id => window.clearTimeout(id));
this.intervalIds.forEach(id => window.clearInterval(id));
}
}
}
22 changes: 22 additions & 0 deletions packages/icestark-sandbox/tests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Sandbox from '../src/index';

describe('sandbox', () => {
const sandbox = new Sandbox();
const delay = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));

test('execute script in sandbox', () => {
sandbox.execScriptInSandbox('window.a = 1;expect(window.a).toBe(1);');
expect((window as any).a).toBe(undefined);
});

test('capture global event', async () => {
sandbox.execScriptInSandbox(`
setInterval(() => {expect(1).toBe(2)}, 100);
setTimeout(() => { expect(1).toBe(2)}, 100)`
);
sandbox.clear();
// delay 1000 ms for timeout
await delay(1000);
expect(true).toBe(true);
});
});
14 changes: 14 additions & 0 deletions packages/icestark-sandbox/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"jsx": "react",
"experimentalDecorators": true,
"declaration": true,
"sourceMap": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "lib"
},
"include": ["src/*"],
"exclude": ["node_modules"]
}
Loading

0 comments on commit 423fb54

Please sign in to comment.