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

R glue with limited set of functions allowed in pattern?

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

library(glue)
df <- tibble(a = c(70,80,90,4,5), conversionunit = c(60,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

并且只允许使用那些指定的。有什么办法吗?

在此基础上previous answer

library(glue)
library(dplyr)

df <- tibble(a = c(70,80,90,4,5), conversionunit = c(60,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


定义保存数据和函数的环境并将其父级设置为 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"