Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make sandbox around Function() in Javascript

Can I limit the access of a string-generated function (using the Function constructor) to the parent/global scopes?

For example: the following code, as it is, prints false, because the function is storing/modifying the variable a in window.

window.a = 4;
Function("a=3;")()
console.log(a === 4);

Could I restrict the access to window/parent scope and make it print out "true"?

like image 874
FRD Avatar asked Aug 08 '12 17:08

FRD


4 Answers

Here is an additional idea which could be quite powerful together with Esailija's proposal (see the comments on his answer for the discussion).

You could create dummy iframe and use its Function function. The function created with that will only have access to the scope of the iframe by default, though it could still break out of it. Fortunately it is easy to prevent that, by the way Esailija suggested.

I could imagine the function to be like this:

function sandboxed(code) {
    var frame = document.createElement('iframe');
    document.body.appendChild(frame);

    var F = frame.contentWindow.Function,
        args = Object.keys(frame.contentWindow).join();

    document.body.removeChild(frame);

    return F(args, code)();
}

DEMO

Optionally you might want to prepend 'use strict'; to the code.


This works at least in Chrome. Whether the function created this way has access to the iframe's global scope or the page's global scope can be easily tested with:

(function() {
    var frame = document.createElement('iframe');
    document.body.appendChild(frame);
    var same = window === frame.contentWindow.Function('return window;')();
    alert(same ? ':(' : ':)');
    document.body.removeChild(frame);
}());
like image 156
Felix Kling Avatar answered Nov 09 '22 08:11

Felix Kling


Here's my adapted sandboxed function. It takes code and an optional dictionary with arguments to the function.

function sandboxed(code, args = {}) {
    var frame = document.createElement('iframe');
    document.body.appendChild(frame);
    var F = frame.contentWindow.Function;
    document.body.removeChild(frame);
    return F(...Object.keys(args), "'use strict';" + code)(...Object.values(args));
}

console.log(sandboxed('return a - 10;', {'a': 34}));

// 24
like image 21
rop Avatar answered Nov 09 '22 08:11

rop


I don't think so. You could name the globals you want to protect in the parameters so that they shadow them:

window.a = 4;
Function("a", "a=3;")()
console.log(a === 4);

But the function is going to have access to global no matter what you try... that's why it's called global.

Depending on what you are trying to do, there are other work-arounds such as web workers... and as always, hidden iframe hacks.

like image 6
Esailija Avatar answered Nov 09 '22 07:11

Esailija


@Esailija's answer is right. Additionally, I would recommend limiting the number of global variables that you have to protect in the first place. Put anything that you would normally put in the global namespace in an APP scope that you control:

var APP = (function() {
    return {
        a: 4
    };
}());

There's no way to completely limit access to the global scope, but at least this way you only need to protect one object: APP.

like image 2
FishBasketGordo Avatar answered Nov 09 '22 07:11

FishBasketGordo