-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial stab at having a general R project scanner
Some redundancies already seen here may warrant adding a class or function factories to assist.
- Loading branch information
Showing
7 changed files
with
168 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#' Parse renv LOCK file | ||
#' | ||
#' Parse the LOCK file containing R packages and versions at the provided location. | ||
#' | ||
#' @param dir Directory of project with LOCK file. | ||
#' @param file_name Name of LOCK file (default: 'renv.lock'). | ||
#' @param as.data.frame Boolean value, determine if parsed content is in a data.frame format | ||
#' | ||
#' @returns Package and version information as a list of data.frame. | ||
#' | ||
#' @noRd | ||
parse_renv_lock <- function(dir = '.', file_name = 'renv.lock', as.data.frame = TRUE) { | ||
|
||
lock_location <- file.path(dir, file_name) | ||
stopifnot(file.exists(lock_location)) | ||
|
||
lock_n_load <- jsonlite::fromJSON(lock_location)[['Packages']] | ||
|
||
if(as.data.frame) { | ||
purrr::list_rbind(purrr::map(lock_n_load, | ||
function(x) { | ||
data.frame(name = purrr::pluck(x, 'Package'), | ||
version = purrr::pluck(x, 'Version'), | ||
ecosystem = 'CRAN') | ||
}) | ||
) | ||
} else { | ||
purrr::map(lock_n_load, | ||
function(x) { | ||
c(name = purrr::pluck(x, 'Package'), | ||
version = purrr::pluck(x, 'Version'), | ||
ecosystem = 'CRAN') | ||
}) | ||
} | ||
} | ||
|
||
#' Parse R installed libraries | ||
#' | ||
#' Parse and return installed libraries discovered at the library paths. | ||
#' | ||
#' @details | ||
#' Default path will be from results of \code{.libPaths()}. | ||
#' | ||
#' | ||
#' @param ... Parameters for \code{\link[utils]{installed.packages()}}. | ||
#' | ||
#' @returns Package and version information as a data.frame. | ||
#' | ||
#' @noRd | ||
parse_r_libpath <- function(...) { | ||
rlibs <- utils::installed.packages(...)[,c('Package', 'Version')] | ||
rlibs <- as.data.frame(rlibs, row.names = FALSE) | ||
colnames(rlibs) <- c('name', 'version') | ||
rlibs <- cbind(rlibs, ecosystem = 'CRAN') | ||
rlibs | ||
} | ||
|
||
|
||
#' Scan renv LOCK file for vulnerabilities | ||
#' | ||
#' Parse and scan the renv LOCK file at specified location for vulnerabilities in the OSV database. | ||
#' | ||
#' @inheritParams parse_renv_lock | ||
#' | ||
#' @returns A data.frame specifying which packages are vulnerable or not. | ||
#' | ||
#' @noRd | ||
osv_scan_renv <- function(dir = '.', as.data.frame = TRUE) { | ||
pkg_data <- parse_renv_lock(dir = dir, as.data.frame = as.data.frame) | ||
pkg_data$is_vul <- is_pkg_vulnerable(name = pkg_data$name, ecosystem = pkg_data$ecosystem, version = pkg_data$version) | ||
pkg_data | ||
} | ||
|
||
|
||
#' Scan installed R libraries for vulnerabilities | ||
#' | ||
#' Parse and scan installed R libraries for vulnerabilities in the OSV database. | ||
#' | ||
#' @inheritParams parse_r_libpath | ||
#' | ||
#' @returns A data.frame specifying which packages are vulnerable or not. | ||
#' | ||
#' @noRd | ||
osv_scan_r_libpath <- function(...) { | ||
pkg_data <- parse_r_libpath(...) | ||
pkg_data$is_vul <- is_pkg_vulnerable(name = pkg_data$name, ecosystem = pkg_data$ecosystem, version = pkg_data$version) | ||
pkg_data | ||
} | ||
|
||
|
||
#' Scan an R project for vulnerabilities | ||
#' | ||
#' Parse and scan LOCK files and installed packages for package vulnerabilities in the OSV database. | ||
#' | ||
#' @param dir Project location. | ||
#' @param sort_by_vul Boolean value, to determine if vulnerable packages should be listed at top. | ||
#' | ||
#' @returns A data.frame specifying which packages are vulnerable or not. | ||
#' | ||
#' @noRd | ||
osv_scan_r_project <- function(dir = '.', sort_by_vul = TRUE) { | ||
|
||
# Attempt each and bind | ||
lock_pkgs <- tryCatch(parse_renv_lock(dir = dir), | ||
error = function(e) {warning(e); return(NULL)}) | ||
|
||
pkg_data <- unique(rbind(parse_r_libpath(), lock_pkgs)) | ||
|
||
pkg_data$is_vul <- is_pkg_vulnerable(name = pkg_data$name, ecosystem = pkg_data$ecosystem, version = pkg_data$version) | ||
|
||
if(sort_by_vul) { | ||
pkg_data[order(pkg_data$is_vul, pkg_data$name, pkg_data$version, decreasing = TRUE),] | ||
} else{ | ||
pkg_data[order(pkg_data$name, pkg_data$version),] | ||
} | ||
} | ||
|
||
|
||
#' Use OSV database to scan for vulnerabilities | ||
#' | ||
#' Scan project based upon specified mode to determine if any vulnerable packages are detected. | ||
#' | ||
#' @param mode The kind of scan to perform. | ||
#' @param ... Parameters passed to specific underlying functions for mode selected. | ||
#' | ||
#' @returns A data.frame specifying which packages are vulnerable or not. | ||
#' | ||
#' @examplesIf interactive() | ||
#' osv_scan('r_libpath') | ||
#' | ||
#' @export | ||
osv_scan <- function(mode, ...) { | ||
switch(mode, | ||
r_project = osv_scan_r_libpath(...), | ||
renv = osv_scan_renv(...), | ||
r_libpath = osv_scan_r_project(...)) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.