4

I want to parse user's expressions that validates to booleans using standard javascript like:

var1 > obj1.prop1 && var2 + 1 <= 5

Since this expressions are written by the user, I want to be sure they are clean becase they are going to be evaluated server side with NodeJS.

Instead of having to parse the expression as text looking for patterns and reinvent the wheel, is there a way I can use the power of Node to directly evaluate the expression without the risk of code injection?

DomingoSL
  • 14,920
  • 24
  • 99
  • 173

3 Answers3

4

You might not like this answer. But you have to do work. There is no magic bullet.

Your question contradicts itself by requiring "standard javascript" and "without the risk of code injection". You cannot have both. Standard JavaScript allows expressions like 'require("fs").rmdirSync("/")'

The expression entered by the user has to be constrained to a seriously limited subset of JavaScript. The server must validate that the input is limited to this subset before attempting to evaluate it.

So first you need to think carefully about what limited subset is allowed. It looks like you want to allow constants integers like '5', operators like '>', '&&' and '<='. You also allow access to variables like 'var1' 'obj1.prop1' 'var2'. I'd imagine you need to be very specific about the list of allowed variables.

The key to preventing script injection is to define a subset that includes only things you know are safe. You should not try to start with the whole of JavaScript and exclude things you think are dangerous - because you will miss some.

Once you have carefully defined what the expressions may contain, you need to implement code to parse and validate expressions. You may find a library or standard code to do this, but you will have to modify or configure it to permit your specific requirements.

James
  • 5,635
  • 2
  • 33
  • 44
1

You can use the mathjs library which comes with it's own expression parser.

Website: http://mathjs.org/

Jos de Jong
  • 6,602
  • 3
  • 38
  • 58
0

In generally no matter what you will finally use, I would launch an evaluation process where you drop all privilege and only communicated with it using unix domain socket. And send the code you want to evaluate to it.

Launch it as root and open the unix domain socket and then drop the privilege to nobody.

process.setgid('nobody');
process.setuid('nobody');

One thing that you should avoid is to do something like this:

const root = global = {};
const require = function() {
  console.log('tryed to call require', arguments);
}

eval("require('fs')");

This might work on the first look, but e.g. with ES6 there was the import keyword introduces, so even if you overwrite require you could still use import to load module.

Also the methods mentioned in Safely sandbox and execute user submitted JavaScript? like vm.runInContext('globalVar *= 2;', sandbox); would not help. But the referenced sandcastle, might be something you could look at, but even if you use a sandboxing library I would still suggest to run it in an isolated unprivileged process.

Like James suggested in the answer you should go the way that whitelist certain features instead of backlisting harmful ones.

Community
  • 1
  • 1
t.niese
  • 39,256
  • 9
  • 74
  • 101