SBoMs, cheaper, faster, more accurate, better
Written by Niklas Düster and Spyros Gasteratos
In this post we provide a brief introduction on what is an SBOM, we provide detailed steps on how to produce an SBOM for any repository, how to enrich its information and lastly how to visualize the result. All, with free, Open Source software, automated, massively scalable and without any code.
Software Bill of Materials (SBOM) provide transparency that is an important building block for identifying risk in the software supply chain.
An SBOM is a list of all the components in a piece of software, including their dependencies, and relationships between them. It optionally also contains the licenses that govern those components. An accurate and up-to-date SBOM enables security teams to detect outdated or vulnerable components, assists compliance with identifying unacceptable licensing, and overall helps organizations to gain visibility into what they consume, as well as how and what they ship.
SBOMs are becoming increasingly more prominent in software requirements, with many governments and organizations mandating their use, and providing relevant guidelines.
As a response there is a growing number of enterprise and open source tools that allow generation and management of SBOMs such as Trivy, OWASP Dependency-Track, Syft, Tern, Mend (formerly Whitesource), FOSSA and many others.
In Ocurity we need to monitor our software for outdated libraries, vulnerable components and unacceptable licenses. We also need this information readily available, updated automatically and of course easy to digest. Luckily we have Dracon, a very powerful security workflow engine which allows us to seamlessly unify any security tooling.
In this post we demonstrate how Dracon makes creation and maintenance of high quality SBOMs trivial for any organization size.
A real-world scenario would be the following:
A large organization with many interdependent projects needs to generate SBOMs for all of its projects. The SBOMs need to be accurate and high quality. Quality needs to be measurable and the SBOMs need to be stored and visualized in an external system.
We can accurately simulate such a scenario by generating SBOMs for a large Github organization with many repositories in multiple states of maintenance but also high security requirements.
The Kubernetes Github organization is acceptable for this.
There are many tools that can generate SBOMs.
As an example, Trivy supports the creation of SBOMs from a filesystem and can produce output in the popular SBOM format CycloneDX.
OWASP Dependency-Track can ingest CycloneDX documents, and store and visualize the information within them for easy observability and management. As a convenient addition, it also monitors vulnerability databases (e.g. NVD and OSV), and notifies us in case it identified vulnerable components in our portfolio.
Dracon as a security workflow management solution can orchestrate all these tools and its API supports CycloneDX information.
Ebay’s sbom-scorecard checks SBOMs for completeness and provides a numerical score.
We will use it as a way to determine if we did a good job.
First, we need to know the baseline SBOM score we can get from different tools.
We clone a random repository from our Github organization, (kubernetes/kops) and run Trivy against it.
docker run -v $(pwd):/code -ti aquasec/Trivy filesystem -f cyclonedx /code >sbom.json
This produces an sbom file which can then be read by our scorecard solution as such.
$ docker run -v $(pwd):/code /sbom-scorecard score /code/sbom.json Guessed: cdx 424 total packages 99% have versions. 0% have licenses. 0% have package digest. 99% have purls. 0% have CPEs. Has creation info? true Spec valid? true == Spec Compliance: 25/25 Package ID: 9/20 (99% have purls and 0% have CPEs) Package Versions: 19/20 Package Licenses: 0/20 Creation Info: 15/15 Total points: 69/100 or 69%
The score is 69% which is acceptable, we have package information and package URLs, but we lack licensing, digests and CPEs. The information we get from Trivy will serve our purpose of basic vulnerability analysis and package evaluation use cases.
Similarly, running Syft against the same repository provides us with a valid SBOM document, which however is sadly unrecognizable by sbom-scorecard.
Guessed: spdx 0 total packages 0 total files 0% have licenses. 0% have package digest. 0% have package versions. 0% have purls. 0% have CPEs. 0% have file digest. Spec valid? true Has creation info? false == Spec Compliance: 25/25 Package ID: 0/20 (No packages) Package Versions: 0/20 (No packages) Package Licenses: 0/20 (No packages) Creation Info: 0/15 (No creation info found) Total points: 25/100 or 25%
Therefore in this particular case we will proceed using Trivy.
Having selected our tooling, we can then upload this information to Dependency Track via it’s GUI.
First we create a project
Then we select our project, click on the components tab and click on ‘Upload BOM’
After Dependency Track has processed our BOM we get more information such as potential vulnerabilities and a risk score but also we can visualize all of the components of the repository.
We can also download our enriched BOM and see our new score.
$ docker run -v $(pwd):/code /sbom-scorecard score ./kops-bom-dt.json Guessed: cdx 424 total packages 99% have versions. 0% have licenses. 0% have package digest. 99% have purls. 0% have CPEs. Has creation info? true Spec valid? true == Spec Compliance: 25/25 Package ID: 9/20 (99% have purls and 0% have CPEs) Package Versions: 19/20 Package Licenses: 0/20 Creation Info: 15/15 Total points: 69/100 or 69%
No change. This is because
sbom-scorecard does not provide a score for vulnerability information. Being able to communicate vulnerability information by embedding it into the SBOM is useful, but in this case it doesn’t help us in increasing our score.
Let’s see if we can raise the score before we submit the SBOM to Dependency-Track, to provide it with even more and better data to perform analyses on!
An easy win which also provides immediate value to us is adding Licensing information to our BOM.
We can do so by using Dracon’s deps.dev enricher which queries Google’s OpenSource insights dataset for licensing information on a package.
We can then visualize our results using Dracon’s Dependency Track Consumer.
To do so, we need a Pipeline that runs Trivy, enriches its results with information from OpenSource insights and finally puts the results into Dependency Track:
--- apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization nameSuffix: -sbom-project namespace: dracon resources: - https://github.com/ocurity/dracon//components/base/ components: - https://github.com/ocurity/dracon//components/sources/git/ - https://github.com/ocurity/dracon//components/producers/aggregator/ - https://github.com/ocurity/dracon//components/producers/docker-Trivy - https://github.com/ocurity/dracon//components/enrichers/aggregator/ - https://github.com/ocurity/dracon//components/enrichers/depsdev/ - https://github.com/ocurity/dracon//components/consumers/dependency-track
We can install this pipeline with the following command:
kubectl -n dracon apply -k .
We also need to configure these components, we can do so with a pipelinerun.yaml
--- apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: generateName: dracon-sbom-project- namespace: dracon spec: pipelineRef: name: dracon-sbom-project params: - name: repository_url value: $repo-url - name: consumer-dependency-track-api-url value: http://localhost:7070 - name: consumer-dependency-track-project-name value: "$project-name" - name: consumer-dependency-track-project-uuid value: "$project-uuid" - name: consumer-dependency-track-project-version value: "v0.0.1" - name: consumer-dependency-track-token value: "$dt-token" - name: producer-docker-Trivy-format value: "cyclonedx" - name: producer-docker-Trivy-command value: "fs" workspaces: - name: source-code-ws subPath: source-code volumeClaimTemplate: spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
We can start this pipeline run with the following command:
kubectl -n dracon create -f pipelinerun.yaml
Running this uploads licensing information to Dependency Track with no extra effort on our side, it is also automated and easily customized, not a bad start.
Our new score is 89%:
$ docker run -v $(pwd):/code /sbom-scorecard score /code/sbom-with-licenses.json Guessed: cdx 424 total packages 99% have versions. 98% have licenses. 0% have package digest. 99% have purls. 0% have CPEs. Has creation info? true Spec valid? true == Spec Compliance: 25/25 Package ID: 9/20 (99% have purls and 0% have CPEs) Package Versions: 19/20 Package Licenses: 19/20 Creation Info: 15/15 Total points: 89/100 or 89%
The simple Dracon pipeline we assembled throughout the course of this post is a great example of how multiple tools can collaborate in delivering high quality SBOMs to upstream Consumers like Dependency-Track. We achieved this by writing barely any code, in a declarative way that is easy to understand and maintain.
Manually running pipelines for all of our organization however is inefficient.
We can easily scale this process with the following steps:
- Use Dependency Track’s API to create a project for each of our repositories.
- Write a pipeline-run template which has variables for each parameter we need to change per repository.
- For every repository in our organization, create a templated
- Mass-execute all our pipeline runs.
We can mass-create Dependency Track projects using the script found here
Once the required dependencies have been installed, we can execute the script as such:
DT_API_KEY=<> DT_URL=<> ./create-projects.sh
This gives us a mapping of Repository Name to Dependency Track Project UUID which we can save in a file named
We can now mass-generate templated
pipelinerun.yaml files with the script found here
go run main.go -apiKey <dependency-track-api-key> -output $(pwd)/pipelineruns -resultsFile $(pwd)/result.txt -template pipelinerun.yaml.tmpl -url <dependency-track-url>
This creates several
.yaml files in the directory
We can now install and run our pipelines with
kubectl -n dracon apply -k .
and run them with:
for run in $(ls pipelineruns/); do k -n dracon create -f pipelineruns/$run; done
When the runs finish, we can see the results in Dependency Track for all our repositories.
Repeat executions can be achieved via cronjob.
The whole pipeline along with detailed instructions on recreating this for ANY github organization is in the supporting material of this post in our community-pipelines repository
A cronjob can fail for many reasons, moreover, cronjobs offer a point in time execution while SBOMs change possibly with every commit, in Ocurity, we prefer a more resilient approach.
Therefore in Dracon enterprise we use its Github Application integration to schedule the BOM pipeline to run on every single commit for every repository.
This is transparent to colleagues using Github and allows our security team immediate visibility into our current SBOMs while also allowing us to get notified automatically when a component has a CVE by
subscribing to Dependency Track notifications. This is just one example of the great workflows we are performing through Dracon, contact us to find out more.