tiiuae / sbomnix

A suite of utilities to help with software supply chain challenges on nix targets
135 stars 23 forks source link
bill-of-materials cpe cyclonedx dependencies nix purl python sbom sbom-generator sbom-tool security software-bill-of-materials software-supply-chain software-supply-chain-security spdx-sbom static-analysis vulnerability-scanners

sbomnix

This repository is home to various command line tools and Python libraries that aim to help with software supply chain challenges:

For an example of how to use the tooling provided in this repository to automate daily vulnerability scans for a nix flake project, see: ghafscan.

The CycloneDX and SPDX SBOMs for each release of sbomnix tooling is available in the release assets.

All the tools in this repository originate from Ghaf Framework.

Table of Contents

Getting Started

sbomnix requires common Nix tools like nix and nix-store. These tools are expected to be in $PATH.

Running as Nix Flake

sbomnix can be run as a Nix flake from the tiiuae/sbomnix repository:

# '--' signifies the end of argument list for `nix`.
# '--help' is the first argument to `sbomnix`
$ nix run github:tiiuae/sbomnix#sbomnix -- --help

or from a local repository:

$ git clone https://github.com/tiiuae/sbomnix
$ cd sbomnix
$ nix run .#sbomnix -- --help

See the full list of supported flake targets by running nix flake show.

Running from Nix Development Shell

If you have nix flakes enabled, start a development shell:

$ git clone https://github.com/tiiuae/sbomnix
$ cd sbomnix
$ nix develop

You can also use nix-shell to enter the development shell:

$ git clone https://github.com/tiiuae/sbomnix
$ cd sbomnix
$ nix-shell

Keep in mind this doesn't add the various entrypoint binaries to your PATH directly. They are produced during the setuptools build.

While you're in the devshell, you can run various command line tools via the entrypoint files directly:

# sbomnix:
$ src/sbomnix/main.py --help

# nixgraph:
$ src/nixgraph/main.py --help

# nixmeta:
$ src/nixmeta/main.py --help

# vulnxscan:
$ src/vulnxscan/vulnxscan_cli.py --help

# repology_cli:
$ src/repology/repology_cli.py --help

# repology_cve:
$ src/repology/repology_cve.py --help

# nix_outdated:
$ src/nixupdate/nix_outdated.py --help

# provenance:
$ src/provenance/main.py --help

Buildtime vs Runtime Dependencies

Buildtime Dependencies

Closure of a nix store path is a list of all the dependent store paths, recursively, referenced by the target store path. For a package, the closure of it's derivation lists all the buildtime dependencies. As an example, for a simple C program, the buildtime dependencies include packages to bootstrap gcc, stdenv, glibc, bash, etc. on the target architecture. Even a simple hello-world C program typically includes over 150 packages in its list of buildtime dependencies. It's important to note that generating buildtime dependencies in Nix does not require building the target.

For reference, following is a link to graph from an example hello-world C program that includes the first two layers of buildtime dependencies: direct dependencies and the first level of transitive dependencies: C hello-world buildtime, depth=2.

Runtime Dependencies

Runtime dependencies are a subset of buildtime dependencies. Nix automatically determines the runtime dependencies by scanning the generated output paths (i.e. build output) for the buildtime dependencies' store paths. This means nix needs to build the target output first, before runtime dependencies can be determined. For reference, below is a complete runtime dependency graph of an example hello-world C program:

By default, where applicable, the tools in this repository assume runtime dependencies. This means, for instance, that unless specified otherwise, sbomnix will output an SBOM including runtime-only dependencies, nixgraph outputs runtime dependency graph, and vulnxscan and nix_outdated scan runtime dependencies. Since Nix needs to build the target output to determine the runtime dependencies, all the tools in this repository will also build (force-realise) the target output as part of each tool's invocation when determining the runtime dependencies. All the mentioned tools in this repository also support working with buildtime dependencies instead of runtime dependencies with the help of --buildtime command line argument. As mentioned earlier, generating buildtime dependencies in Nix does not require building the target. Similarly, when --buildtime is specified, the tools in this repository do not need to be build the given target.

Usage Examples

The usage examples work for both the built package, as well as inside the devshell.

Keep in mind inside the devshell, calls to sbomnix need to be replaced with src/sbomnix/main.py (and similar for other entrypoints).

In the below examples, we use Nix package wget as an example target. To print wget out-path on your local system, try:

$ nix eval -f '<nixpkgs>' 'wget.outPath'
"/nix/store/8nbv1drmvh588pwiwsxa47iprzlgwx6j-wget-1.21.3"

Generate SBOM Based on Derivation File or Out-path

By default sbomnix scans the given target and generates an SBOM including the runtime dependencies. Notice: determining the target runtime dependencies in Nix requires building the target.

# Target can be specified with flakeref too, e.g.:
# sbomnix .
# sbomnix github:tiiuae/sbomnix
# sbomnix nixpkgs#wget
# Ref: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#flake-references
$ sbomnix /nix/store/8nbv1drmvh588pwiwsxa47iprzlgwx6j-wget-1.21.3
...
INFO     Wrote: sbom.cdx.json
INFO     Wrote: sbom.spdx.json
INFO     Wrote: sbom.csv

Main outputs are the SBOM json files sbom.cdx.json and sbom.spdx.json in CycloneDX and SPDX formats.

Generate SBOM Including Buildtime Dependencies

By default sbomnix scans the given target for runtime dependencies. You can tell sbomnix to determine the buildtime dependencies using the --buildtime argument. Below example generates SBOM including buildtime dependencies. Notice: as opposed to runtime dependencies, determining the buildtime dependencies does not require building the target.

$ sbomnix /nix/store/8nbv1drmvh588pwiwsxa47iprzlgwx6j-wget-1.21.3 --buildtime

Generate SBOM Based on Result Symlink

sbomnix can be used with output paths too (e.g. anything which produces a result symlink):

$ sbomnix /path/to/result 

Generate SBOM Based on Flake Reference

sbomnix also supports scanning flake references:

$ sbomnix github:NixOS/nixpkgs?ref=nixos-unstable#wget --buildtime

Visualize Package Dependencies

sbomnix finds the package dependencies using nixgraph. Moreover, nixgraph can also be used as a stand-alone tool for visualizing package dependencies. Below, we show an example of visualizing package wget runtime dependencies:

$ nixgraph /nix/store/8nbv1drmvh588pwiwsxa47iprzlgwx6j-wget-1.21.3 --depth=2

Which outputs the dependency graph as an image (with maxdepth 2):

For more examples on querying and visualizing the package dependencies, see: nixgraph.

Contribute

Any pull requests, questions and error reports are welcome. To start development, we recommend using Nix flakes development shell:

$ git clone https://github.com/tiiuae/sbomnix
$ cd sbomnix/
# Optionally, install git hooks to check the git commit message
$ ./githooks/install-git-hooks.sh
$ nix develop

Run make help to see the list of supported make targets. Prior to sending any pull requests, make sure at least the make pre-push runs without failures.

To deactivate the Nix devshell, run exit in your shell. To see other Nix flake targets, run nix flake show.

License

This project is licensed under the Apache-2.0 license - see the Apache-2.0.txt file for details.

Acknowledgements

sbomnix uses Nix store derivation scanner (nix.py and derivation.py) originally from vulnix.