user1636522
user1636522

Reputation:

Is eval() evil in this case?

Hi :) I'm wondering if eval is secure in this case :

!function(){
    var a = 1;
    !function(){
        var b = 2;
        try { eval.call({}, 'a'); } 
        catch (e) { console.log(e.message); }
        try { eval.call({}, 'b'); } 
        catch (e) { console.log(e.message); }
    }();
}();

Both calls fail. Does this mean that eval has no access to the surrounding scopes? In other words, using eval.call({}, '...') instead of eval('...'), can eval only access the object's members?


Some tests here : https://stackoverflow.com/a/20975485/1636522.

Upvotes: 2

Views: 210

Answers (2)

user1636522
user1636522

Reputation:

Additional test

Tries to access variables from the inside of the IIFE.

var a = 1;
!function(){
    var b = 2;
    try { eval.call(null, 'console.log(a)'); } 
    catch (e) { console.log(e.message); }
    try { eval.call(null, 'console.log(b)'); } 
    catch (e) { console.log(e.message); }
    try { eval.call({ c: 3 }, 'console.log(c)'); } 
    catch (e) { console.log(e.message); }
}();

Chrome :

1
b is not defined
c is not defined 

Cross browser test

Tries to access variables from the outside of the IIFE. Considering Pointy's answer, all variables except a should be accessible.

!function () {
    var a = 1;
    eval.call(window, [
        'b = 2;',
        'c = function () { return 3; };',
        'd = function d() { return 4; };',
        'function e() { return 5; };',
        'x = function () { return a; };'
    ].join(' '));
}();
var l = function (a) { console.log(a); };
try { l('window.a -> ' + window.a); } catch(e) { l(e.message); };
try { l('window.b -> ' + window.b); } catch(e) { l(e.message); };
try { l('window.c() -> ' + window.c()); } catch(e) { l(e.message); };
try { l('window.d() -> ' + window.d()); } catch(e) { l(e.message); };
try { l('window.e() -> ' + window.e()); } catch(e) { l(e.message); };
try { l('window.x() -> ' + window.x()); } catch(e) { l(e.message); };

Chrome 31, FF 26, IE 8-10 :

window.a -> undefined
window.b -> 2
window.c() -> 3
window.d() -> 4
window.e() -> 5
a is not defined 

IE 7 does not behave as expected :

window.a -> undefined
window.b -> 2
window.c() -> 3
object doesn't support this property or method
function expected
window.x() -> 1

IE 7 and named functions

eval.call(window, 'function f(){return 1}') 
!function () {
    eval.call(window, 'function g(){return 2}');
    console.log(g());
}();
console.log(f());
console.log(g());

It seems that named functions are part of the scope from which eval was called :

2
1
g is null or undefined

Cross browser eval at window level

Actually, "cross browser" means "Chrome 31, FF 26, IE 7-10" :)

function evil(code, success, failure) {
    var head, script;
    script = document.createElement('script');
    script.text = [
        'try{_logevil[0](eval(\'', 
            code.replace(/('|\\)/g, '\\\$1'), 
        '\'))}catch(e){_logevil[1](e);}'
    ].join('');
    window._logevil = [success, failure];
    head = document.getElementsByTagName('head')[0];
    head.appendChild(script);
    head.removeChild(script);
}

Usage examples :

evil(
    'var a = 1; alert(a); a;', 
    function (r) { console.log(r); }, 
    function (e) { console.log(e.message); }
);

// prints "1"

evil(
    'alert(b);',
    function (r) { console.log(r); }, 
    function (e) { console.log(e.message); }
);

// prints "b is not defined"

Upvotes: 1

Pointy
Pointy

Reputation: 413720

If eval is not with a "calling context" — a local value of this basically — then the execution context is the global context. Section 10.4.2 of the spec.

So this should work:

!function(){
    var a = 1;
    function evalWrapper() {
        var b = 2;
        try { eval('a'); console.log("yaay!"); } 
        catch (e) { console.log(e.message); }
    };
    evalWrapper.call({});
}();

The first scenario described in the spec is:

If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,
a. Initialise the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.

Thus, anything you do other than directly call eval( something ) — including, if you read that referenced section, referencing the "eval" function by something other than the name "eval"! — then the code in the string is evaluated in the global context, including the global lexical context. It's therefore as if the code were not inside the surrounding function at all.

Upvotes: 2

Related Questions