Reputation:
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
Reputation:
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
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
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
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
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