Here's an idea. What if you used a static analyzer (something you could build with esprima, for example) to determine which outside variables the eval'd code uses, and alias them. By "outside code" i mean variables the eval'd code uses but does not declare. Here's an example:
eval(safeEval(
"var x = window.theX;"
+"y = Math.random();"
+"eval('window.z = 500;');"))
where safeEval returns the javascript string modified with a context that blocks access to outside variables:
";(function(y, Math, window) {"
+"var x = window.theX;"
+"y = Math.random();"
+"eval(safeEval('window.z = 500;');"
"})();"
There are a couple things you can do now with this:
- You can ensure that eval'd code can't read the values of outside variables, nor write to them (by passing
undefined
as the function arguments, or not passing arguments). Or you could simply throw an exception in cases where variables are being unsafely accessed.
- You also ensure that variables created by eval don't affect the surrounding scope
- You could allow eval to create variables in the surrounding scope by declaring those variables outside the closure instead of as function parameters
- You could allow read-only access by copying values of outside variables and using them as arguments to the function
- You could allow read-write access to specific variables by telling safeEval to not alias those particular names
- You can detect cases where the eval does not modify a particular variable and allow it to be automatically excluded from being aliased (eg. Math in this case, is not being modified)
- You could give the eval a context in which to run, by passing in argument values that may be different than the surrounding context
- You could capture context changes by also returning the function arguments from the function so you can examine them outside the eval.
Note that the use of eval
is a special case, since by its nature, it effectively can't be wrapped in another function (which is why we have to do eval(safeEval(...))
).
Of course, doing all this work may slow down your code, but there are certainly places where the hit won't matter. Hope this helps someone. And if anyone creates a proof of concept, I'd love to see a link to it here ; )