我有一个javascript文件读取另一个文件,其中可能包含需要eval()编辑的javascript片段。脚本片段应该符合javascript的严格子集,这限制了它们可以做什么以及它们可以更改哪些变量,但我想知道是否有某种方法可以通过阻止eval在全局范围内查看变量来强制执行此操作。如下所示:
function safeEval( fragment )
{
var localVariable = g_Variable;
{
// do magic scoping here so that the eval fragment can see localVariable
// but not g_Variable or anything else outside function scope
eval( fragment );
}
}
实际代码不需要看起来像这样 - 我可以使用闭包等任何和所有奇怪的技巧。但我确实想知道这是否是可能的。
答案 0 :(得分:52)
简短回答:不会。如果它在全球范围内,它可以用于任何事情。
长答案:如果您eval()
确实希望阅读或弄乱您的执行环境的不受信任的代码,那么您就搞砸了。但是,如果您拥有并信任所有正在执行的代码,包括eval()
编辑的代码,您可以通过覆盖执行上下文来伪造它:
function maskedEval(scr)
{
// set up an object to serve as the context for the code
// being evaluated.
var mask = {};
// mask global properties
for (p in this)
mask[p] = undefined;
// execute script in private context
(new Function( "with(this) { " + scr + "}")).call(mask);
}
同样,我必须强调:
这仅用于屏蔽可信代码以及执行该代码的上下文。如果您不相信代码,请不要
eval()
它(或将其传递给新的Function()
,或以任何其他行为方式使用eval()
。)
答案 1 :(得分:3)
这是一个想法。如果您使用静态分析器(例如,您可以使用esprima构建的东西)来确定eval'd代码使用哪些外部变量,并将它们作为别名。 “外部代码”是指eval'd代码使用但不声明的变量。这是一个例子:
eval(safeEval(
"var x = window.theX;"
+"y = Math.random();"
+"eval('window.z = 500;');"))
其中safeEval返回使用阻止访问外部变量的上下文修改的javascript字符串:
";(function(y, Math, window) {"
+"var x = window.theX;"
+"y = Math.random();"
+"eval(safeEval('window.z = 500;');"
"})();"
现在你可以做几件事:
undefined
作为函数参数,或者不传递参数)。或者,您可以在不安全地访问变量的情况下抛出异常。请注意,使用eval
是一种特殊情况,因为从本质上讲,它实际上不能包含在另一个函数中(这就是为什么我们必须eval(safeEval(...))
)。
当然,完成所有这些工作可能会减慢您的代码速度,但肯定有一些地方无法解决问题。希望这有助于某人。如果有人创建概念证明,我很乐意在这里看到它的链接; )
答案 2 :(得分:3)
Shog9♦的答案很棒。但是如果你的代码只是一个表达式,代码将被执行,不会返回任何内容。对于表达式,请使用
function evalInContext(context, js) {
return eval('with(context) { ' + js + ' }');
}
以下是如何使用它:
var obj = {key: true};
evalInContext(obj, 'key ? "YES" : "NO"');
它将返回"YES"
。
如果您不确定要执行的代码是表达式还是语句,可以将它们组合起来:
function evalInContext(context, js) {
var value;
try {
// for expressions
value = eval('with(context) { ' + js + ' }');
} catch (e) {
if (e instanceof SyntaxError) {
try {
// for statements
value = (new Function('with(this) { ' + js + ' }')).call(context);
} catch (e) {}
}
}
return value;
}
答案 3 :(得分:2)
您无法限制评估范围
顺便提一下this post
可能还有其他方法可以完成你想要在宏观方案中完成的事情,但是你不能以任何方式限制eval的范围。您可以在javascript中将某些变量隐藏为伪私有变量,但我不认为这是您的目的。
答案 4 :(得分:2)
与上面with
块方法中的动态函数包装脚本类似,这允许您将伪全局变量添加到要执行的代码中。您可以通过将特定事物添加到上下文中来“隐藏”特定事物。
function evalInContext(source, context) {
source = '(function(' + Object.keys(context).join(', ') + ') {' + source + '})';
var compiled = eval(source);
return compiled.apply(context, values());
// you likely don't need this - use underscore, jQuery, etc
function values() {
var result = [];
for (var property in context)
if (context.hasOwnProperty(property))
result.push(context[property]);
return result;
}
}
有关示例,请参阅http://jsfiddle.net/PRh8t/。请注意,并非所有浏览器都支持Object.keys
。
答案 5 :(得分:1)
有一个名为Google Caja的项目。你可以使用Caja“沙盒”第三方javascript。 https://developers.google.com/caja/
答案 6 :(得分:1)
请勿使用eval
。还有一个替代方法js.js
:JS interpreter written in JS,这样您就可以在已设法设置的任何环境中运行JS程序。以下是项目页面中API的示例:
var jsObjs = JSJS.Init();
var rval = JSJS.EvaluateScript(jsObjs.cx, jsObjs.glob, "1 + 1");
var d = JSJS.ValueToNumber(jsObjs.cx, rval);
window.alert(d); // 2
JSJS.End(jsObjs);
没有什么可怕的,正如你所看到的那样。
答案 7 :(得分:1)
我偶然发现我可以使用代理来限制作用域对象,似乎更容易将变量屏蔽在作用域之外。我不确定这种方法是否有缺点,但到目前为止它对我来说效果很好。
function maskedEval(src,ctx={})
{
ctx = new Proxy(ctx,{
has:()=>true
})
// execute script in private context
let func = (new Function( "with(this) { " + src + "}"));
func.call(ctx);
}
a = 1;
maskedEval("console.log(a)",{console:console});
maskedEval("console.log(a)",{console:console,a:22});
maskedEval("a = 1",{a:22})
console.log(a)
答案 8 :(得分:0)
不要执行您不信任的代码。 Globals始终可以访问。 如果您确实信任该代码,则可以使用其范围内的特定变量执行它,如下所示:
(new Function("a", "b", "alert(a + b);"))(1, 2);
这相当于:
(function (a, b) {
alert(a + b);
})(1, 2);