2013-07-26 45 views
4

我正在用JavaScript编写一个简单的REPL(Read,Evaluate,Print,Loop)实现。我能够隔离代码和调用上下文,像这样:如何通过JavaScript eval来防止意外的全球名称空间污染?

var sandbox = { 
    // Allow the input code to use predefined helper functions 
    // without the preceding use of the this keyword. 
    helper_fn: function() { alert("foo"); } 
}; 

var func = new Function("with (this) { " + user_inputed_code + " }"); 
func.call(sandbox); 

现在关闭user_inputed_code使this参照网址到​​如果输入的代码访问或变异this很影响​​。

但是,我注意到,如果推算代码不小心忘记了使用关键字var的变量赋值,全局名称空间会被污染。

反正有防止这种情况吗?如果是这样(可能是正则表达式?)?有没有更好的方法来解决这个问题?

+0

你能* EVAL *在一个单独的框架或窗口,有一个单独的*全球*对象的代码?顺便提一句,在ECAMScript中,“上下文”用于[* execution context *](http://www.ecma-international.org/ecma-262/5.1/#sec-10.3)。一个特定的执行上下文的* this *参数只是涉及所有参数和变量,作用域链等的“上下文”的一小部分。 – RobG

+0

我打算在Titanium项目中使用它。所以帧不可用。但冻结可能会起作用。将不得不看运行时是否支持。 – Sukima

+0

你可以在严格模式下运行代码吗?这样分配给未声明的变量会引发错误。尽管你不能使用*。用户仍然可以使用'window.foo = ...'创建全局变量。 – RobG

回答

1

原来有一个方法与"use strict"和没有Object.freeze。你必须用自己的沙盒对象来手动替换全局命名空间:

var sandbox, module, func, output; 
// Empty object or with defined methods/properties 
// you want to expose as globals. 
sandbox = {}; 
// A reference to an object you WANT to provide safe 
// access to. In this example it's just an empty object. 
module = {}; 
// A better version of eval: 
func = new Function("module", "with(this){return(function(module,global,window){\"use strict\";return eval(\"" + code + "\");})(module,this,this)}"); 
output = func.call(sandbox, module); 

该代码使得全球和窗口指的是沙盒对象,而不是全局命名空间。它伪装变量globalwindow作为沙箱对象,如果输入未使用var,则使用"use strict"将导致它抛出异常。它还将函数包装在with语句中,以使沙箱对象中定义的方法和属性的工作方式与之前的this.一样。要查看实施示例(使用测试规范),请查看this gist

谢谢大家的意见。希望这个答案有助于他人。

+1

如果'code'调用另一个函数并且该函数未处于严格模式下会怎么样?或者执行'eval'本身? –

+0

除了一些令人作呕的正则表达式,我不知道如何防止这种情况。 – Sukima

+0

看到我的答案,我提供了两种方法来防止这种情况以及链接到repl实现。 –

2

我将提供两种完全不同的方法,与其他人讨论的方法相同。它们都很激烈,并且在你想要相对隔离你的环境时非常有用。

  • 适用于所有浏览器的最简单的方法是创建一个iframe,并向其添加脚本标记。 (请注意,如果它们位于同一个域中,至少在旧版浏览器中,真正过于热情的iframe仍然可以通过该方法)。我在this question讨论。
  • 使用web Workers,默认情况下具有独立的环境,并且不能访问主执行线程的全局对象。我讨论in this question

更具体地说,如果您正在构建一个REPL看看this answer我们讨论,我解释如何EVAL代码在同一个范围,但在全球范围,使用I帧的第一种方法。

(我假定一个浏览器,在节点就可以简单的使用,VM模块,并选择在runInContext上下文)

相关问题