Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a return value from arbitrary eval'd code

I have a requirement that the user can provide arbitrary statements which can be stored in a function and called later to get a return value. A simple example of this is that userInput might be

var x = 10;
x;

I would store this via

var callback = function() {
    return eval(userInput);
}

and then running callback() returns 10 as expected.

However, I also need to support the case with an explicit return statement, ie userInput might be

var x = 10;
return x;

In this case the eval method above will fail with SyntaxError: return not in function. Instead I could store callback as

var callback = new Function(userInput);

My issue is that I would like to combine these two approaches according the rule 'get explicit return value otherwise get the result of the last executed statement'. In particular this can't be done with analysis of the code at callback creation time as the user could potentially do something odd like

if(envVar < 10)
    return a;
b * 0.5;

which combines both.

Any thoughts on how to structure the creation of the callback function to accommodate these possible inputs? Unfortunately it is not possible to enforce one style or the other on the user.


UPDATE to answer some of the comments below.

One solution suggested is to search for a return token and choose between new Function and eval. This doesn't work for my last example, see http://jsfiddle.net/ZGb6z/2/ - out4 should be "no" but ends up being undefined as the last executed statement is not returned.

Another suggestion is to modify the user input to add an explicit return on the last line if one is not found. Unfortunately it's not possible to know which statement will be executed last. Consider

var x = 10;
switch(x) {
  case 10:
    100;
    break;
  default:
    200;
    break;
}

When called it should return 100, but it would take some serious analysis to determine where to put returns for arbitrary code.

like image 216
Guy Cook Avatar asked Dec 27 '13 10:12

Guy Cook


2 Answers

Just use a try catch, manipulating the input will be very painful for you, and try catch can't really make your code any more obtuse at this point.

var failback = function () {
  try {
    return eval(userInput);
  } catch (e) {
    return Function(userInput);
  }
};

What I would really suggest is investing in a parser, kind of like how Angular does it. That kind of thing would prevent your users from doing whatever the hell they want, introducing attack vectors, yadda, yadda, yadda.

like image 181
bevacqua Avatar answered Oct 03 '22 15:10

bevacqua


Either manage your expectations or manage your user's expectations. eval and new Function() are not suitable for your requirements if you require mixed usage of explicit and non-explicit return statements in the same user-input. You will continue to find issues following either of these routes.

Simply searching for the word return is not sufficient either... var returning = true; or var a = 'return'; or /* return true */ true; will all throw false positives.

Managing your expectations: To do such a thing you require a form of lexer and parser, at which point you can do away with eval entirely and execute your own safe functions based on the parsed input. This is the best approach when execution of user input has to occur anyway as you can ensure that nothing gets executed you do not wish to permit. If you want to cover these sort of edge cases and permit strange user input then you must be prepared to increase the size and development time of your application. I have built a few applications executing user generated code and have always come to the conclusion this is the correct route to go down.

Managing your user's expectations: Provide a guide, tell them not to mix explicit returns with non-explicit returns, these are strange coding practices anyway. Better yet explicitly tell them to include or omit the return statement. There is no shame in asking your users to follow them, especially if it allows you to improve their experience elsewhere.

like image 33
George Reith Avatar answered Oct 03 '22 15:10

George Reith