How We Generate a Software Bill of Materials (SBOM) with CycloneDX

By

Generating a software bill of materials (SBOM) is no small feat. In this post, I'll walk through the steps I used to generate our SBOM when Sounil Yu joined the JupiterOne team as CISO and Head of Research.

"It gave me confidence that I'm going into an organization that has modern software development practices."
- Sounil Yu

The "bill of materials" has its roots in manufacturing. It lists in hierarchical order the raw materials, sub-assemblies, intermediate assemblies, sub-components, parts, and the quantities of each needed to manufacture an end product. 

A software bill of materials is similar in nature, showing the components used to assemble the software that is under inspection. Software has evolved over the last decade from homegrown code to being assembled with open source repositories and software packages. Because we pull from different sources to deliver value, we now have a software supply chain.

 

Cybercrime Necessitates the Software Bill of Materials for Modern Software Development Practices

We've become increasingly aware of threats to the software supply chain over the last couple years, especially as the pandemic forced many companies to quickly transform and support remote work.

Cybercrimes targeting the software supply chain have become more prevalent:

  • The Solarwinds attack at the end of 2020 was notable due to how many people were impacted and the depth of access the attackers had
  • Several software providers and open source libraries were compromised
  • And at the end of 2021, Log4j ruined many holidays

Each time these compromises hit the news, every software provider experiences the stress of "am I open to this sort of attack too?" The difficulty in understanding the dependencies of software packages just adds to the collective tension in a time where breaches feel inevitable.

Many people approach various software providers and open source repositories because they fulfill a need. There's a problem they're trying to solve and the repo or software of choice fits the bill. Just like medicine treats ailments and can sometimes have adverse side effects, you also need to know whether using a particular software provider or open source package may have adverse effects on the quality or security of your application. 

However, unlike medicine which often requires all sorts of bureaucracy to reach approval for general use, organizations don't have the time to apply as rigorous of a vetting process. This is why decentralizing security-related choices, pushing authority to the teams making those micro decisions, and equipping them with frameworks to determine risk are so important to building a culture of security.

 

Using Open Source to Create Our Software Bill of Materials (SBOM)

While there is debate on what format the software bill of materials should have, the most important thing is to just pick one and use it - after all, pulling together a full view of what components make up your software is a huge feat today. And in an industry where the wins are few, celebrate the wins as they come! That way, when the next compromise comes along, you'll be able to understand your exposures more quickly than before your SBOM was created.

The three major formats of SBOMs include SPDX, CycloneDX, and SWID. We've chosen CycloneDX as our standard SBOM format at JupiterOne, so the steps that follow will show how to generate a CycloneDX SBOM and how we ingest it with JupiterOne to get a clear picture of dependencies.

 

Step 0: Setup

First things first, we need to clone our secops automation examples repo to follow along with this tutorial.

I set up aliases in my shell so that I can refer to these things by some nicer names. That way, when I refer to generate-sbom or ingest-cyclonedx or npm-inventory, I'm really delving into this GitHub repo and directly exercising some node.js files.

image1

I've picked a JupiterOne project at random as our example app to compose an SBOM - it's called Jupiter Mapper. You can use a project of your choice to follow along with these steps.

 

Step 1: Install and invoke the tool

The jupiter-mapper project utilizes NPM for package management.  Use npm install -g @cyclonedx/bom to install the cyclonedx-bom tool. If you'd like to create a CycloneDX bill of materials for a project that uses a package manager other than NPM, check out the various options available in CycloneDX GitHub Org; one of them is likely to meet your needs.

By default, the tool will generate the bill of materials in XML format. We can override that and tell it to generate a JSON format SBOM using cyclonedx-bom -o sbom.json

Additionally, by default, CycloneDX will not include developer dependencies. If you want to include those, you can specify that by adding -d to the end of the command above.

In light of the cryptomining attacks we've seen, having developer dependencies helps us understand if there are developer machines that may have been compromised as a result of an NPM package being compromised.

As we drill down into how this affects risk analysis, there are two important distinctions to make:

  • Development vs Production
  • A developer dependency is typically used to build, test, and format code, not run the code itself. Therefore it affects developer laptops, and likely containers in your development or test environments, but typically not production environments or containers. In distinction to that, production dependencies are definitely going to affect your production containers and production environments.
  • Direct vs Transitive
  • What dependencies did a developer intentionally add to a project at a high level? Those are direct. Transitive are the dependencies of that dependency, often many layers deep. The transitive dependency graph, that chain of dependencies, can become quite complex. It is the reason why we are faced with a lot of the pain that we have with these software supply chain issues.
  • Note: In theory, CycloneDX supports ingesting dependency graph information like the transitive dependency relationships mentioned here. However, with the complexity of relationships, it's challenging to reach a clean CycloneDX representation of the dependencies, so in practice we use JupiterOne to organize the relationships.

 

Step 2: Pull dependency distinctions out of the CycloneDX file

We'll be using JupiterOne to ingest the CycloneDX file, but because the file doesn't maintain which dependencies are direct vs transitive or dev vs prod, and these distinctions are quite useful, we'll need to add our own annotations.

The package.json file of this NPM project is a JSON structure that contains devDependencies and dependency arrays that represent the distinctions we want to make. These arrays are conveniently extracted with the jq tool and redirected to an output file. We'll use these output files as input parameters for our ingestion script.

jq '.devDependencies | keys' package.json > devDeps.json

 

jq '.dependencies | keys' package.json > directDeps.json

 

 

Step 3: Ingest CycloneDX SBOM into JupiterOne

I know I said what you're thinking.

"Why is he telling us to use his company's product?"

Because relationship mapping and filtering is so much easier with JupiterOne!

The ingestion script is going to require two arguments. One is the SBOM and a path to our SBOM file that CycloneDX has generated for us. The other required argument is the repo, and this is going to specify the name of the code repository that we're going to ingest against. We'll also include the two optional arguments for our dev dependencies and direct dependencies.

Note: For the purposes of this ingestion script, the repo you ingest against must already exist in the JupiterOne graph database, so make sure you integrate your source code management (SCM) tool of choice - GitHub, Bitbucket, GitLab, etc.

The full command we'll use here for our example:

ingest-cyclonedx –sbom ./sbom.json –repo jupiter-mapper –devDeps ./devDeps.json –directDeps ./directDeps.json

 

Alternatively, if you're also using an NPM project, and you don't want to perform Steps 1 and 2 above, you can invoke the npm-inventory script. This only ingests direct dependencies by default, but is otherwise an equivalent method to ingesting NPM dependency data into the JupiterOne graph database.

 

Step 4: Pull the data out of the JupiterOne graph database

A quick aside on the JupiterOne data model: JupiterOne, like other graph databases, is looking at relationships between entities. In this particular case, we'll look at the CodeRepo -USES-> CodeModules relationship since this USES relationship will carry the distinctions of dev vs production and direct vs transitive dependencies.

UPDATE: In an attempt to simplify this process, we've added a button to our UI that makes it easier to export your data. Simply click "Generate SBOM" when looking at your Asset Inventory and follow the prompts to create your Software Bill of Materials.

Generate_SBOM_Button_JupiterOne

 

Using the script: 

You can still use this script to generate your SBOM. To do so, we use our alias generate-sbom to initiate a query of the JupiterOne graph database to Find CodeModule THAT USES CodeRepo.

You can filter the query to only show direct dependencies that are production, as we do in our public-facing SBOM:

generate-sbom "Find npm_package THAT USES as u CodeRepo WHERE u.directDependencies=true and u.devDependencies=false"

 

Furthermore, if you annotate your code repos in GitHub with tagging information, you can adjust the query to build a tag-specific SBOM.

generate-sbom "Find CodeModule THAT USES CodeRepo WITH tag.Component='<insert tag name>'"

 

 

Bonus Output from Ingesting CycloneDX SBOM Data with JupiterOne

If we were hit with a critical package vulnerability, we would want to know the blast radius. I can use a saved query, like this:

{% set packageName_string = "{{packageName}}" %} {% set packageVersion_string = "{{packageVersion}}" %}Find User as u
that OPENED PR with createdOn >= (date.now - 5 days) as pr
that HAS CodeRepo as r
that USES CodeModule with name='{{packageName_string|escape_jinjava}}' and version='{{packageVersion_string|escape_jinjava}}'

return u.name as User, r.name as Repo, pr.createdOn as OpenedDate, pr.webLink as PR ORDER BY OpenedDate DESC

 

Note: this makes use of J1QL's variable placeholder feature to prompt for the packageName and packageVersion values.

Just supply these inputs and immediately surface a shortlist to investigate for remediation purposes – repos that are affected, PRs opened in the last few days, their authors, and links to those PRs.

Bottom line: SBOMs don't eliminate risks, but they do help us manage them better, especially as we explore beyond direct dependencies.

Erich Smith
Erich Smith

Erich is the Principal Security Engineer at JupiterOne. An industry veteran of 20+ years, his background includes roles in software development, security, devops, systems administration, and compliance automation.

To hear more from Erich, get our newsletter. No spam, just the good stuff once or twice a month. Sign up below.

Keep Reading

What’s new in JupiterOne: Reducing time to value with the new Query Builder (Part 2)
February 6, 2023
Blog
What’s new in JupiterOne: Reducing time to value with the new Query Builder (Part 2)

The new JupiterOne Query Builder streamlines your querying experience by eliminating errors, simplifying query builds, and reducing time to value.

The top 10 questions that every engineering leader should be able to answer
February 2, 2023
Blog
The top 10 questions that every engineering leader should be able to answer

We polled some of our engineering leaders to see what it takes to succeed. In part two, we see if their answers align with the CISOs we talked to.

Identify compromised versions of Github using JupiterOne
January 31, 2023
Blog
Identify compromised versions of GitHub apps using JupiterOne

As a preventative measure, Github will be deprecating the Mac and Windows signing certificates used to sign Desktop app versions 3.0.2-3.1.2 and Atom versions 1.63.0-

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.