17

I need to evaluate user-entered arithmetic expressions like "2 * (3 + 4)" in Javascript but I don't want to use eval for security reasons.

I could strip out all the characters that are not numbers or operators but I'm not sure this would be safe anyway and it would be nice if the user could use functions like cos, sqrt, etc...

Are there any Javascript libraries that do arithmetic expression evaluation?

Giuseppe Ottaviano
  • 4,533
  • 2
  • 18
  • 18
  • Unless you roll your own you won't overcome any security issues? – James Feb 21 '11 at 14:05
  • Does this answer your question? [Evaluating a string as a mathematical expression in JavaScript](//stackoverflow.com/q/2276021/90527) – outis Dec 24 '21 at 00:43

5 Answers5

23

You can try JavaScript Expression Evaluator:

This library is a modified version of Raphael Graf’s ActionScript Expression Parser. When I wrote the JavaScript Function Plotter, I wanted a better alternative to using JavaScript’s eval function. There’s no security risk currently, because you can only run code in your own browser, but it’s not as convenient for math (Math.pow(2^x) instead of 2^x, etc.).

Then your code will be like that:

console.info ( Parser.evaluate( "2 * (3 + 4)" ) ); //prints 14

The source code is on GitHub and it's published on npm as expr-eval. Can be used like so:

import { Parser } from 'expr-eval';

console.log(Parser.evaluate("2 * (3 + 4)")); // 14
Alec Mev
  • 4,663
  • 4
  • 30
  • 44
Maxym
  • 11,836
  • 3
  • 44
  • 48
  • 1
    Thanks! That's exactly what I was looking for, and I swear I googled for all the possible variations on "expression evaluation" I could come up with... – Giuseppe Ottaviano Feb 21 '11 at 18:21
11

As already mentioned, the most damage any user could do is pretty much what they could already do using the built-in console in any of the major browsers. However, if you wanted to restrict the user to using Math properties/methods, you could write a simple regex to handle this for you. Something like this should work:

function mathEval (exp) {
    var reg = /(?:[a-z$_][a-z0-9$_]*)|(?:[;={}\[\]"'!&<>^\\?:])/ig,
        valid = true;
       
    // Detect valid JS identifier names and replace them
    exp = exp.replace(reg, function ($0) {
        // If the name is a direct member of Math, allow
        if (Math.hasOwnProperty($0))
            return "Math."+$0;
        // Otherwise the expression is invalid
        else
            valid = false;
    });
    
    // Don't eval if our replace function flagged as invalid
    if (!valid)
        alert("Invalid arithmetic expression");
    else
        try { alert(eval(exp)); } catch (e) { alert("Invalid arithmetic expression"); };
}

I realize you didn't want to use eval for security reasons, but the regex should make it pretty safe as it rules out any words that aren't direct properties of the Math object and most non-math JS operators, including the assignment operator (=) and binary operators. The harder method would be writing a tokenizer to parse the mathematical expression, because it's not a regular language.

Feel free to try and break the working example I wrote, if you can or if you notice a problem, leave a comment and I'll see what I can do to fix it.


Note: Yi Jiang mentioned [in JavaScript chat](https://chat.stackoverflow.com/rooms/17/javascript) that it might also be useful to allow lower case for things like `Math.PI`. If that's the case, you could just add the following `else if` statement in the replacement function:
else if (Math.hasOwnProperty($0.toUpperCase())
    return "Math."+$0.toUpperCase();

Add it between the if and else statement (example).

Community
  • 1
  • 1
Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 5
    "The most damage any user could do is pretty much what they could already do using the built-in console in any of the major browsers" is based on false premises. Strings come from outside the browser via servers and URLs. The best policy is to code assuming that inputs to your function will come from places you don't expect as other devs add to your application, which means not using excessively powerful operators like `eval`. – Mike Samuel Apr 28 '13 at 13:07
  • 1
    @Mike: I'm hard pressed to agree 100%. Whilst what you said is true when evaluating input from other sources, I stand by my answer for when you're only evaluating user input. – Andy E Apr 28 '13 at 13:28
  • 1
    @AndyE, My reading of the phrase "evaluating user input" includes evaluating input from user Carol in user Alice's browser. Before every PM and their mother was trying to make their website "social", a more limited reading would be reasonable, but today I think the more expansive reading should be the default unless the application spec specifically states otherwise. – Mike Samuel Apr 28 '13 at 16:51
  • 1
    The usecases can include evaluating user input on the server side. Then the security is important. – Hubert OG Apr 07 '15 at 21:27
5

You can use the advanced expression parser from math.js, which does not use JavaScript's eval.

http://mathjs.org

Usage:

var ans = math.evaluate('2 * (3 + 4)');

or use the parser (which supports variable and function assignments):

var parser = math.parser();
var ans = parser.evaluate('2 * (3 + 4)');
Jos de Jong
  • 6,602
  • 3
  • 38
  • 58
1

Try nerdamer

var result = nerdamer('sqrt(4)+cos(1)-2').evaluate();
document.getElementById('text').innerHTML = result.text();
<script src="http://nerdamer.com/js/nerdamer.core.js"></script>
<div id="text"></div>
ArchHaskeller
  • 1,270
  • 1
  • 12
  • 28
1

You could use a regex to replace everything except for your whitelist. But since the javascript is executed on the client (unless you're running the AOL http server) anyone who can modify the inputs can modify the code as well - so you're not really making it any more or less secure than it already is.

symcbean
  • 47,736
  • 6
  • 59
  • 94
  • 2
    It depends on whether the user typing the expression to be evaluated is the same as the user evaluating this expression. – Bruno Jul 09 '13 at 17:50