22.3 Compute resources

What about errors, holes, dangerous function combinations, selective characters such as special characters that are not recognizable by the system language? What can be done to prevent unexpected values from being used?

Malicious attackers can:

  • run any R code they want
  • delete important files
  • modify data
  • send confidential data back to the user of the app

You should never:

  • source() an uploaded .R file
  • rmarkdown::render() an uploaded .Rmd

Mind:

  • the combination of parse() and eval() as big warning sign for any Shiny app

Danger:

  • it is possible to ask R to execute arbitrary code inside a model formula

Below are some examples of dangerous app chunks:

1 - This first example shows how the combination of parse() and eval() allows the user to insert only numeric values.

server <- function(input, output, session) {

  output$results <- renderText({

    eval(parse(text = input$code))
 
  })
}

2 - Other kinds of possibilities for error chain generators are given by the model formula, the formula can executes an extra command print()

lm(y ~ {print("Hi!"); x}, data = df)

#> [1] "Hi!"

3 - Third warning comes from the {glue} package, the book in this case suggests to pay attention to use:

glue::glue("{title}-{print('Hi'); number}")

and suggests to substitute with glue_safe() which instead releases an error/warning:

glue::glue_safe("{title}-{print('Hi'); number}")

#> Error in .transformer(expr, env): object 'print('Hi'); number' not found

4 - In case of variable transformation, this is what can happen:

https://xkcd.com/327/

Figure 22.9: https://xkcd.com/327/

The code to construct a SQL query with paste() could lead to flexibilities in code construction that would allow the addition of extra characters not warned or advised by any friendly error messages

find_student <- function(name) {
  paste0("SELECT * FROM Students WHERE name = ('", name, "');")
}


find_student("Hadley")
## [1] "SELECT * FROM Students WHERE name = ('Hadley');"
find_student("Robert'); DROP TABLE Students; --")
## [1] "SELECT * FROM Students WHERE name = ('Robert'); DROP TABLE Students; --');"

solution: use glue::glue_sql()

con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")


find_student <- function(name) {
  
  glue::glue_sql("SELECT * FROM Students WHERE name = ({name});", .con = con)
}


find_student("Robert'); DROP TABLE Students; --")
## <SQL> SELECT * FROM Students WHERE name = ('Robert''); DROP TABLE Students; --');