2016-06-08 5 views
8

Ich möchte den Benutzer die Eingabe einer Zeichenfolge für eine Formel für y als Funktion von x, z.Werten Sie einen beliebigen symbolischen Ausdruck aus, der bei einigen Werten als String angegeben wird.

fn <- "x^2 + exp(3*x)" 

Ich möchte dann diesen Ausdruck an einigen Punkten, z.B. für Werte von x <- 1:0.1:100.

Ich weiß, was ich tun kann:

x <- 1:0.1:100 
y <- eval(parse(text = fn)) 

jedoch das lässt mein Computer offen für Angriffe, wenn der Benutzer liefert fn, die keine Formel

fn <- 'x; print("Your computer is compromised")' 

gibt es eine beliebige alternative Wege zur Erreichung Was ich machen will; was ich vorhabe zu tun?

+1

Ich denke, es sollte eine Art und Weise, dies zu tun mit 'substitute'/'parse', aber ich kann es nicht erarbeiten –

+4

https://cran.r-project.org/package=RAppArmor –

Antwort

9

Einer der coolsten Features des R ist, dass es seine eigene Sprache verarbeiten kann, so dass Sie eine Funktion weiße Liste erstellen und überprüfen Ausdrücke dagegen:

# Function to check if an expression is safe 
is_safe_call <- function(text, allowed.funs) { 
    # Recursive function to evaluate each element in the user expression 
    safe_fun <- function(call, allowed.funs) { 
    if(is.call(call)) { 
     call.l <- as.list(call) 
     if(!is.name(call.l[[1L]]) || !as.character(call.l[[1L]]) %in% allowed.funs) 
     stop(as.character(call.l[[1L]]), " is not an allowed function") 
     lapply(tail(call.l, -1L), safe_fun, allowed.funs) 
    } 
    TRUE 
    } 
    # parse and call recursive function 
    expr <- parse(text=text) 
    if(length(expr) > 1L) stop("Only single R expressions allowed") 
    safe_fun(expr[[1L]], allowed.funs) 
} 

Wir haben dann eine weiße Liste der zulässigen Funktionen definieren. Es ist sehr wichtig, dass Sie sehr vorsichtig sind, was Sie hier zulassen. Insbesondere wenn Sie parse, eval oder andere Funktionen mit möglichen unangenehmen Nebenwirkungen zulassen (system, unlink, usw.), öffnen Sie Ihr System für den Angriff.

allowed.funs <- c("+", "exp", "*", "^", "-", "sqrt") 

Und wir testen:

is_safe_call("x^2 + exp(3*x)", allowed.funs) 
## [1] TRUE 
is_safe_call("x^2 - sqrt(3*x)", allowed.funs) 
## [1] TRUE 
is_safe_call("unlink('~', recursive=TRUE)", allowed.funs) 
## Error in safe_fun(parse(text = text)[[1L]], allowed.funs) : 
## unlink is not an allowed function 
is_safe_call("x + sqrt(unlink('~', recursive=TRUE))", allowed.funs) 
## Error in FUN(X[[i]], ...) : unlink is not an allowed function 
is_safe_call('x; print("Your computer is compromised")') 
## Error in is_safe_call("x; print(\"Your computer is compromised\")") : 
## Only single R expressions allowed 

Keine waranties ausdrücken oder in diese impliziert werden. Es mag einen Weg geben, dies zu hacken, an den ich nicht gedacht habe, also stellen Sie es nicht ohne eine gründliche Überprüfung auf einen öffentlich zugänglichen Server, aber ich denke, das könnte einfach funktionieren.

Beachten Sie, dass, wenn jemand einen Ausdruck zur Verfügung stellen kann, der sich selbst hackt parse, dann können Sie auf diese Weise kompromittiert werden.


EDIT: Ben Bolker schlug einen cleveren Trick, um zu versuchen, dies zu hacken, aber diese Funktion ist robust, dass:

is_safe_call("exp <- function(...) system(\"echo do bad stuff\")", allowed.funs) 
## Error in safe_fun(expr[[1L]], allowed.funs) : 
## <- is not an allowed function 
allowed.funs <- c("+", "exp", "*", "^", "-", "sqrt", "<-") 
is_safe_call("exp <- function(...) system(\"echo do bad stuff\")", allowed.funs) 
## Error in FUN(X[[i]], ...) : function is not an allowed function 
allowed.funs <- c("+", "exp", "*", "^", "-", "sqrt", "<-", "function") 
is_safe_call("exp <- function(...) system(\"echo do bad stuff\")", allowed.funs) 
## Error in FUN(X[[i]], ...) : system is not an allowed function 
+0

das sieht gut aus. Ich dachte, eine andere Methode wäre es, Formel zu nutzen, aber ich kann mir den zugrunde liegenden Code nicht ansehen. – Alex

+0

@BrodieG schön. Gute Arbeit! – coatless

+5

aber was ist mit 'exp <- function (...) system (" echo do bad stuff ")' ...? Wenn Sie sicher sein wollen, müssen Sie sicherstellen, dass Sie 'base :: exp' verwenden? –

Verwandte Themen