Software Bill Of Materials

View Source

Software Bill-of-Materials (SBOM)

A Software Bill-of-Materials (SBOM) is a document to share information about the software used, dependencies and, essentially, what the software is made of. SBOMs have many different use cases, and some examples include verification of licenses, or vulnerability scanning using databases such as CVE and OSV, among others.

Erlang/OTP has multiple third-party dependencies. Some are vendored into the source code of Erlang/OTP:

  • pcre2 (erts/emulator/pcre)
  • zlib (erts/emulator/zlib)
  • asmjit (erts/emulator/asmjit)
  • openssl (erts/emulator/openssl)
  • zstd (erts/emulator/zstd)
  • others

The Erlang/OTP project provides source SBOMs starting with OTP 28. Below we detail structure of the source SBOM.

Source SBOM Structure And General Understanding

Erlang/OTP publishes a source SBOM for Erlang/OTP using SPDX v2.2 format. The source SBOM can be seen as a tree data structure.

  • root: the root of the tree is found under the key documentDescribes. The value is a single item that points to the SPDX package that represents the root node from where all packages converge. This root node contains mostly configuration files that do not belong to Erlang/OTP applications nor runtime applications. All SPDX packages (Erlang/OTP apps and runtime, explained later) are under the packages key in the SPDX document.

    {
      "SPDXID": "SPDXRef-DOCUMENT",
      "creationInfo": { ... },
      "dataLicense": "CC0-1.0",
      "documentDescribes": [ "SPDXRef-Project-OTP" ],     <----- ROOT NODE
      ...,
    
      "name": "Erlang/OTP",
        "packages": [
          {
            "SPDXID": "SPDXRef-Project-OTP",              <----- DESCRIPTION OF ROOT NODE
            "downloadLocation": "https://github.com/erlang/otp/releases",
            "externalRefs": [ { "comment": "",
                                "referenceCategory": "PACKAGE-MANAGER",
                                "referenceLocator": "pkg:github/erlang/otp@28.0.1",
                                "referenceType": "purl"
                              } ],
          "filesAnalyzed": true,
          "hasFiles": [ "SPDXRef-File-1", "SPDXRef-File-2", ...]  <----- FILES IN ROOT NODE
          },
          ...      <----- OTHER PACKAGES LIKE ERTS
        ]
    }
  • First level branches from root represent Erlang/OTP applications, the runtime system (erts), and some vendor build scripts (SPDXRef-otp-make-install-sh). As an example, we show below the erts package.

    {
      "SPDXID": "SPDXRef-otp-erts",
      "downloadLocation": "https://github.com/erlang/otp/releases",
      "externalRefs": [ { "comment": "Erlang Runtime System",
                          "referenceCategory": "PACKAGE-MANAGER",
                          "referenceLocator": "pkg:otp/erts@16.0.1?vcs_url=git+https://github.com/erlang/otp.git",
                          "referenceType": "purl"}],
      ...
      "filesAnalyzed": true,
      "hasFiles": [ "SPDXRef-File-380", ...],
      "name": "erts",
      "packageVerificationCode": { "packageVerificationCodeValue": "2568c51ee8756f36b6173037035ca4f77ed0d00b" },
      "supplier": "Organization: Ericsson AB",
      "versionInfo": "16.0.1"
    },
  • All Erlang/OTP application SPDX packages are named with the prefix SPDXRef-otp-<appname>. <appname> represents the name of an Erlang application, where the value is the name of the Erlang application with the underscores _ dropped, e.g., common_test becomes commontest.

  • Application packages have at least two sub packages. One for tests and one for docs.

    The documentation and the tests packages add a suffix to the SPDXRef-otp-<appname>, namely documentation and test. We use stdlib as a running example to explain the package structure in the SPDX SBOM, where Erlang/OTP applications:

    • SPDXRef-otp-stdlib-documentation contains all documentation about stdlib, and
    • SPDXRef-otp-stdlib-test contains all tests about stdlib, and SPDXRef-otp-stdlib contains the source code of the stdlib application.
    {
        "SPDXID": "SPDXRef-otp-stdlib",                                   <------- stdlib PACKAGE
        "downloadLocation": "https://github.com/erlang/otp/releases",
        "externalRefs": [...],
        "filesAnalyzed": true,
        "hasFiles": [
          "SPDXRef-File-9022",
          "SPDXRef-File-9023",
          ...
        ],
        "name": "stdlib",
        "packageVerificationCode": { "packageVerificationCodeValue": "29200c1cd7da4a5c015cdafd6f71db538ae0a1c9" },
        "supplier": "Organization: Ericsson AB",
        "versionInfo": "7.0.2"
    },
    {
        "SPDXID": "SPDXRef-otp-stdlib-documentation",                         <------- stdlib DOCUMENTATION PACKAGE
        ...
        "name": "stdlib-documentation",
        "packageVerificationCode": { "packageVerificationCodeValue": "ad443de0ca77bf6cbadc35813e0807494949f25c" },
        "supplier": "Organization: Ericsson AB",
        "versionInfo": "7.0.2"
    },
    {
        "SPDXID": "SPDXRef-otp-stdlib-test",                                   <------- stdlib TEST PACKAGE
        ...
    }
  • Application packages have the following fields:

    • name which represents the Erlang/OTP application name, e.g., stdlib, erts, etc, and/or the application name with the suffix documentation or test, e.g., stdlib-test and stdlib-documentation.
    • copyrightText includes the copyright of all the files under the given package.
    • downloadLocation specifies where the package can be downloaded from.
    • versionInfo specifies the version of the application, which in case of documentation or test packages, it refers to the top-level application. For example, the stdlib package has versionInfo equals to 7.0.2 and its corresponding stdlib-documentation and stdlib-test packages will have the same versionInfo, as this is the version of the package.
    • licenseInfoFromFiles contains the list of licenses found in the files that belong to the given package.
    • for other clarications, please check the SPDX 2.2 standard.
  • The application package, application test package, and the application documentation package may all in turn contain one or more vendor packages. An example of this is the package SPDXRef-otp-erts who contains other packages, such as SPDXRef-otp-erts-asmjit.

  • To remove non-needed applications from your SBOM, remove the first level packages (Erlang/OTP applications) that are not needed, including all of their transitive dependencies (other packages reachable from them), as well as all files reachable from these packages. For example, to remove the application ftp, one must remove the package SPDXRef-otp-ftp, SPDXRef-otp-ftp-documentation, and SPDXRef-otp-ftp-test, and all the files that they reference (including also relationship items). In most ocassions, you may want to remove the first level Erlang/OTP applications and the keep first level vendor dependencies (identified by comment "vendor package" in the SPDX package). The reason for keeping the first level vendor dependencies is that those include Erlang/OTP building scripts.

Below we show how the stdlib packages are linked between them and against the root package, "SPDXRef-Project-OTP". In this particular case, stdlib does not have any more relationships. But Erlang/OTP applications have dependencies in their app.src file and these are also captured in the source SBOM in the relationships field. If you remove packages, you need to remove relationships that do not exist anymore.

  {
    "SPDXID": "SPDXRef-DOCUMENT",
    "creationInfo": { ... },
    "dataLicense": "CC0-1.0",
    "documentDescribes": [ "SPDXRef-Project-OTP" ],     <----- ROOT NODE
    ...,

    "name": "Erlang/OTP",
    "packages": [ ... ],
    "relationships": [                                  <----- RELATIONSHIPS, OR, HOW EVERYTHING FITS TOGETHER
       {
         "relatedSpdxElement": "SPDXRef-otp-stdlib",
         "relationshipType": "TEST_OF",                 <----- THESE ARE TESTS
         "spdxElementId": "SPDXRef-otp-stdlib-test"
       },
       {
         "relatedSpdxElement": "SPDXRef-otp-stdlib",
         "relationshipType": "DOCUMENTATION_OF",        <----- THESE ARE DOCUMENTS, EXAMPLES, ETC
         "spdxElementId": "SPDXRef-otp-stdlib-documentation"
       },
       {
         "relatedSpdxElement": "SPDXRef-Project-OTP",
         "relationshipType": "PACKAGE_OF",              <------ THIS SPECIFIES THAT stdlib IS PART OF PROJECT-OTP
         "spdxElementId": "SPDXRef-otp-stdlib"
       },
       ...
    ]
  }

Verification Of Source SBOM

In each release, Erlang/OTP releases a source SBOM together with a signed SBOM attestation artifact. This gives users the ability to verify the signed artefact.

Below we show how to do this for Erlang/OTP version 28.0.2 using Sigstore cosign (installation) and/or Github gh tools (installation).

Sigstore cosign

  1. Download the SBOM for 28.0.2, named bom.spdx.json (here)

  2. Download the sigstore file, bom.spdx.json.sigstore (here)

  3. Run cosign with the following parameters

    cosign verify-blob-attestation \
       --bundle "bom.spdx.json.sigstore" \
       --new-bundle-format \
       --type "https://spdx.dev/Document/v2.2" \
       --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
       --certificate-identity "https://github.com/erlang/otp/.github/workflows/main.yaml@refs/tags/OTP-28.0.2" \
       "bom.spdx.json"
    

Github CLI gh

  1. Download the SBOM for 28.0.2, named bom.spdx.json (here)

  2. Run gh with the following parameters

    gh attestation verify \
       --predicate-type "https://spdx.dev/Document/v2.2" \
       --repo "erlang/otp" \
       --source-ref "refs/tags/OTP-28.0.2" \
       "bom.spdx.json"