I'm trying to sandbox a function in a way to prevent it from doing anything that is considered malicious. Simple example:
const fn = (window, setTimeout) => {
console.log(`window: ${typeof window}`);
console.log(`setTimeout: ${typeof setTimeout}`);
};
fn (undefined, undefined);
// console output:
// window: undefined
// setTimeout: undefined
The function fn
does not have any access to the window
or setTimeout
functions, because they're hidden by the arguments of the same name.
To make this more useful, I'd like to write a higher-order function to do the sandboxing:
const sandbox = (fn) => (...args) => {
return ((window, setTimeout) => {
return fn.call(args);
})();
};
const maliciousFn = (a, b) => {
console.log(`a: ${a}`);
console.log(`b: ${b}`);
console.log(`window: ${typeof window}`);
console.log(`setTimeout: ${typeof setTimeout}`);
return 42;
};
const safeFn = sandbox(maliciousFn);
console.log (`maliciousFn returns: ${maliciousFn(13, 27)}`);
console.log (`safeFn returns: ${safeFn(13, 27)}`);
This doesn't work as the maliciousFn
has already captured the references of window
and setTimeout
, and the additional wrapper can't change that.
Is it possible to do this properly?
Background: The reason behind this is that I'd like to write a sandboxed eval
function
that could evaluate any given script safely without the risk of doing anything malicious.
Of course, I'd have to hide all globals, not just window
and setTimeout
. But once it
works for window
and setTimeout
, I can easily add all the other globals as well.
const safeEval = sandbox(eval);
const result = safeEval(someScriptComingFromAnUnsafeSource);