Assuming that you are checking the user provided operands and operators properly to ensure that they contain the data you want instead of other javascript executable code, you can concatenate the two operands with the operator in between and feed it to eval()
to get it executed.
Now, eval()
is dangerous because it can execute any JavaScript code. The user can feed executable and possibly malicious JavaScript code as the operator and eval()
would evaluate it. Therefore, when you do the concatenation, you should do it after validating that the operand is safe. To stress this point, I'll write one of the most important tenets of computer security in large fonts:
All input is evil until proven otherwise.
Also, note that eval()
calls the JavaScript interpreter to interpret, compile and execute your code. This is slow. While you may not notice any observable performance issue if you are just using eval()
once in a while, you may notice performance issues if you are calling eval()
very frequently, say, on every keyevent.
Considering these drawbacks of eval()
, you might want to go for a neater solution like the one posted by Felix Kling. However, it is also possible to solve this problem using eval()
in a safe manner as shown below:
function compare(a, op, b)
{
// Check that we have two numbers and an operator fed as a string.
if (typeof a != 'number' || typeof b != 'number' || typeof op != 'string')
return
// Make sure that the string doesn't contain any executable code by checking
// it against a whitelist of allowed comparison operators.
if (['<', '>', '<=', '>=', '==', '!='].indexOf(op) == -1)
return
// If we have reached here, we are sure that a and b are two integers and
// op contains a valid comparison operator. It is now safe to concatenate
// them and make a JavaScript executable code.
if (eval(a + op + b))
doSomething();
}
Note that validating the input against a whitelist is almost always a better idea than validating it against a blacklist. See https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet#White_List_Input_Validation for a brief discussion on it.
Here is a demonstration of this solution: http://jsfiddle.net/YrQ4C/ (Code also reproduced below):
function doSomething()
{
alert('done something!')
}
function compare(a, op, b)
{
if (typeof a != 'number' || typeof b != 'number' || typeof op != 'string')
return
if (['<', '>', '<=', '>=', '==', '!='].indexOf(op) == -1)
return
if (eval(a + op + b))
doSomething();
}
// Positive test cases
compare(2, '<', 3)
compare(2, '<=', 3)
// Negative test cases
compare(2, '>', 3)
compare(2, '>=', 3)
// Attack tests
compare('alert(', '"attack!"', ')')
// Edit: Adding a new attack test case given by Jesse
// in the comments below. This function prevents this
// attack successfully because the whitelist validation
// for the second argument would fail.
compare(1, ';console.log("executed code");2==', 2)
Edit: Demo with Jesse's test case included: http://jsfiddle.net/99eP2/