Skip to contents

sbom() looks at what a folder holds, builds the SBOM from the strongest source it finds, and reads every other source to check it. It runs no code from the project and makes no network requests, except that, with Nix installed, a default.nix is read by running nix-instantiate (see sbom_nix()), which may download nixpkgs, that snapshot looks up a CRAN snapshot, and that advisories = TRUE downloads OSV's advisory data.

Usage

sbom(path = ".", ...)

Arguments

path

A project folder.

...

Named arguments for the reader that sbom() picks: any argument of sbom_renv(), sbom_rsconnect(), sbom_nix() (such as nix and snapshot) or sbom_package() other than the file or folder it reads, such as library, lib, dependencies, r_version, author, overrides or advisories. The code scan takes lib, author, organization, regid, timestamp, overrides, embedded, system, advisories and advisories_on_error. A Shiny app also takes evaluate_ui (see sbom_shiny()), which other folders ignore. A name that no reader takes is an error. The reader ignores those it does not take, except that the cross-checks of DESCRIPTION and the code also use lib. Overrides, given or from sbom-overrides.csv, apply to the SBOM after the cross-checks, which compare what the sources say, and advisories are matched after the overrides.

Value

An <sbom> object. See sbom_components() to read it.

Details

The sources, from strongest to weakest:

  1. renv.lock, read by sbom_renv().

  2. manifest.json, read by sbom_rsconnect(), when it is a Posit Connect manifest: one with version and packages, whose packages and files are objects.

  3. default.nix, read by sbom_nix(). A file that rix wrote lists the packages it names, without versions, unless snapshot is given or Nix is installed and the environment is built: then it is read from the Nix store, with versions. With Nix installed but the environment not built, the file itself is read, with an issue. A hand-written file can be read only with Nix.

  4. DESCRIPTION, read by sbom_package(), when it has a Package field.

  5. The R code itself, scanned with renv::dependencies(). Each package it uses is looked up in lib and listed at the version installed there, as inferred; one that is not installed is listed as unknown, with an issue. renv itself, which renv infers from a renv.lock, is left out, and so is the code in any subfolder that holds its own renv.lock, DESCRIPTION or manifest.json, a project of its own. In a package folder, so are test fixtures and snapshots, inst/extdata and the files .Rbuildignore excludes. The root is the folder, an application named after it, with its git commit as the version. This is the weakest evidence, and a note says so.

A folder that is a Shiny app (see sbom_shiny()) is described by sbom_shiny(), which sbom() calls for it. That includes a folder with app.R, or ui.R and server.R, even when it also has a DESCRIPTION without golem structure.

The strongest source present makes the SBOM. Every other source present, and the code scan when the SBOM does not already come from it, are read to cross-check it, and their disagreements become issues: a package that one source lists and the other does not, or that they list at different versions. A DESCRIPTION, the code, and a default.nix read without Nix or a snapshot declare no versions, so they report only the packages they use that the SBOM does not list. A DESCRIPTION without a Package field is not a package, so it is used only to cross-check the packages its Depends and Imports name. A source that cannot be read for a cross-check, or a manifest.json that is not a Posit Connect manifest, is an issue, not an error. So is a hand-written default.nix that sbom cannot read, even when it is the strongest source: one on a machine without Nix, or, under the default nix = NA, one that Nix fails to evaluate or whose environment has not been built. A warning says so, and the SBOM is built from the next source. Any other failure to read the strongest source, such as nix = TRUE on a machine without Nix, is an error. A cross-check reads default.nix with nix as given (by default, Nix is used when it is installed, and nix = FALSE never calls it) and without a snapshot. Under nix = TRUE, a default.nix that Nix cannot read is an error even as a cross-check. When Nix runs for that cross-check and cannot read the environment, its issue is carried into the SBOM.

A project folder's git commit is read from the files in the nearest .git in it or a folder above it, without running git. A folder that is not in a git repository, or whose commit cannot be read, has no version, with an issue.

When the folder holds sbom-overrides.csv and overrides is not given, that file is applied as the overrides.

Advisories, when asked for with advisories, are matched once, with the SBOM, after the overrides (and, for a Shiny app, after the app's own components are added). The cross-checks never read advisory data. See sbom_package() for what is matched and what is sent.

The inputs element of the SBOM's metadata lists every source read.

Examples

x <- sbom(sbom_example("connect-app"), system = FALSE)
x
#> <sbom> connect-app
#> Components: 9
#> Relationships: 8
#> Issues: 9 (see sbom_issues())
#> Advisories: not requested.
sbom_issues(x)
#>                                                                                 ref
#> 1                                                                    local/localpkg
#> 2                                                                    local/localpkg
#> 3                                                               project/connect-app
#> 4                                                               project/connect-app
#> 5 pkg:cran/biocpkg@1.2.3?repository_url=https://bioconductor.org/packages/3.22/bioc
#> 6    pkg:cran/glue@1.8.1?repository_url=https://packagemanager.posit.co/cran/latest
#> 7                                                                    local/localpkg
#> 8                                                                    unknown/ppmpkg
#> 9                                                               project/connect-app
#>       field severity              code
#> 1   license  warning   license-no-spdx
#> 2  supplier  warning  supplier-unknown
#> 3    author     info    no-sbom-author
#> 4   library     info drift-not-checked
#> 5 component  warning     drift-missing
#> 6 component  warning     drift-missing
#> 7 component  warning     drift-missing
#> 8 component  warning       drift-extra
#> 9   version  warning   version-missing
#>                                                                                                  message
#> 1 The license of localpkg 0.0.1, file LICENSE, has no SPDX expression, so it is recorded as NOASSERTION.
#> 2                     localpkg 0.0.1 was installed from a local source, so it has no supplier on record.
#> 3          No author or organization was given for this SBOM, so only the tool that made it is recorded.
#> 4              No project library was found, so the installed packages were not compared with renv.lock.
#> 5                                                biocpkg 1.2.3 is in renv.lock but not in manifest.json.
#> 6                                                   glue 1.8.1 is in renv.lock but not in manifest.json.
#> 7                                               localpkg 0.0.1 is in renv.lock but not in manifest.json.
#> 8                                                 ppmpkg 2.0.0 is in manifest.json but not in renv.lock.
#> 9                          The project folder connect-app is not a git repository, so it has no version.