Skip to main content
Version: 1.12.x

SBOM Validation Overview

note

The minimum supported CycloneDX version for SBOM validation is CycloneDX 1.5.

The hopctl validate sbom command (introduced in Hoppr v1.11.0) is used to validate that an SBOM:

  • is using the current latest CycloneDX specification version
  • is populated with the minimum fields recommended by NTIA
  • has valid license data with no expired or soon-to-expire licenses

The command can be configured to simply emit warnings or optionally return a failing exit code using various strictness flags when missing fields or invalid values are encountered.

Command Line Options

 Usage: hopctl validate sbom [OPTIONS]

Validate SBOM file(s)

| Options | Flag | Type | Description |
| ---------------------------- | ---- | --------- | ----------------------------------------------------------------------------------- |
| --sbom | -s | FILE | Path to SBOM file (can be specified multiple times) |
| --sbom-dir | -d | DIRECTORY | Directory containing SBOM files (can be specified multiple times) |
| --strict | -S | | Enable all strict validation options |
| --strict-ntia-minimum-fields | -n | | Raise error if minimum fields recommended by NTIA are not set |
| --strict-license-fields | -l | | Raise error if SBOM license or SBOM/component license expiration fields are not set |
| --expiration-days | -d | FLOAT | Number of days allowed by license expiration check |
| --output-format | -f | [json] | Format for output file [default: json] |
| --output-file | -o | FILE | Path to output file |
| --help | -h | | Show this message and exit. |

| Experimental | Flag | Type | Description |
| --------------------------------- | ------------------------------ | ------------------------------- | ------------------------------------------------------------------------------ |
| --profile | -p | [strict|licensing|ntia|default] | Profile of configuration presets to apply (supersedes --strict* flags) │
│ --config | -c | FILE | Path to a validation config file [env var: HOPPR_CONFIG] |

Checks

note

Components of type operating-system are not evaluated since they often don't contain the required fields.

The following checks will be performed on the SBOM document and its metadata:

  • Is this SBOM using the latest CycloneDX specification version?
  • Does the SBOM specify the metadata field?
    • Does the SBOM metadata specify one of the authors or tools fields?
    • Does the SBOM metadata specify the timestamp field?

The following checks will be performed on each component in the SBOM's components array:

  • Does the component specify the supplier field?
  • Does the component specify the name field?
  • Does the component specify the version field?
  • Does the component specify one of the unique identifier fields cpe, purl, or swid?

License Fields

The following checks will be performed on the SBOM's metadata field and each component in the components array:

  • Does the metadata or component specify the licenses field?

The following checks will be performed on each license in the metadata or component licenses array:

  • Does the license specify one of the id or name fields?
  • Does the license specify the licensing field?

License Expiration

The following checks will be performed on each license in the metadata or component licenses array:

  • If present, does the licensing field specify the expiration field?
  • Is the date in the expiration field in the past (expired), or within the minimum allowable number of days in the future (about to expire)?
note

The minimum allowable number of days for a license's expiration to pass validation is configured via the --expiration-days option (default: 30)

Strictness Flags

The command line options --strict, --strict-ntia-minimum-fields, and --strict-license-fields control whether a validation failure will be displayed as a warning or a failure, as well as whether to return a failing exit code of 1 upon completion.

Validating with no strictness options specified (warn only) will still report validation errors, but will be logged in the log file at the WARN log level, displayed with a warning symbol in the result summary, and return an exit code of 0.

Experimental

SBOM validation has been updated in version 1.12 to allow for the customization of check severity.

Experiemental validation works as a set of profiles in place of the existing strictness flags.

Profile

  • strict - collection of licensing and ntia check failures are treated as errors
  • licensing - licensing check failures are treated as errors
  • ntia - NTIA checks failures are treated as errors
  • default - existance checks are treated as warnings

Configuration

The supplied configuration file for sbom validation is what allows for the customization of how the check results are interpreted. It will be merged with the selected profile (or default) and take priority over any shared existing keys.

Each check listed will accept a value of either ignore, warn, or error to configure desired response behavior on failure.

An example of the strict check override configuration. The other built in profiles can be found in the hoppr project here.

---
sbom-checks:
components-field: warn
unique-id: warn
vulnerabilities-field: warn

metadata-checks:
licenses:
name-or-id: error
last-renewal: warn
purchase-order: warn
license-types: error
licenses-field: error

authors: error
timestamp: error

component-checks:
licenses:
name-or-id: error
license-types: error
licenses-field: error

name-field: error
supplier-field: error
version-field: error
unique-id: error

Items can be excluded from validation altogether by specifying a pattern under the top-level exclude: key. Any items matching these patterns will not be evaluated during validation.

A pattern can be one of the following forms:

  • jmespath:<JMESPath search expression>
  • An object representing objects to exclude based on the fields and patterns specified

In the latter form, the name of each first child of the exclude: object must be one of the types defined as an array by the CycloneDX specification.

---
exclude:
<CycloneDX array object name>: # e.g. `components`, `licenses`
# multiple fields defined on the same list item represent an AND relationship
- <field name 1>: <field value or pattern>
<field name 2>: <field value or pattern>

# separate list items represent an OR relationship when filtering
- <field name 3>: <field value or pattern>

# alternatively, exclusion filter can be specified with a JMESPath expression
- jmespath:<JMESPath search expression>

component-checks:
licenses:
name-or-id: error
license-types: error
licenses-field: error

name-field: error
supplier-field: error
version-field: error
unique-id: error

The <field value or pattern> above can be one of:

  • regexp:/<regular expression identifying elements to exclude>/[gmi]*
  • literal string representing the desired exclusion value

Example exclude configuration


# Configuration for items to exclude from validation.
exclude:

# license
license: [{"license": {"id": "SSPL-1.0"}}]

Examples

The following SBOM file named acme.cdx.json will be used in the subsequent examples:

{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:b6f4e926-6a94-4209-960f-c7045f4e3440",
"version": 1,
"metadata": {
"timestamp": "2023-11-27T23:57:56+00:00",
"licenses": [
{
"license": {
"name": "Acme Software License",
"url": "https://acme-software.com/licenses/LICENSE.txt"
}
}
],
"tools": [],
"component": {
"bom-ref": "pkg:oci/python@sha256%3A31ceea009f42df76371a8fb94fa191f988a25847a228dbeac35b6f8d2518a6ef?arch=amd64\u0026repository_url=index.docker.io%2Flibrary%2Fpython",
"type": "container",
"name": "Acme Software",
"version": "1.0.0",
"purl": "pkg:oci/python@sha256%3A31ceea009f42df76371a8fb94fa191f988a25847a228dbeac35b6f8d2518a6ef?arch=amd64\u0026repository_url=index.docker.io%2Flibrary%2Fpython"
}
},
"components": [
{
"bom-ref": "cd42f200-197b-41df-9d39-f86ce6e8dce4",
"type": "operating-system",
"name": "debian",
"version": "12.2"
},
{
"bom-ref": "pkg:deb/debian/adduser@3.134?arch=all\u0026distro=debian-12.2",
"type": "library",
"supplier": {
"name": "Debian Adduser Developers \u003cadduser@packages.debian.org\u003e"
},
"name": "adduser",
"version": "3.134",
"licenses": [
{
"license": {
"name": "GPL-2.0"
}
}
],
"purl": "pkg:deb/debian/adduser@3.134?arch=all\u0026distro=debian-12.2"
},
{
"bom-ref": "pkg:deb/debian/apt@2.6.1?arch=amd64\u0026distro=debian-12.2",
"type": "library",
"supplier": {
"name": "APT Development Team \u003cdeity@lists.debian.org\u003e"
},
"name": "apt",
"version": "2.6.1",
"licenses": [
{
"license": {
"name": "GPL-2.0"
}
},
{
"license": {
"name": "BSD-3-Clause"
}
},
{
"license": {
"name": "Expat"
}
}
],
"purl": "pkg:deb/debian/apt@2.6.1?arch=amd64\u0026distro=debian-12.2"
}
]
}

Warn Only

$ hopctl validate sbom --sbom acme.cdx.json --log hoppr.log --basic-term
Validating acme.cdx.json...

======================================================== Summary =========================================================
Results for acme.cdx.json:
CycloneDX Specification Version ✔
Minimum NTIA Fields ⚠
1 out of 1 SBOM licenses missing minimum license fields ⚠
1 out of 1 SBOM licenses expired or expiring within 30 days ⚠
0 out of 2 components missing minimum NTIA fields ✔
4 out of 4 component licenses missing minimum license fields ⚠
4 out of 4 component licenses expired or expiring within 30 days ⚠
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
See log file hoppr.log for full results.

$ echo $?
0
$ hopctl validate sbom --sbom acme.cdx.json --strict-ntia-minimum-fields --log hoppr.log --basic-term
Validating acme.cdx.json...

======================================================== Summary =========================================================
Results for acme.cdx.json:
CycloneDX Specification Version ✔
Minimum NTIA Fields ❌
1 out of 1 SBOM licenses missing minimum license fields ⚠
1 out of 1 SBOM licenses expired or expiring within 30 days ⚠
0 out of 2 components missing minimum NTIA fields ✔
4 out of 4 component licenses missing minimum license fields ⚠
4 out of 4 component licenses expired or expiring within 30 days ⚠
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
See log file hoppr.log for full results.

$ echo $?
1

Enforce Required License Fields

$ hopctl validate sbom --sbom acme.cdx.json --strict-license-fields --log hoppr.log --basic-term
Validating acme.cdx.json...

======================================================== Summary =========================================================
Results for acme.cdx.json:
CycloneDX Specification Version ✔
Minimum NTIA Fields ⚠
1 out of 1 SBOM licenses missing minimum license fields ❌
1 out of 1 SBOM licenses expired or expiring within 30 days ❌
0 out of 2 components missing minimum NTIA fields ✔
4 out of 4 component licenses missing minimum license fields ❌
4 out of 4 component licenses expired or expiring within 30 days ❌
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
See log file hoppr.log for full results.

$ echo $?
1

Enforce All Validation Checks

The --strict option is equivalent to including both the --strict-ntia-minimum-fields and --strict-license-fields options.

$ hopctl validate sbom --sbom acme.cdx.json --strict --log hoppr.log --basic-term
Validating acme.cdx.json...

======================================================== Summary =========================================================
Results for acme.cdx.json:
CycloneDX Specification Version ✔
Minimum NTIA Fields ❌
1 out of 1 SBOM licenses missing minimum license fields ❌
1 out of 1 SBOM licenses expired or expiring within 30 days ❌
0 out of 2 components missing minimum NTIA fields ✔
4 out of 4 component licenses missing minimum license fields ❌
4 out of 4 component licenses expired or expiring within 30 days ❌
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
See log file hoppr.log for full results.

$ echo $?
1