模式中允许的功能集有限的 R 胶水?

问题描述

我使用 glue() 来格式化字符串。理想情况下,我想让用户选择提供他自己的、可能复杂的格式模式。这些通常作为 yaml 配置文件的一部分分发,该文件还包含许多其他设置。

library(glue)
df <- tibble(a = c(70,80,90,4,5),conversionunit = c(60,60,1,1))
pattern <- "{a/conversionunit} minutes" # loaded from user's config file
df <- df %>% mutate(output = glue(pattern))

# more complex alternative
pattern <- "{round(a/conversionunit,digits=2)} minutes" # loaded from user's config file
df <- df %>% mutate(output = glue(pattern))

但是,由于 glue 语句可能会执行任意代码,因此存在安全风险。下面的例子当然是良性的。风险的产生是因为用户很可能在没有详细研究的情况下下载和使用复杂的配置文件,并且不法分子可能会分发邪恶的配置文件

pattern <- "{shell('echo 1',intern = TRUE)} {a}"
df <- df %>% mutate(output = glue(pattern))

我知道 glue_safe 但这比我想要的更严格。理想情况下,我想提供一个允许的功能列表

safe_fun <- list(`*` = `*`,`/` = `/`,"round" = round) %>% as.environment() # etc

并且只允许使用那些指定的。有没有办法做到这一点?

解决方法

定义保存数据和函数的环境并将其父级设置为 emptyenv()

library(glue)
library(tibble) # lst

safe_fun <- lst(`*`,`/`,round)
safe_env <- list2env(c(df,safe_fun),parent = emptyenv())

# test 1
glue("{a/conversionunit} minutes",.envir = safe_env)
## 1.16666666666667 minutes
## 1.33333333333333 minutes
## 1.5 minutes
## 4 minutes
## 5 minutes

# test 2
glue("{sqrt(a)/conversionunit} minutes",.envir = safe_env)
## Error in sqrt(a) : could not find function "sqrt"
,

以此previous answer为基础:

library(glue)
library(dplyr)

df <- tibble(a = c(70,80,90,4,5),conversionunit = c(60,60,1,1))

allowed <- c("/")

# Create new empty environment
safe_env <- new.env(parent = emptyenv())

# assign allowed functions & objects
lapply(allowed,function(f) assign(f,get(f),safe_env))
list2env(df,safe_env)

pattern <- "{a/conversionunit} minutes" # loaded from user's config file

df %>% mutate(output = glue(pattern,.envir=safe_env))
#> # A tibble: 5 x 3
#>       a conversionunit output                  
#>   <dbl>          <dbl> <glue>                  
#> 1    70             60 1.16666666666667 minutes
#> 2    80             60 1.33333333333333 minutes
#> 3    90             60 1.5 minutes             
#> 4     4              1 4 minutes               
#> 5     5              1 5 minutes