4

サンドボックス環境で使用したい特別な (ダミー) 関数があります。

disable.system.call <- function(...) {
    mc <- match.call()
    if (grepl('system', deparse(mc[[2]])))
        stop('NONO')
    eval(mc, env = .GlobalEnv)        
}

system最初の引数の名前に単語が含まれているかどうかをチェックするだけで、特別なことは何もしません。これは単なる POC の例です。

base後で行うこと: この単純な関数をいくつかの関数と関数に割り当ててstats、評価された式systemに最初の引数として単語が含まれているかどうかを確認します。例えば:

e <- new.env()
eval(parse(text = 'model.frame <- disable.system.call'), envir = e)

system内部のない呼び出しは魅力のように機能するため、これは非常にクールに機能しますが、フィルターは機能します。

> eval(parse(text = 'model.frame("1 ~ 1")'), envir = e)
  1
1 1
> eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
Error in model.frame("1 ~ system(\"ls -la\")") : NONO

文字列のような式が見つかった内部でlm呼び出す呼び出しでも動作しています:model.frame

> eval(parse(text = 'lm(\'1 ~ system("ls -la")\')'), envir = e)
Error in model.frame(formula = "1 ~ system(\"ls -la\")", drop.unused.levels = TRUE) : 
  NONO

もう少し先に進んで、from から呼び出される非常に単純な関数 ( disable.system.call) を割り当てました。残念ながら、私はこれまでのところ得ていません:as.formulamodel.frame

> e <- new.env()
> eval(parse(text = 'as.formula <- disable.system.call'), envir = e)
> eval(parse(text = 'as.formula("1 ~ 1")'), envir = e)
1 ~ 1
> eval(parse(text = 'as.formula(\'1 ~ system("ls -la")\')'), envir = e)
Error in as.formula("1 ~ system(\"ls -la\")") : NONO
> eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
  1 system("ls -la")
1 1                0
> eval(parse(text = 'lm(\'1 ~ system("ls -la")\')'), envir = e)

Call:
lm(formula = "1 ~ system(\"ls -la\")")

Coefficients:
     (Intercept)  system("ls -la")  
           1                NA  

私が知ってmodel.frameいるように呼び出しas.formulaていますが、これは機能しません (上記の出力からわかるように)。カスタム環境で上記のようにmodel.frame呼び出しているためではstats::as.formulaないと確信しています。lmmodel.frame

ヒントやアイデアは大歓迎です!

4

2 に答える 2

4

そうではないのではないかと疑っていましたがstats:::model.frame.default、 environment のカスタム バージョンの代わりに が呼び出されていますe。(これはもちろん、パッケージ化された関数に一般的に期待される動作です。最初の例で見られる奇妙なスコープは、lm()「非標準評価」の使用による特別なケースです。答え)。

以下に示すように、 を使用して、それぞれのケースでtrace()どのバージョンが呼び出されているかを確認できます。as.formula()

disable.system.call <- function(...) {
    mc <- match.call()
    if (grepl('system', deparse(mc[[2]])))
        stop('NONO')
    eval(mc, env = .GlobalEnv)        
}
e <- new.env()
eval(parse(text = 'as.formula <- disable.system.call'), envir = e)


# (1) trace custom 'as.formula()' in environment e
trace(e$as.formula)


# Calling model.frame() **does not** call the the custom as.formula()
eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
#   1 system("ls -la")
# 1 1              127

# (2) trace stats:::as.formula()
trace(stats:::as.formula)

# Calling model.frame() **does** call stats:::as.formula()
eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
# trace: as.formula
#   1 system("ls -la")
# 1 1              127

編集:FWIW、最初の例でカスタムmodel.frame() 呼び出された理由は、 「非標準評価」と呼ばれることがあるものを採用しているためです。(この件に関する詳細については、この pdfを参照してください。) 重要な点は、呼び出し環境で評価されるように実際に指示することです。あなたの場合、これは関数のバージョンを見つけることにつながりました。lm()lm()lm()model.frame()

lm()非標準の評価を使用する理由model.frame()は、式で名前が付けられた変数が呼び出し環境で見つかった場合でもアクセスできるようにするためです (data引数を介して に渡された変数にアクセスできるだけではありませlm()ん)。Thomas Lumley がリンクされた pdf で述べているように:

式の変数がデータ引数に含まれている必要がある場合、人生ははるかに簡単になりますが、この要件は式が導入されたときに行われませんでした.

興味がある場合は、 の定義から関連する行を以下に示しますlm

mf <- match.call(expand.dots = FALSE)
...
mf[[1L]] <- as.name("model.frame")
mf <- eval(mf, parent.frame())
于 2012-04-08T23:03:00.167 に答える
3

他の人が を使用できないようにする場合systemは、定義を上書きする方が簡単です。

assignInNamespace(
  "system", 
  function(...) stop("system calls are not allowed"), 
  getNamespace("base")
)

system("pwd")  #throws an error

私はあなたのユースケースを大雑把に推測していますが、ユーザーが任意の R コードを他のアプリケーションに渡せるようにしていますか? その場合、危険な関数を削除するか、ダミーに置き換えて、独自のバージョンの R をコンパイルすることをお勧めします。


関数が呼び出されたときにカスタム コードを実行するもう 1 つの可能性は、trace. 例えば、

trace(system, quote(stop("You have called system")))  #you may also want print = FALSE
于 2012-04-08T22:38:21.083 に答える