Skip to main content

What can you do with an enriched SBOM? A parlay quickstart guide

Written by

June 7, 2023

0 mins read

We just released parlay, a new open source tool that can enrich SBOMs with additional information. You can read more in the announcement blog post. In that post, we briefly mentioned why this is important for decision-making based on SBOM data, but thought a few quick examples might be interesting.

parlay can add a lot of extra information to an SBOM, and we can use that information to write more powerful policies. While lines of JSON aren’t a perfect barometer, you can see in this particular example that the SBOM grew by more than 400% — which opens up lots of use cases.

$ snyk sbom --format cyclonedx1.4+json | jq | wc -l
    262
$ snyk sbom --format cyclonedx1.4+json | parlay e enrich - | jq | wc -l
    1169

License policies

An SBOM with just the minimum elements won’t have information about the licenses that apply to the included packages. Let’s use Parlay and the Ecosyste.ms data to add that information.

snyk sbom --format cyclonedx1.4+json | parlay e enrich -

Now that we have a richer SBOM, we can write more powerful policies. For this example, we’ll use Open Policy Agent and its powerful Rego programming language. Here we create two lists of policies, one to deny and one to warn on.

package main

name = input.metadata.component.name

licenses_to_deny = [
  "LGPL",
]

licenses_to_warn = [
  "MIT"
]

deny[msg] {
  component := input.components[_]
  expression := component.licenses[_].expression
  startswith(expression, licenses_to_deny[_])
  msg := sprintf("%s is using %s which is licensed with an %s license", [name, component.name, expression])
}

warn[msg] {
    component := input.components[_]
    expression := component.licenses[_].expression
    startswith(expression, licenses_to_warn[_])
    msg := sprintf("%s is using %s which is licensed with an %s license", [name, component.name, expression])
}

Let’s continue our love of Unix pipes and use Conftest to apply that policy to the enriched SBOM.

snyk sbom --format cyclonedx1.4+json | parlay e enrich - | conftest test -

In the example project, this issued a series of warnings about certain packages using indicated licenses.

WARN - - main - snykit is using diff-lcs which is licensed with an MIT license
WARN - - main - snykit is using mustermann which is licensed with an MIT license
WARN - - main - snykit is using nio4r which is licensed with an MIT license
WARN - - main - snykit is using puma-metrics which is licensed with an MIT license
WARN - - main - snykit is using rack which is licensed with an MIT license
WARN - - main - snykit is using rack-protection which is licensed with an MIT license
WARN - - main - snykit is using rack-test which is licensed with an MIT license
WARN - - main - snykit is using rake which is licensed with an MIT license
WARN - - main - snykit is using rspec which is licensed with an MIT license
WARN - - main - snykit is using rspec-core which is licensed with an MIT license
WARN - - main - snykit is using rspec-expectations which is licensed with an MIT license
WARN - - main - snykit is using rspec-mocks which is licensed with an MIT license
WARN - - main - snykit is using rspec-support which is licensed with an MIT license
WARN - - main - snykit is using sinatra which is licensed with an MIT license
WARN - - main - snykit is using tilt which is licensed with an MIT license

16 tests, 1 passed, 15 warnings, 0 failures, 0 exceptions

Vulnerability policies

Let’s try something a bit more complicated. Let’s write a policy to flag vulnerabilities over a certain CVSS score in direct dependencies. This is a good example of when you need package data, dependency data, and vulnerability data.

deny[msg] {
  vulnerability := input.vulnerabilities[_]
  rating := vulnerability.ratings[_]
  rating.source.name == "Snyk"
  rating.score > max_cvss_score
  component := input.components[_]
  bom_ref = vulnerability["bom-ref"]
  component["bom-ref"] == bom_ref

  root = input.metadata.component["bom-ref"]

  dependency := input.dependencies[_]
  dependency.ref == root

  direct := dependency.dependsOn[_]
  direct == bom_ref

  msg := sprintf("%s version %s has a vulnerability with a CVSS score of %s", [component.name, component.version, round(rating.score)])
}

round(n) = f {
  f := sprintf("%.2f", [n])
  contains(f, ".") # Ensure that it was a float
}

round(n) = f {
  not contains(sprintf("%.2f", [n]), ".") # Test if it wasn't a float
  f := sprintf("%v.00", [n]) # Fudge the decimals for integer values
}

We can then generate or take our SBOM, enrich it using the Snyk vulnerability data, and then apply our policy using Conftest.

snyk sbom --format cyclonedx1.4+json | parlay s enrich - | conftest test -
FAIL - - main - puma version 4.2.1 has a vulnerability with a CVSS score of 9.10

3 tests, 2 passed, 0 warnings, 1 failure, 0 exceptions

Here we see it reporting on one vulnerability that matched our criteria.

Remember, this is just an example. The Rego language used by Open Policy Agent can be used to describe complex logic if required. And if you prefer another policy tool, that should work too.

Who is the author of this software?

One last quick example. The following example uses parlay to enrich our SBOM, and then uses jq and awk to output some useful information. In this case, we’re pulling out the named author of each package. Again, this information is often not in an SBOM with the minimum elements.

$ snyk sbom --format cyclonedx1.4+json | parlay e enrich - | jq -r '(.components[] | [.name, .author]) | @tsv' | awk '{print $1; $1=""; print}'
nio4r
 Socketry
puma
 Puma
prometheus-client
 Prometheus
puma-metrics

rack
 Official Rack repositories
rack-test
 Official Rack repositories
rake
 The Ruby Programming Language
rspec-support
 RSpec
rspec-core
 RSpec
diff-lcs
 Austin Ziegler
rspec-expectations
 RSpec
rspec-mocks
 RSpec
rspec
 RSpec
ruby2_keywords
 The Ruby Programming Language
mustermann
 Sinatra
rack-protection
 Sinatra
tilt
 Jeremy Evans
sinatra
 Sinatra

There are lots more things you can do with richer SBOM data, and lots more data that would be useful to enrich SBOMs with as well. Let us know what you’d like to see, and please share examples of your experiments with parlay.