Software Bills of Materials

Upload software bills of materials for your services, repositories, or both.

Note: This feature is currently in Beta and can be enabled by an OpsLevel Admin on the Admin Settings page.

You can upload software bills of materials (SBOMs) to OpsLevel. SBOMs serve as lists of software packages that are being used by your services and/or repositories. This enables you to create checks on the packages being used by your services, as well as those packages' versions. Currently the CycloneDX JSON v1.5 SBOM format is supported.

We suggest automating this upload and are currently working on out-of-the-box SBOM collection via our source code version management integrations (e.g. Git Integrations).

API Endpoint

To upload a software bill of materials, send a POST request to the following URL:

https://app.opslevel.com/upload/documents/sbom/<resource_id>

<resource_id> can either be the ID of the service or of the repository to which you want to attach the software bill of materials to, e.g. Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8yNDg. You can retrieve the resource ID with our GraphQL API.

Mapping

Software bill of materials files can be attached to services or to repositories in OpsLevel. Some organizations rely on monorepos that hold dozens of services, some of which are even bundled into a co-located images. Other organizations rely on multiple repositories that roll up into a single image at release time.

In order to accommodate various release strategies, OpsLevel allows uploading software bills of materials to either repositories or to services, and shows packages from a linked repository on the service associated with it.

Where to upload

If you are unsure of whether to attach the software bill of materials to the service or the repository, consider following these guidelines:

  • If the repository hosts only one service, or if the repository hosts more than one service and the generated software bill of materials does not relate to one of those services specifically (e.g. because it is shared by multiple services within the repository), attach the software bill of materials to the repository.
  • If you have access to service-level software bills of materials for multiple services inside a repository hosting more than one service, attach the software bill of materials to the service.
  • If you're generating a software bill of materials for a build of a service that contains code from two or more repositories, attach the software bill of materials to the service.

Request Headers

Set the Content-Type HTTP request header to application/json.

Additionally, the upload request must be authenticated using an API token (see the "Create an API Token" section of our GraphQL API documentation).

Set the Authorization header to Bearer <api_token> (e.g. Bearer ABfFuAsAHou3XzyVW8nmeNqK86FAZCG3OHmK).

Request Body

The HTTP request body of the upload request should conform to the CycloneDX JSON Standard in version 1.5. Various tools exist to generate software bills of materials in this format, e.g. syft.

An example body could look like the following:

{
    "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
    "bomFormat": "CycloneDX",
    "specVersion": "1.5",
    "serialNumber": "urn:uuid:3c1ed074-6751-46f7-9eb3-5279bb5c6495",
    "version": 1,
    "metadata": {
        "timestamp": "2023-12-20T08:34:16-06:00",
        "tools": [
            {
                "vendor": "anchore",  
                "name": "syft",
                "version": "0.98.0"
            }
        ],
        "component": {
            "bom-ref": "08329a07b4eb8eac",
            "type": "file",
            "name": "./"
        }
    },
    "components": [
        {
            "bom-ref": "pkg:gem/[email protected]?package-id=fdf94e8c0de5bf8d",
            "type": "library",
            "name": "zeitwerk",
            "version": "2.6.12",
            "cpe": "cpe:2.3:a:ruby-lang:zeitwerk:2.6.12:*:*:*:*:*:*:*",
            "purl": "pkg:gem/[email protected]",
            "properties": [
                {
                    "name": "syft:package:foundBy",
                    "value": "ruby-gemfile-cataloger"
                },
                {
                    "name": "syft:package:language",
                    "value": "ruby"
                },
                {
                    "name": "syft:package:type",
                    "value": "gem"
                },
                {
                    "name": "syft:cpe23",
                    "value": "cpe:2.3:a:ruby_lang:zeitwerk:2.6.12:*:*:*:*:*:*:*"
                },
                {
                    "name": "syft:cpe23",
                    "value": "cpe:2.3:a:zeitwerk:zeitwerk:2.6.12:*:*:*:*:*:*:*"
                },
                {
                    "name": "syft:cpe23",
                    "value": "cpe:2.3:a:ruby:zeitwerk:2.6.12:*:*:*:*:*:*:*"
                },
                {
                    "name": "syft:location:0:path",
                    "value": "/Gemfile.lock"
                }
            ]
        },
        {
            "bom-ref": "pkg:npm/[email protected]?package-id=9ac1a725ce574216",
            "type": "library",
            "name": "zenscroll",
            "version": "4.0.2",
            "licenses": [
                {
                    "license": {
                        "id": "Unlicense"
                    }
                }
            ],
            "cpe": "cpe:2.3:a:zenscroll:zenscroll:4.0.2:*:*:*:*:*:*:*",
            "purl": "pkg:npm/[email protected]",
            "properties": [
                {
                    "name": "syft:package:foundBy",
                    "value": "javascript-lock-cataloger"
                },
                {
                    "name": "syft:package:language",
                    "value": "javascript"
                },
                {
                    "name": "syft:package:type",
                    "value": "npm"
                },
                {
                    "name": "syft:location:0:path",
                    "value": "/yarn.lock"
                }
            ]
        }
    ]
}

Submitting the Request

Submitting the request successfully should result in an HTTP response status code of 202/accepted and the following body:

{
    "status": "OK"
}

An invalid request will produce an HTTP response status code other than 202/accepted, with a body like the following:

{
    "errors": [
        "value at `/sbom/$schema` is not one of: [\"http://cyclonedx.org/schema/bom-1.5.schema.json\"]"
    ]
}

Uploading additional software bills of materials for a particular resource (repository or service) will replace the previous SBOM. We support up to one current SBOM per service and up to one per repository.

User Interface

Once you uploaded a software bill of materials, its contents will be visible within our application on either the service page, or on the repository page, depending on what the SBOM was attached to.

Service Software Bills of Materials

Navigate to Catalog > Services > Service of Interest > Dependencies > Packages.

A table like the one below will display all the software packages that we detected the service is using. This table shows both software packages stemming from software bills of materials attached directly to the service, as well as from software bills of materials attached to the repository that the service is hosted in - indicated by the "Uploaded To" column.

Repository Software Bill of Materials

Navigate to Catalog > Repositories > Repository of Interest.

A table like the one below will display all the software packages that we detected the repository is using. This table shows only the software packages stemming from software bills of materials attached directly to the repository.


What’s Next

Create some package version checks for your services!