Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there known problems with >= and <= and the eval function in JS?

I am currently writing a JS rules engine which at one point needs to evaluate boolean expressions using the eval() function.

Firstly I construct an equation as such:

var equation = "relation.relatedTrigger.previousValue" + " " + relation.operator +
        " " + "relation.value";

relation.relatedTrigger.previousValue is the value I want to compare.

relation.operator is the operator (either "==", "!=", <=, "<", ">", >=").

relation.value is the value I want to compare with.

I then simply pass this string to the eval function and it returns true or false as such:

return eval(equation);

This works absolutely fine (with words and numbers) or all of the operators except for >= and <=. E.g. When evaluating the equation:

relation.relatedTrigger.previousValue <= 100

It returns true when previousValue = 0,1,10,100 & all negative numbers but false for everything in between.

I would greatly appreciate the help of anyone to either answer my question or to help me find an alternative solution.

Regards,

Augier.

P.S. I don't need a speech on the insecurities of the eval() function. Any value given to relation.relatedTrigger.previousValue is predefined.

edit: Here is the full function:

function evaluateRelation(relation)
{
console.log("Evaluating relation")
var currentValue;

//if multiple values
if(relation.value.indexOf(";") != -1)
{
    var values = relation.value.split(";");
    for (x in values)
    {

        var equation = "relation.relatedTrigger.previousValue" + " " + relation.operator +
        " " + "values[x]";
        currentValue = eval(equation);
        if (currentValue)
            return true;
    }
    return false;
}

//if single value
else
{
    //Evaluate the relation and get boolean
    var equation = "relation.relatedTrigger.previousValue" + " " + relation.operator +
        " " + "relation.value";
        console.log("relation.relatedTrigger.previousValue " + relation.relatedTrigger.previousValue);
    console.log(equation);
    return eval(equation);
}
}

Answer: Provided by KennyTM below. A string comparison doesn't work. Converting to a numerical was needed.

like image 669
Augier Avatar asked Sep 21 '12 09:09

Augier


People also ask

Why is it bad to use eval in JavaScript?

eval() is a dangerous function, which executes the code it's passed with the privileges of the caller. If you run eval() with a string that could be affected by a malicious party, you may end up running malicious code on the user's machine with the permissions of your webpage / extension.

What is wrong with eval ()?

Malicious code : invoking eval can crash a computer. For example: if you use eval server-side and a mischievous user decides to use an infinite loop as their username. Terribly slow : the JavaScript language is designed to use the full gamut of JavaScript types (numbers, functions, objects, etc)… Not just strings!

What is a safe alternative to using eval ()?

An alternative to eval is Function() . Just like eval() , Function() takes some expression as a string for execution, except, rather than outputting the result directly, it returns an anonymous function to you that you can call. `Function() is a faster and more secure alternative to eval().

Is eval deprecated?

The eval server command has been deprecated since MongoDB 3.0 and is definitely not recommendable for performance or security reasons.


1 Answers

You didn't show how relation.relatedTrigger.previousValue is obtained, but I guess the type of this variable is still a string. In this case, the right hand side will be treated as a string instead. A string comparison matches all characteristics you mentioned:

>>> '-213' <= '100'
true
>>> '0' <= '100'
true
>>> '1' <= '100'
true
>>> '2' <= '100'
false
>>> '10' <= '100'
true
>>> '13' <= '100'
false

You need to make sure relation.relatedTrigger.previousValue is a number. One solution is use the unary + operator in the comparison, e.g.

+relation.relatedTrigger.previousValue <= 100

This has nothing to do with eval. The problem is the overly liberal implicit conversion in Javascript.


Edit: By the way, instead of eval, you could use a dictionary of functions instead. This is faster and also safer. See http://jsperf.com/eval-vs-function-map.

var fmap = {
    '>=': function(a, b) { return a >= b; },
    ...
};

fmap[relation.operator](+relation.relatedTrigger.previousValue, 
                        +relation.value);
like image 122
kennytm Avatar answered Oct 05 '22 20:10

kennytm