Profile format
Every key a profile file accepts.
- A complete example
- Identity
- Composition
- schema
- rules
- license_policy
- scoring
- Using your own profile
- Environment variables
A complete example
This is ntia-min-elements trimmed to two rules, showing every top-level section:
id: ntia-min-elements
name: NTIA Minimum Elements for an SBOM
version: "2021-07"
sources:
- name: NTIA
ref: "The Minimum Elements For a Software Bill of Materials (SBOM), 2021-07-12"
url: https://www.ntia.gov/report/2021/minimum-elements-software-bill-materials-sbom
schema:
min_versions:
spdx: "2.2"
cyclonedx: "1.3"
rules:
- id: ntia-timestamp
scope: document
severity: MUST
category: Freshness
citation: "NTIA: Timestamp"
field: timestamp
validators: [present, rfc3339_utc]
- id: ntia-supplier
scope: component
severity: MUST_WHERE_AVAILABLE
category: Completeness
citation: "NTIA: Supplier Name"
field: supplier
validators: [present]
scoring:
weights:
Completeness: 0.40
Accuracy: 0.15
Consistency: 0.15
Provenance: 0.20
Freshness: 0.10
thresholds:
version_coverage_min: 0.90
Identity
| Key | Required | Description |
|---|---|---|
id |
yes | Stable identifier, and what --profile takes. Match the filename. |
name |
yes | Human-readable name, shown in reports and ossbomer profiles. |
version |
no | Version of the standard this tracks, not of ossbomer. |
sources |
no | Where the requirements come from: name, ref, url. Worth filling in, since it is how a reader checks your work. |
Composition
| Key | Description |
|---|---|
extends |
List of profile ids to inherit rules from. |
excludes |
List of rule ids to drop from what was inherited. |
extends: [eu-cra-annex-i, fedramp-sbom]
excludes: [cra-top-level-dependencies]
Your own rules are added on top. Referring to inherited rules by id means
catalog updates reach you without a merge.
schema
Covered in full on Schema policy.
| Key | Description |
|---|---|
min_versions |
Per-format version floor, e.g. spdx: "2.3". |
require_signature |
Require a signature to be present. |
deprecated_versions_forbidden |
Reject versions the format has retired. |
deprecated_versions |
Override the default deprecated set per format. |
rules
| Key | Required | Description |
|---|---|---|
id |
yes | Unique within the profile. This is what excludes targets, so treat it as API. |
scope |
yes | document, component, or dependency. |
severity |
yes | MUST, MUST_WHERE_AVAILABLE, SHOULD, or MAY. |
category |
yes | One of the five scoring categories. |
citation |
yes | The clause this comes from, quoted in the finding. |
field |
usually | The IR field to check. Omitted when the validator works on the whole scope. |
validators |
yes | Validators to run, in order. |
scope decides what the rule runs against: once for document, once per
component for component, and over the dependency graph for dependency.
Available validators are listed by ossbomer validators and in the
CLI reference.
What field can name
field names an attribute on the normalized document, not a path in the source
file, so one rule works across SPDX and CycloneDX. Both formats are mapped onto
the same shape.
Document scope:
| Field | Holds |
|---|---|
creators |
Everyone credited with creating the SBOM: people, organizations and tools. |
tools |
The tool-only subset of creators. |
tool_versions |
Versions of those tools, kept separate so a rule can ask whether one was declared at all. |
timestamp |
When the SBOM data was last changed. |
sbom_version |
The revision of the SBOM document. Not the spec version, and not the version of what it describes. |
lifecycles |
The lifecycle phase it was generated in, such as build. CycloneDX 1.5+ only. |
name, namespace, supplier, data_license |
Document metadata. |
Component scope:
| Field | Holds |
|---|---|
name, version, type |
Component identity. |
purl, cpe, bom_ref |
Identifiers. |
supplier, author, publisher |
Who produced it. |
licenses |
Effective license strings: the normalized SPDX form where one resolved, otherwise the raw text. |
license_declarations |
The full record per declaration: raw text, which slot it came from, what it normalized to and how. What license_spdx_normalized reads. |
hashes |
Algorithm to digest. |
external_refs, properties |
Passed through from the source. |
Anything not listed falls back to a dotted lookup into the component’s raw source
mapping, so field: raw.someVendorExtension works for data the IR does not model.
Not every field exists in every format. SPDX 2.x has no document-version or
lifecycle-phase field, so sbom_version and lifecycles are empty there and a
rule requiring them fails for SPDX 2.x documents. That is a true statement about
the format rather than a defect in the document, and cisa-2026-min reports it
as such.
license_policy
Covered in full on License policy.
| Key | Description |
|---|---|
engine |
ospac, or omit to use only inline rules. |
use_case |
Passed to ospac as distribution_type. |
policy_path |
Your own ospac policy directory, relative to the profile file. |
context |
Extra keys your policy matches on, e.g. linking_type. |
rules |
Per-identifier overrides: spdx_id, allowed, reason. |
spdx_id is required in an override, and must be a single SPDX identifier.
Overrides are matched by exact identifier, so a rule without one would match
nothing; that is an error at load time rather than a silently inert rule. An
expression key is rejected for the same reason, so use the engine for
expression-level decisions.
scoring
| Key | Description |
|---|---|
weights |
Relative weight per category in the composite. |
thresholds |
Signal thresholds, e.g. version_coverage_min. |
Weights are normalized, so they do not have to sum to 1.0. Set a category to 0 to keep it out of the composite while still reporting it.
scoring is inherited through extends like everything else, so a profile that
extends a parent and omits scoring scores with the parent’s weights rather than
falling back to the built-in defaults.
Using your own profile
Put the file in a directory and point at it:
ossbomer validate --profile acme-shipping-bar --file sbom.json --profile-path ./private-profiles
--profile-path accepts several directories separated by the platform path
separator. Overlays are searched before the bundled catalog, so a file sharing an
id with a shipped profile replaces it.
You can also set the search path in the environment, which is often tidier for CI:
export OSSBOMER_PROFILE_PATH=/opt/acme/profiles:/opt/acme/overlays
ossbomer validate --profile acme-shipping-bar --file sbom.json
The order is --profile-path, then OSSBOMER_PROFILE_PATH, then the bundled
catalog. --profile also accepts a path to a file directly, which skips the search
entirely.
Environment variables
| Variable | Effect |
|---|---|
OSSBOMER_PROFILE_PATH |
Extra profile directories, os.pathsep-separated. Searched before the bundled catalog. |
OSSBOMER_LICENSE_ALIASES |
Extra license normalization tables, os.pathsep-separated. See License policy. |