Skip to content

Instantly share code, notes, and snippets.

@westc
Last active April 16, 2019 19:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save westc/c6e423049cbb65ba507274dd0f6e03bd to your computer and use it in GitHub Desktop.
Save westc/c6e423049cbb65ba507274dd0f6e03bd to your computer and use it in GitHub Desktop.
A JavaScript sandbox which works by using a web worker.
(function(undefined) {
var global = this;
var FUNC_PATHS = Object.keys(console).reduce(function (carry, name) {
if ('function' === typeof console[name]) {
carry.push(['console', name]);
}
return carry;
}, [['alert']]);
function WORKER_CODE() {
self.addEventListener("message", (function (isInitialized, undefined) {
return function (event) {
// Run all times except the first time...
var data = event.data;
if (isInitialized) {
var action = data.a;
var code = data.c;
if ('string' === typeof code) {
if (action === 'run') {
importScripts(URL.createObjectURL(new Blob([code], { type: 'text/javascript' })));
}
else if (action === 'eval') {
self.postMessage({ a: 'eval', r: (1,eval)(code), i: data.i });
}
}
else if (action === 'importScripts') {
importScripts.apply(self, data.p);
}
}
// Run on the initial call...
else {
isInitialized = true;
// Since data is an array of function paths that need to be made available
// make sure those paths exist:
data.forEach(function (funcPath) {
var namePath = funcPath.slice();
var funcName = funcPath.pop();
var funcParent = funcPath.reduce(function (funcParent, name) {
return funcParent[name] = funcParent[name] || {};
}, self);
if ('function' !== typeof funcParent[funcName]) {
funcParent[funcName] = function () {
self.postMessage({ a: 'call', n: namePath, p: [].slice.call(arguments) });
};
}
});
}
};
})());
}
global.Sandbox = function Sandbox(opt_initCode) {
this.restart(this._initCode = opt_initCode);
};
Sandbox.prototype = {
restart: function (opt_initCode) {
if (this._worker) {
this.terminate();
}
var sandbox = this;
var worker = new Worker(URL.createObjectURL(new Blob(['(' + WORKER_CODE + ')()'], { type: 'text/javascript' })));
worker.postMessage(FUNC_PATHS);
worker.addEventListener("message", function (event) {
var data = event.data;
var action = data.a;
if (action === "call") {
var funcName = data.n.pop();
var funcParent = data.n.reduce(function (value, name) {
return value[name] = value[name] || {};
}, global);
if (funcParent != undefined && 'function' === typeof funcParent[funcName]) {
funcParent[funcName].apply(funcParent, data.p);
}
}
else if (action === 'eval') {
var f = sandbox._evalCallbacks[data.i];
f && f.call(sandbox, data.r);
}
});
this._worker = worker;
this._evalCallbacks = [];
opt_initCode = opt_initCode == undefined ? this._initCode : opt_initCode;
if (opt_initCode) {
this.run(opt_initCode);
}
},
terminate: function() {
this._worker.terminate();
},
eval: function (code, opt_callback) {
var args = { a: 'eval', c: code };
if ('function' === typeof opt_callback) {
args.i = this._evalCallbacks.push(opt_callback) - 1;
}
this._worker.postMessage(args);
},
run: function (code) {
this._worker.postMessage({ a: 'run', c: code });
},
importScripts: function() {
this._worker.postMessage({ a: 'importScripts', p: [].slice.call(arguments) });
}
};
})();
@westc
Copy link
Author

westc commented Mar 5, 2019

Example of how to use this:

var sb = new Sandbox();
sb.run('const s = 3;');
sb.run('console.log(s);');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment