Evil Eval的替代方案 - 关系运算符

时间:2021-06-03 22:33:08

As a form of input validation I need to coerce a string like '9>6' to evaluate to a boolean value.

作为输入验证的一种形式,我需要强制一个像'9> 6'这样的字符串来评估一个布尔值。

Apart from evaluating the string I can't seem to find a workaround .

除了评估字符串,我似乎无法找到解决方法。

I've always heard about the evilness of eval (especially since I'm validating a form input), about the fact that it could evaluate any script and performance issues.

我一直都听说过eval的邪恶(特别是因为我正在验证表单输入),关于它可以评估任何脚本和性能问题的事实。

but....

Are there any alternative in my case (dealing with relational operators)?

在我的情况下是否有任何替代方案(处理关系运算符)?

var arr = ['<9', '>2'];

var check = function (a) {

    return arr.every(function (x) {
            var string = '';

            string += a + x;

            try {
                return eval(string);
            } catch (e) {
                return false;
            }
        });
    };

console.log(check('3'))

3 个解决方案

#1


2  

I can see three things to improve:

我可以看到三件事需要改进:

  • Instead of stringifying the a value and concatenating it to your expression, you should just refer to the a variable directly so that you can compare to the true form of the value. Otherwise you might get odd behaviour.

    您应该直接引用一个变量,以便可以与值的真实形式进行比较,而不是将值与字符串进行字符串化并将其连接到表达式。否则你可能会有奇怪的行为。

    return eval("a " + x);
    
  • Since you are probably using check multiple times, you should not call eval every time (or even multiple times per call). You can compose the strings into one large && expression, and even better you can compile the conditions to single functions beforehand so that you don't have to call eval from within check at all.

    由于您可能多次使用检查,因此不应每次调用eval(甚至每次调用多次)。您可以将字符串组合成一个大的&&表达式,甚至可以更好地将条件编译为单个函数,这样您就不必在check中调用eval。

  • You should consider using the Function constructor instead of eval.

    您应该考虑使用Function构造函数而不是eval。

With those, your code might look like this:

有了这些,您的代码可能如下所示:

var arr = ['<9', '>2'];

var check = new Function("a", "return "+arr.map(function(x) {
    return "a"+x;
}).join(" && ")+";");
console.log(check.toString()); // function anonymous(a) { return a<9 && a>2; }
console.log(check('3'));

or this:

var arr = ['<9', '>2'];
var fns = arr.map(function(x) {
    return new Function("a", "return a "+x+";");
});
function check(a) {
    return fns.every(function(f) {
        return f(a);
    });
}
console.log(check('3'));

Of course I am expecting that arr is not controlled by the user (or worse, another than the current user); if you expect your users to input those conditions you should make sure to whitelist them explicitly, or maybe use a more sophisticated expression grammar/parser/evaluator right away.
Unless that is the case, eval (or for that matter, Function) is not exactly evil, it just does shorten (and possible simplify) your code. You could've written out full-blown functions (like Nit and peterh suggested) just as well.

当然我期望arr不受用户控制(或者更糟糕的是,不是当前用户);如果您希望用户输入这些条件,您应该确保明确地将它们列入白名单,或者可以立即使用更复杂的表达式语法/解析器/评估程序。除非是这种情况,否则eval(或者就此而言,功能)并不完全是邪恶的,它只会缩短(并可能简化)您的代码。你也可以写出完整的功能(如Nit和peterh建议的那样)。

#2


3  

I wouldn't say eval is inherently evil, I only say that it has its advantages and disadvantages. In my practice I experienced very few cases where its advantages were numerous.

我不会说eval本质上是邪恶的,我只说它有其优点和缺点。在我的实践中,我遇到的情况很少,其优势很多。

The two main problems with the eval:

eval的两个主要问题:

  1. It needs runtime interpretation, i.e. string processing and interpreting from the javascript engine. It is slow. Normal javascript runs in a tokenized form.
  2. 它需要运行时解释,即从javascript引擎进行字符串处理和解释。这很慢。普通javascript以标记化形式运行。

  3. If you use eval, you also start a game where you want to block inserting any harmful code, without decreseased functionality, and the hackers trying to find a way through your blocks. And your only possibility to know you didn't lose that your site weren't cracked until now. You don't have any possibility to know that you won.
  4. 如果您使用eval,您还可以启动一个游戏,您希望阻止插入任何有害代码,而不会降低功能,并且黑客会尝试通过您的块找到方法。你唯一知道你的可能性并没有失去你的网站直到现在才被破解。你没有任何可能知道你赢了。

But: eval runs mainly on the browser side, or processes user input data (created by the same user), or server data which was generated by your server-side.

但是:eval主要在浏览器端运行,或处理用户输入数据(由同一用户创建)或服务器端生成的服务器数据。

The solution in your case is obvious: use hard OO things, for example functors.

在你的情况下解决方案是显而易见的:使用硬OO的东西,例如仿函数。

function comparator(lighter, num) {
  return function(x) {
    return lighter ? (x<num) : (x>num);
  }
}

var arr = [ comparator(true, '9'), comparator(false, '2') ];

var check = function(a) {
  return arr.every(function(comp) { return comp(a); });
}

console.log(check('3'));

On the first spot it is much more complex as your version, but it is only because you are accustomed to eval and aren't to tricky functor solutions. Actually, the complexity of the solutions are imho similar.

在第一个位置它会比你的版本复杂得多,但这只是因为你已经习惯了eval而不是那些棘手的仿函数解决方案。实际上,解决方案的复杂性是非常相似的。

#3


2  

One option would be to define helper functions, but how useful this approach would be depends largely on what you want to achieve in the larger picture.

一种选择是定义辅助函数,但这种方法的用处在很大程度上取决于您希望在更大的图片中实现的目标。

function geq(n) {
    return function(m) {
        return m >= n;
    }
}

function leq(n) {
    return function(m) {
        return m <= n;
    }
}

var arr = [leq(9), geq(2)];

function check(n) {
    arr.forEach(function(checkAgainst) {
        console.log(checkAgainst(n));
    });
}

#1


2  

I can see three things to improve:

我可以看到三件事需要改进:

  • Instead of stringifying the a value and concatenating it to your expression, you should just refer to the a variable directly so that you can compare to the true form of the value. Otherwise you might get odd behaviour.

    您应该直接引用一个变量,以便可以与值的真实形式进行比较,而不是将值与字符串进行字符串化并将其连接到表达式。否则你可能会有奇怪的行为。

    return eval("a " + x);
    
  • Since you are probably using check multiple times, you should not call eval every time (or even multiple times per call). You can compose the strings into one large && expression, and even better you can compile the conditions to single functions beforehand so that you don't have to call eval from within check at all.

    由于您可能多次使用检查,因此不应每次调用eval(甚至每次调用多次)。您可以将字符串组合成一个大的&&表达式,甚至可以更好地将条件编译为单个函数,这样您就不必在check中调用eval。

  • You should consider using the Function constructor instead of eval.

    您应该考虑使用Function构造函数而不是eval。

With those, your code might look like this:

有了这些,您的代码可能如下所示:

var arr = ['<9', '>2'];

var check = new Function("a", "return "+arr.map(function(x) {
    return "a"+x;
}).join(" && ")+";");
console.log(check.toString()); // function anonymous(a) { return a<9 && a>2; }
console.log(check('3'));

or this:

var arr = ['<9', '>2'];
var fns = arr.map(function(x) {
    return new Function("a", "return a "+x+";");
});
function check(a) {
    return fns.every(function(f) {
        return f(a);
    });
}
console.log(check('3'));

Of course I am expecting that arr is not controlled by the user (or worse, another than the current user); if you expect your users to input those conditions you should make sure to whitelist them explicitly, or maybe use a more sophisticated expression grammar/parser/evaluator right away.
Unless that is the case, eval (or for that matter, Function) is not exactly evil, it just does shorten (and possible simplify) your code. You could've written out full-blown functions (like Nit and peterh suggested) just as well.

当然我期望arr不受用户控制(或者更糟糕的是,不是当前用户);如果您希望用户输入这些条件,您应该确保明确地将它们列入白名单,或者可以立即使用更复杂的表达式语法/解析器/评估程序。除非是这种情况,否则eval(或者就此而言,功能)并不完全是邪恶的,它只会缩短(并可能简化)您的代码。你也可以写出完整的功能(如Nit和peterh建议的那样)。

#2


3  

I wouldn't say eval is inherently evil, I only say that it has its advantages and disadvantages. In my practice I experienced very few cases where its advantages were numerous.

我不会说eval本质上是邪恶的,我只说它有其优点和缺点。在我的实践中,我遇到的情况很少,其优势很多。

The two main problems with the eval:

eval的两个主要问题:

  1. It needs runtime interpretation, i.e. string processing and interpreting from the javascript engine. It is slow. Normal javascript runs in a tokenized form.
  2. 它需要运行时解释,即从javascript引擎进行字符串处理和解释。这很慢。普通javascript以标记化形式运行。

  3. If you use eval, you also start a game where you want to block inserting any harmful code, without decreseased functionality, and the hackers trying to find a way through your blocks. And your only possibility to know you didn't lose that your site weren't cracked until now. You don't have any possibility to know that you won.
  4. 如果您使用eval,您还可以启动一个游戏,您希望阻止插入任何有害代码,而不会降低功能,并且黑客会尝试通过您的块找到方法。你唯一知道你的可能性并没有失去你的网站直到现在才被破解。你没有任何可能知道你赢了。

But: eval runs mainly on the browser side, or processes user input data (created by the same user), or server data which was generated by your server-side.

但是:eval主要在浏览器端运行,或处理用户输入数据(由同一用户创建)或服务器端生成的服务器数据。

The solution in your case is obvious: use hard OO things, for example functors.

在你的情况下解决方案是显而易见的:使用硬OO的东西,例如仿函数。

function comparator(lighter, num) {
  return function(x) {
    return lighter ? (x<num) : (x>num);
  }
}

var arr = [ comparator(true, '9'), comparator(false, '2') ];

var check = function(a) {
  return arr.every(function(comp) { return comp(a); });
}

console.log(check('3'));

On the first spot it is much more complex as your version, but it is only because you are accustomed to eval and aren't to tricky functor solutions. Actually, the complexity of the solutions are imho similar.

在第一个位置它会比你的版本复杂得多,但这只是因为你已经习惯了eval而不是那些棘手的仿函数解决方案。实际上,解决方案的复杂性是非常相似的。

#3


2  

One option would be to define helper functions, but how useful this approach would be depends largely on what you want to achieve in the larger picture.

一种选择是定义辅助函数,但这种方法的用处在很大程度上取决于您希望在更大的图片中实现的目标。

function geq(n) {
    return function(m) {
        return m >= n;
    }
}

function leq(n) {
    return function(m) {
        return m <= n;
    }
}

var arr = [leq(9), geq(2)];

function check(n) {
    arr.forEach(function(checkAgainst) {
        console.log(checkAgainst(n));
    });
}