Skip to contents

Project Status: Active R-CMD-check coverage osv-scanner

pkgaudit scans R packages for security-relevant files and code without executing anything it scans. It reports what code does and when it runs, so code that runs on install or load is distinguishable from code that runs only when called.

A general-purpose scanner like Semgrep can read R – but it reads scripts, not packages. It does not look for R inside an \examples{} block, an \Sexpr{} macro, or a vignette. It does not know that .onLoad() runs when a user calls library() or that a configure script runs on R CMD check. pkgaudit extracts code wherever it exists, scans it for functions and commands that need human review, and reports the lifecycle phases in which each finding runs.

For why this matters, see R Package Security. For the rule set, see Rule Coverage.

Installation

remotes::install_github("tylerjssmith/pkgaudit")

Usage

A source package tarball can be scanned before it is installed. The example below scans untrustedpkg, a small package shipped with pkgaudit for demonstration.

library(pkgaudit)

tarball <- system.file(
  "extdata", "untrustedpkg", "untrustedpkg_0.1.0.tar.gz",
  package = "pkgaudit"
)

result <- audit_tarball(tarball)

summary(result, path = FALSE)
#> --- pkgaudit Summary --------------------------------------------------------
#> Package:   untrustedpkg v0.1.0 (source tarball)
#> SHA-256:   0c58ddcb365787ab7401c5eedaa4be7eb4ce6bea0a5ca290b6b7b1d8eb621d44
#> Scanned:   2026-08-27 01:16 UTC with pkgaudit v0.4.0, rules v0.4.0
#> 
#> --- R Patterns --------------------------------------------------------------
#> phase            rule            n   attck
#> at_build         httr            1   T1041
#> at_build         system          1   T1059.003 T1059.004
#> at_check         download_file   1   T1105
#> at_check         httr            1   T1041
#> at_check         system          1   T1059.003 T1059.004
#> at_install_src   httr            1   T1041
#> at_install_src   system          1   T1059.003 T1059.004
#> at_load          system          1   T1059.003 T1059.004
#> none             download_file   1   T1105
#> 
#> none: reported at no phase because nothing in the package was seen to call
#> it. Code under R/ is read this way by rule; a caller elsewhere, or a user,
#> can still reach it. See vignette("rules").
#> 
#> --- Shell / Make Matches ----------------------------------------------------
#> phase            rule            n   attck
#> at_build         curl            1   T1041 T1105
#> at_check         curl            1   T1041 T1105
#> at_install_src   curl            1   T1041 T1105
#> 
#> --- Coverage ----------------------------------------------------------------
#> status       top_level   type          files   lines
#> parsed       R/          R                 2       6
#> parsed       man/        Rd                1      12
#> matched      .           shell             1       3
#> unexamined   .           DESCRIPTION       1
#> 
#> --- Errors ------------------------------------------------------------------
#> No exceptions were raised.

Phases overlap – building a package with vignettes, for example, also installs and loads it – and one occurrence is counted under every phase it runs in. The summary above reflects five findings, some counted under multiple phases.

pkgaudit can integrate its scan with other tools. emit_sarif() renders its results as SARIF 2.1.0, which editors and code-scanning platforms read directly. export_unscanned() exports code written in languages pkgaudit cannot read to a directory for a scanner that can.

See Getting Started with pkgaudit for details.

Security

pkgaudit’s own security model, and how to report a vulnerability, are in SECURITY.md. To propose or revise a rule, see CONTRIBUTING.md. How pkgaudit works internally, for a reader auditing the source, is in Internals.