The goal of defender is to do static code analysis on other R packages to check for potential security risks and best practices. It provides checks on multiple levels:
- static code analysis without installing the package
- more thorough but potentially dangerous checks with installation / in Docker container
The checks do not tell you whether something is harmful but rather they flag code that you should double-check before running / loading the package.
You can install defender from github with:
# install.packages("devtools")
devtools::install_github("ropenscilabs/defender")
You can check for system calls in any directory locally available:
defender::summarize_system_calls("../testevil")
#> path line_number call
#> 1 inst/root_sys.R 1 system2("ls")
#> 2 inst/root_sys.R 4 system("ls")
#> 3 R/exported.R 7 system2("ls")
#> 4 R/internal.R 4 system("ls")
#> 5 R/internal.R 8 system("ls -la")
#> 6 R/processx.R 3 processx::run("ls")
#> 7 R/sys.R 8 sys::exec_internal("ls")
#> 8 R/system_hidden.R 2 system2("lm")
#> function_name
#> 1 system2
#> 2 system
#> 3 system2
#> 4 system
#> 5 system
#> 6 processx::run
#> 7 sys::exec_internal
#> 8 system2
You can also include additional elements to flag as dangerous:
sc <- defender::system_calls("poll")
defender::summarize_system_calls("../testevil", calls_to_flag = sc)
#> path line_number call
#> 1 inst/root_sys.R 1 system2("ls")
#> 2 inst/root_sys.R 4 system("ls")
#> 3 R/exported.R 7 system2("ls")
#> 4 R/internal.R 4 system("ls")
#> 5 R/internal.R 8 system("ls -la")
#> 6 R/processx.R 9 poll("ls")
#> 7 R/processx.R 3 processx::run("ls")
#> 8 R/sys.R 8 sys::exec_internal("ls")
#> 9 R/system_hidden.R 2 system2("lm")
#> function_name
#> 1 system2
#> 2 system
#> 3 system2
#> 4 system
#> 5 system
#> 6 poll
#> 7 processx::run
#> 8 sys::exec_internal
#> 9 system2
You can check the NAMESPACE file in a package for dangerous imports:
defender::check_namespace("../testevil")
#> type import package
#> 1 package sys sys
#> 2 package processx processx
#> 3 function processx::run processx
You can also include additional elements to flag as dangerous:
di <- defender::dangerous_imports("processx::poll")
defender::check_namespace("../testevil", imports_to_flag = di)
#> type import package
#> 1 package sys sys
#> 2 package processx processx
#> 3 function processx::poll processx
#> 4 function processx::run processx
- Ildi Czeller @czeildi
- Karthik Ram @karthik
- Bob Rudis @hrbrmstr
- Kara Woo @karawoo