【问题标题】:Running JS code in limited context在有限的上下文中运行 JS 代码
【发布时间】:2017-05-16 09:24:25
【问题描述】:

我正在尝试在“隔离”上下文中运行 trusted JS 代码。 基本上想出了这个方法:

function limitedEval(src, context) {
    return (function() {
        with(this) {
            return eval(src) 
        }
    }).call(context)
}

这很好用,但是当脚本使用 var 关键字时,它存储在 执行上下文 中,而不是 with 语句中提供的上下文(我理解这是设计使然) .因此,例如,以下代码不起作用:

var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo)', ctx); // error: foo is undefined

我希望能够多次调用limitedEval() 并重用上下文。这可能吗?

【问题讨论】:

  • "Trusted" 和 eval() 从不属于同一个讨论。
  • 你能详细说明吗?
  • 他的意思是 eval 不是一个安全的函数,会引入多个安全问题。你应该研究类似github.com/ternjs/acorn
  • @ScottMarcus 他们属于完全相同的讨论——使用eval 要求执行的代码是可信的。
  • 您可能对this solution^H^H^H^H^H hack感兴趣

标签: javascript


【解决方案1】:

这似乎是一个非常有趣的问题。您的代码的问题是每次执行limitedEval 时都会生成新函数。这意味着您使用var 关键字创建的任何变量都将仅存在于该函数的上下文中。您真正需要的是每个上下文有 1 个函数并重用该函数的上下文。最明显的方法是使用generators。生成器本质上是可以暂停然后重新启动的函数。

// IIFE to store gen_name
var limitedEval = function() {
    // Symbol for generator property so we don't pollute `ctx` namespace
    var gen_name = Symbol();

    return function limitedEval(src, context) {
        if(!(gen_name in context)) {
            // Generator that will run eval and pause til getting the next source code
            var generator = function * () {
                with(this) {
                    while(true) {
                        yield eval( yield );
                    }
                }
            };

            context[gen_name] = generator.call(context);

            // Initially, we need to execute code until reaching the first `yield`
            context[gen_name].next();
        }

        // First, send the source to the `eval`
        context[gen_name].next( src );

        // Second, get the `eval` result
        return context[gen_name].next().value;
    };
}();

现在你可以打电话了

var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo);', ctx);

现在每个limitedEval 调用都将重用它在提供的ctx 对象上找到的任何生成器函数。如果该函数不存在,它将创建它并将其放在ctx 对象上。因为现在每个ctx 只有一个函数,所以在使用var 关键字创建变量时,它将重用函数的上下文。注意:您将无法通过 ctx 对象查找这些变量,因为它们只会存在于函数的上下文中。

我不完全确定您是否可以在不使用生成器的情况下获得相同的结果。

编辑:其他人提出了很好的建议,所以我用Symbol 替换了随机生成的属性,并用with(this) 代替了with(context)

【讨论】:

  • 您应该使用Symbol() 来防止污染上下文命名空间!
  • 我建议使生成器不是 IIFE,而是命名生成器函数,并使用 with(this)yield eval(yield) 避免 eval 中的任何范围变量
  • 额外问题:有没有办法在严格模式下实现这一点? with 关键字受到限制。
  • 我真的不明白这应该如何创建一个有限的上下文。 with 关键字不限制对父范围的访问。它只是首先在上下文中查找名称。您可能想在with 中使用Proxy 对象,如果目标没有该属性,它只会返回undefined
猜你喜欢
  • 2012-06-08
  • 1970-01-01
  • 1970-01-01
  • 2018-02-10
  • 1970-01-01
  • 1970-01-01
  • 2021-09-29
  • 2018-07-13
  • 2015-12-31
相关资源
最近更新 更多