Overview

The static-eval module is intended to statically evaluate a code block. In theory (and assumed by many modules who depend on it) should have no side effects, should not have have access to the standard library, and effectively be sandboxed. However if un-sanitized user input is passed to evaluate we can break out of this “sandbox” in two ways.

First because the modue wants to support something like:

[1,2,3].map(function(){  }  

FunctionExpression (to create the function passed into map) and CallExpression (to invoke map) must be supported. The body of the created created function expression was not walked which allowed us to create something like:

var evaluate = require('static-eval');
var parse = require('esprima').parse;
var src = '(function(){console.log(process.pid)})';
var ast = parse(src).body[0].expression;
var res = evaluate(ast, {});

In the second issue a function could be used to get a reference to the global Function constructor resulting in another breakout of the sandbox.

var evaluate = require('static-eval');
var parse = require('esprima').parse;
var src = '(function () {}).constructor(“console.log(process.pid)”)';
var ast = parse(src).body[0].expression;
var res = evaluate(ast, {});

I created https://github.com/substack/static-eval/pull/18 to resolve both issues. It now:

  1. Walks the “bodies” of a FunctionExpression to ensure that they were also parsed and handled correctly
  2. Does not allow access to attributes to an object that has been evaluated down to a function.

I came across these issues when using the jsonpath module which depends on static-eval (and is in turn used used by the Ghost blog platform) which states teh following: “Script expressions (i.e, (…) and ?(…)) are statically evaluated via static-eval rather than using the underlying script engine directly. That means both that the scope is limited to the instance variable (@), and only simple expressions (with no side effects) will be valid.”

However we can see that with the example below this expression is not safe.

var jp = require('jsonpath');
var names = jp.query(data, '$..book[?((function (){console.log(process.pid)})())]');

Remediation

Upgrade to static-eval 2.0