【问题标题】:How do I prevent accidental global name space pollution with javascript eval?如何使用 javascript eval 防止意外的全局名称空间污染?
【发布时间】:2013-07-26 21:42:55
【问题描述】:

我正在用 JavaScript 编写一个简单的 REPL(读取、评估、打印、循环)实现。我能够像这样隔离代码和调用上下文:

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 引用sandbox,如果输入的代码访问或改变this,它会影响sandbox

但是,我注意到,如果估算的代码不小心忘记在变量赋值前加上关键字 var,那么全局命名空间就会被污染。

有没有办法防止这种情况发生?如果是这样(也许是正则表达式?)?有没有更好的方法来解决这个问题?

【问题讨论】:

  • 您能否eval 将代码放在具有单独global 对象的单独框架或窗口中?顺便说一句,在 ECAMScript 中,“上下文”与execution context 相关。特定执行上下文的 this 参数只是“上下文”的一小部分,它引用所有参数和变量、作用域链等。
  • 我计划在 Titanium 项目中使用它。所以框架不可用。但是冻结可能会起作用。必须看看运行时是否支持。
  • 你能在严格模式下运行代码吗?这样分配给未声明的变量将引发错误。但是,您不能使用 with 。用户仍然可以使用window.foo = ... 创建全局变量。
  • @Sukima 我刚刚注意到您说您使用的是 Titanium,它允许您使用 createWindow 命令创建一个新的单独执行上下文。您的应用程序可以有多个执行上下文。新的执行上下文通常是通过打开一个指向其 url 属性中的外部 URL 的新窗口来创建的:Ti.UI.createWindow({url:'yourscriptfile.js'}).open(); 您可以在上下文之间传递消息。请参阅本指南:developer.appcelerator.com/blog/2010/12/…

标签: javascript


【解决方案1】:

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

  • 适用于所有浏览器的最简单的技术可能是创建一个 iframe,并将脚本标签附加到它。 (请注意,如果它们在同一个域中,至少在较旧的浏览器中,一个非常热心的 iframe 仍然可以通过它)。我会在 this question 中讨论这个问题。
  • 使用 web Worker,默认情况下具有隔离环境,无法访问主执行线程的全局对象。我讨论了in this question

更具体地说,如果您正在构建 REPL,请查看我们讨论的 this answer,我将解释如何使用 iframe 的第一种方法在相同范围内但在全局范围外评估代码。

(我假设一个浏览器,在节点中你可以简单地使用 vm 模块并在 runInContext 中选择上下文)

【讨论】:

    【解决方案2】:

    原来有一种方法可以使用"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);
    

    此代码允许 global 和 window 引用沙盒对象而不是全局名称空间。它将变量globalwindow 伪装成沙箱对象,如果输入未使用var,使用"use strict" 将导致它抛出异常。它还将函数包装在with 语句中,以使沙盒对象中定义的方法和属性能够像前面有this. 一样工作。要查看实现示例(带有测试规范),请查看this gist

    感谢大家的想法。希望这个答案对其他人有所帮助。

    【讨论】:

    • 如果code 调用另一个函数并且该函数不在严格模式下怎么办?还是自己执行eval
    • 除了一些令人作呕的正则表达式之外,我不知道如何防止这种情况。
    • 查看我的回答,我提供了两种防止这种情况的方法以及指向 repl 实现的链接。
    • 好奇,var window = {}; 可以放在评估函数的开头吗?
    猜你喜欢
    • 1970-01-01
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 1970-01-01
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    相关资源
    最近更新 更多