What Specifically Are the Dangers of Eval(Parse(...))

What specifically are the dangers of eval(parse(...))?

Most of the arguments against eval(parse(...)) arise not because of security concerns, after all, no claims are made about R being a safe interface to expose to the Internet, but rather because such code is generally doing things that can be accomplished using less obscure methods, i.e. methods that are both quicker and more human parse-able. The R language is supposed to be high-level, so the preference of the cognoscenti (and I do not consider myself in that group) is to see code that is both compact and expressive.

So the danger is that eval(parse(..)) is a backdoor method of getting around lack of knowledge and the hope in raising that barrier is that people will improve their use of the R language. The door remains open but the hope is for more expressive use of other features. Carl Witthoft's question earlier today illustrated not knowing that the get function was available, and the question he linked to exposed a lack of understanding of how the [[ function behaved (and how $ was more limited than [[). In both cases an eval(parse(..)) solution could be constructed, but it was clunkier and less clear than the alternative.

Why is using the JavaScript eval function a bad idea?

  1. Improper use of eval opens up your
    code for injection attacks

  2. Debugging can be more challenging
    (no line numbers, etc.)

  3. eval'd code executes slower (no opportunity to compile/cache eval'd code)

Edit: As @Jeff Walden points out in comments, #3 is less true today than it was in 2008. However, while some caching of compiled scripts may happen this will only be limited to scripts that are eval'd repeated with no modification. A more likely scenario is that you are eval'ing scripts that have undergone slight modification each time and as such could not be cached. Let's just say that SOME eval'd code executes more slowly.

At a basic level, what does eval-parse do in R?

eval(parse(text = "Larry")) is the same as typing Larry directly into your R terminal.

> "Text"
[1] "Text"
> eval(parse(text = "Text"))
Error in eval(expr, envir, enclos) : object 'Text' not found
> Text = 1:10
> eval(parse(text = "Text"))
[1] 1 2 3 4 5 6 7 8 9 10

It essentially allows you to call objects by their name in a character string. It's useful inside functions where you don't know which object you'll need yet, although most experienced R users find that storing data in a list eliminates most needs for eval(parse(...))

Avoiding the infamous eval(parse()) construct

Using get and [[:

bar <- list(foo = list(fast = 1:5, slow = 6:10),
oof = list(6:10, 1:5))

rab <- 'bar'

get(rab)[['oof']]
# [[1]]
# [1] 6 7 8 9 10
#
# [[2]]
# [1] 1 2 3 4 5

When is JavaScript's eval() not evil?

I'd like to take a moment to address the premise of your question - that eval() is "evil". The word "evil", as used by programming language people, usually means "dangerous", or more precisely "able to cause lots of harm with a simple-looking command". So, when is it OK to use something dangerous? When you know what the danger is, and when you're taking the appropriate precautions.

To the point, let's look at the dangers in the use of eval(). There are probably many small hidden dangers just like everything else, but the two big risks - the reason why eval() is considered evil - are performance and code injection.

  • Performance - eval() runs the interpreter/compiler. If your code is compiled, then this is a big hit, because you need to call a possibly-heavy compiler in the middle of run-time. However, JavaScript is still mostly an interpreted language, which means that calling eval() is not a big performance hit in the general case (but see my specific remarks below).
  • Code injection - eval() potentially runs a string of code under elevated privileges. For example, a program running as administrator/root would never want to eval() user input, because that input could potentially be "rm -rf /etc/important-file" or worse. Again, JavaScript in a browser doesn't have that problem, because the program is running in the user's own account anyway. Server-side JavaScript could have that problem.

On to your specific case. From what I understand, you're generating the strings yourself, so assuming you're careful not to allow a string like "rm -rf something-important" to be generated, there's no code injection risk (but please remember, it's very very hard to ensure this in the general case). Also, if you're running in the browser then code injection is a pretty minor risk, I believe.

As for performance, you'll have to weight that against ease of coding. It is my opinion that if you're parsing the formula, you might as well compute the result during the parse rather than run another parser (the one inside eval()). But it may be easier to code using eval(), and the performance hit will probably be unnoticeable. It looks like eval() in this case is no more evil than any other function that could possibly save you some time.

R: eval(parse(...)) is often suboptimal

Actually the list probably looks a bit different. The '$' convention is somewhat misleading. Try this:

dat[["orders"]][[ or_ID ]][["price"]]

The '$' does not evaluate its arguments, but "[[" does, so or_ID will get turned into "5810584".

Avoiding eval-parse or do.call

We may use getFunction

library(ggplot2)
p1 <- p +
getFunction(all_ggplot2_funs[grep("theme_", all_ggplot2_funs)][15])()

-checking

> p2 <- p + theme_minimal()
> all.equal(p1, p2)
[1] TRUE


Related Topics



Leave a reply



Submit