To check an AIBOM, CBOM, SaaSBOM or HBOM, run schema validation, then semantic checks the schema cannot make: does every item have a stable identity, do the relationships resolve, does the document say how complete it is, and are the fields that matter for that BOM type actually filled in. A file that passes the schema can still be useless.
Most SBOM quality tooling was built for package lists. It asks whether each component has a supplier, a version and a package URL. Those questions barely apply to a cryptographic algorithm, a hosted API or a circuit board, so a high SBOM score on one of these documents tells you very little. This guide gives you per type checklists, the commands that do the mechanical part, and a scoring rubric a buyer can apply in an afternoon.
Key takeaways
- Schema valid is the floor, not the bar. In our own test, an SPDX 3.0.1 AI package with its required release time removed still passed both the JSON schema and the SHACL check. Every BOM type needs semantic checks on top.
- SBOM scores do not transfer. Package URL coverage and license coverage are close to meaningless for a CBOM, a SaaSBOM or an HBOM. Each type has its own "identity" field and its own depth fields.
- No compositions means unknown. CycloneDX
compositionsis how a BOM states its own completeness; never infer it from silence. - The 2026 minimum elements are still the base layer. The joint CISA guidance published on 29 July 2026 applies to all software and says AI and SaaS may need more. Start there, then add type specific checks.
- The official open source tools now do much of the work. CycloneDX CLI validates schemas, cdxgen's BOM audit has rule packs for AI-BOM, CBOM, HBOM and OBOM,
cdx-validateadds deep checks, and SPDX 3 documents validate against a JSON schema plus a SHACL model. - Score with a published rubric. A 100 point rubric with a schema gate and type specific weights turns "is this good?" into a contract term.
If you have not read our earlier posts on SBOM quality, start with SBOM quality metrics beyond completeness and SBOM validation quality checks. This post goes sideways from them into the other BOM types.
Why SBOM quality scores do not transfer to other BOM types
SBOM quality checks grew out of one use case: matching components against vulnerability and license data. So a good SBOM, by that measure, has a package URL or CPE, a version, a supplier, a license and a hash for nearly every component. The NTIA 2021 minimum elements and the 2026 Minimum Elements both center on identifying a software component well enough to correlate it with other data.
The other BOM types identify different things:
- A CBOM lists algorithms, certificates, protocols and key material. An algorithm has no package URL and no license. Its identity is an object identifier (OID) plus parameters: AES-256 in GCM mode and AES-128 in CBC mode are different assets. The CycloneDX CBOM page frames the goal as finding "deprecated algorithms or expired certificates" and preparing for post-quantum migration.
- A SaaSBOM lists services, which CycloneDX's SaaSBOM page describes as "services, endpoints, and data flows and classifications". Authentication, trust boundaries and data classes have no place in package scoring.
- An HBOM lists devices, boards and firmware for IoT, ICS and embedded systems, per the CycloneDX HBOM page. Identity is a manufacturer plus part number and revision; a serial number identifies one unit, not a product.
- An AIBOM (CycloneDX ML-BOM, or the SPDX 3 AI and Dataset profiles) lists models, datasets and their configuration. A model can carry a
pkg:huggingfacepurl, but reviewers ask which training data, which task, which limitations and which exact weights. - An OBOM describes runtime: operating system, platform and configuration. CycloneDX's OBOM page calls it a "full-stack inventory of runtime environments".
The structural checks carry over. The content checks do not, so each type needs its own idea of identity, its own depth fields and its own failure patterns.
Schema validation versus semantic checks
Every BOM review has two layers, and confusing them is the most common mistake we see.
Schema validation asks whether the document is well formed against the specification's machine readable schema. For CycloneDX that is the JSON schema for the declared specVersion (the specification repository publishes one per version). For SPDX 3, the serialization section of the SPDX 3.0.1 specification defines conformance as two steps: structural validation against the SPDX JSON schema, and semantic validation against the SPDX OWL ontology with its SHACL shapes. Schema validation catches wrong enum values, wrong types, misspelled property names and missing fields the schema marks as required.
Semantic checks ask whether the content is true, consistent and useful. The schema cannot tell you that a bom-ref in dependencies points at nothing, that every component is a "library" because the generator did not know better, that a certificate's notValidAfter is in the past, or that a SaaSBOM lists ten services and no third party ones.
An example from our own testing on 23 October 2026: the SPDX 3.0.1 AIPackage class page lists cardinality updates for an AI package: releaseTime, suppliedBy, downloadLocation, packageVersion and primaryPurpose each have a minimum count of 1. We built a minimal SPDX 3.0.1 AI package, then deleted its releaseTime. The modified document still passed the SPDX JSON schema, and it still passed the SHACL check run by the community spdx3-validate tool (version 0.0.7). When we instead pointed suppliedBy at an identifier that did not exist in the document, the SHACL check failed with "Value does not have class ns1:Agent". So the SHACL layer catches some relationship errors, but not every field the model page lists as required for AI packages.
| Check | What it catches | What it misses | Tooling |
|---|---|---|---|
| CycloneDX JSON schema | Wrong enum values, wrong types, unknown properties, missing schema required fields | Dangling refs, stale data, empty depth fields, false completeness claims | CycloneDX CLI validate, cdx-validate |
| cdxgen deep validation | Metadata, purl, bom-ref and property consistency | Type specific depth (for example a model card with no datasets) | cdx-validate with --deep (on by default) |
| cdxgen BOM audit rule packs | Type specific security and hygiene findings (AI, CBOM, HBOM, OBOM) | Anything the rules do not encode; completeness of collection | cdxgen --bom-audit, cdx-audit |
| SPDX 3 JSON schema | Structure, property names and value types | Many model constraints | Any JSON Schema validator |
| SPDX 3 SHACL | Class of referenced elements and other model shapes | In our test, a missing AIPackage releaseTime | spdx3-validate, pyshacl |
| Your own semantic rules | Whatever your use case needs | Only what you forget to write | JSONata rules in cdxgen, scripts, policy engines |
Compositions: how a BOM states its own completeness
A BOM that lists twelve components could be a full inventory or a quick guess. The only way a reader can tell is if the author says so. CycloneDX has a field for that: compositions. Our post on CycloneDX compositions covers it for SBOMs; here is why it matters even more for the other types.
In the CycloneDX 1.6 and 1.7 schemas, each composition has an aggregate value and the list of assemblies, dependencies or vulnerabilities it describes. The aggregate values are: complete, incomplete, incomplete_first_party_only, incomplete_first_party_proprietary_only, incomplete_first_party_opensource_only, incomplete_third_party_only, incomplete_third_party_proprietary_only, incomplete_third_party_opensource_only, unknown and not_specified.
For a CBOM the honest answer is often incomplete_first_party_only (your code was scanned, the TLS library and OS crypto provider were not). For a SaaSBOM, incomplete_third_party_only is common. For an HBOM with supplier sub-assemblies, incomplete is the usual truth.
The 2026 minimum elements push the same idea. The document replaced the old "Known Unknowns" element with "Explicitly Identifying Unknown Information", and it separates information the author does not know from information the author is deliberately withholding. It also says organizations "may consider an SBOM incomplete if the SBOM author withholds essential component data." CycloneDX compositions are one machine readable way to carry the "unknown" half of that. For withheld information, ask the author to say so in the document or in the delivery terms, since the aggregate values describe completeness, not the reason for a gap. Our post on how to tell if an SBOM is complete goes deeper on known unknowns.
A consumer rule that works across all types: no compositions means "unknown". Never infer completeness from silence.
Start from the 2026 minimum elements
The 2026 Minimum Elements, published by CISA with the NSA, the FBI and international partners on 29 July 2026, updates and replaces the NTIA 2021 minimum elements. The PDF lists new elements: SBOM Author Signature, SBOM Data Format Name and Version, SBOM Generation Context, SBOM Tool Name and Version, SBOM Version, Component Hash Value, Component Hash Algorithm and Component License. It also says some software types, "such as artificial intelligence (AI) software systems and software as a service (SaaS) in cloud environments, may require additional elements", and that any transparency effort should begin with the minimum elements regardless of type.
Two consequences follow. The document metadata (author, tool, timestamp, format, version, signature) applies to every BOM type, so that part of your rubric is shared. And the guidance does not define the extra elements. For AI it points to separate G7 guidance, "Software Bill of Materials for AI, Minimum Elements", which CISA lists as published on 12 May 2026 and links to the German BSI for the document itself. For SaaS it says further specification "may warrant additional exploration"; CISA's older Software Transparency in SaaS Environments paper discusses the problem but is not a field list. For CBOM and HBOM, the format specifications are the main reference, so the checklists below are built on the CycloneDX 1.6 and 1.7 schemas and the SPDX 3.0.1 AI profile.
Per type checklists: required versus useful fields
"Required" below means two different things, and we mark which. Schema required means the specification's schema rejects the document without it. Review required means a reviewer cannot do the job the BOM exists for without it, even though the schema accepts its absence. In CycloneDX, a component only has two schema required fields, type and name, and a service only has one, name. Almost everything a reviewer needs is optional in the schema.
AIBOM (CycloneDX ML-BOM and SPDX 3 AI profile)
What it is for: knowing which models and datasets a system depends on, where they came from, what they are meant to do, and what they must not be used for. The CycloneDX ML-BOM page lists datasets, models, configurations and provenance as the core.
Review required (CycloneDX):
- A component of
type: machine-learning-modelfor every model, including models called through a hosted API. nameand an exactversion. For open weights, a revision or commit, not "latest".supplierormanufacturer.- A hash of the weights file or files, or a purl that pins a revision.
licenses.modelCard.modelParameters.taskanddatasets, with each dataset either inline (componentData) or arefto atype: datacomponent.dependencieslinking the application to the model and the model to its datasets.- A
compositionsentry for the model assembly and one for its datasets.
Review required (SPDX 3.0.1): an ai_AIPackage with name, software_packageVersion, software_downloadLocation, software_primaryPurpose, releaseTime and suppliedBy, which the AIPackage page lists with a minimum count of 1, plus creationInfo.
Useful: modelCard.considerations (use cases, technical limitations, ethical and fairness considerations), quantitativeAnalysis (performance metrics), architecture family, input and output formats, and in SPDX 3, ai_typeOfModel, ai_limitation, ai_informationAboutTraining, ai_safetyRiskAssessment, ai_useSensitivePersonalInformation, ai_autonomyType and ai_energyConsumption.
What an SBOM score misses: whether datasets are linked, whether the model card says anything, whether a hosted model is pinned. cdxgen's AI rules include AIG-002, "AI inference service is used without an explicit model ID", which is exactly the kind of gap a package score never sees.
Minimal CycloneDX example, valid against both the CycloneDX 1.6 and 1.7 JSON schemas (we checked it against both on 23 October 2026):
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"authors": [{ "name": "Platform ML team", "email": "ml-platform@example.com" }],
"tools": { "components": [{ "type": "application", "name": "example-aibom-tool", "version": "1.0.0" }] },
"component": { "type": "application", "bom-ref": "app", "name": "support-assistant", "version": "4.2.0" }
},
"components": [
{
"type": "machine-learning-model",
"bom-ref": "model-intent",
"name": "intent-classifier",
"version": "2026.10.1",
"supplier": { "name": "Example Corp" },
"purl": "pkg:huggingface/example-corp/intent-classifier@a1b2c3d4e5f60718293a4b5c6d7e8f9012345678",
"hashes": [{ "alg": "SHA-256", "content": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" }],
"licenses": [{ "license": { "id": "Apache-2.0" } }],
"modelCard": {
"modelParameters": {
"task": "text-classification",
"architectureFamily": "transformer",
"datasets": [{ "ref": "data-tickets" }]
},
"considerations": {
"useCases": ["Route inbound support tickets to a queue"],
"technicalLimitations": ["English only; accuracy drops on tickets under 10 words"]
}
}
},
{
"type": "data",
"bom-ref": "data-tickets",
"name": "support-tickets-2025",
"data": [{
"type": "dataset",
"name": "support-tickets-2025",
"classification": "confidential",
"governance": { "owners": [{ "organization": { "name": "Example Corp" } }] }
}]
}
],
"dependencies": [
{ "ref": "app", "dependsOn": ["model-intent"] },
{ "ref": "model-intent", "dependsOn": ["data-tickets"] },
{ "ref": "data-tickets", "dependsOn": [] }
],
"compositions": [
{ "aggregate": "complete", "assemblies": ["model-intent"] },
{ "aggregate": "unknown", "assemblies": ["data-tickets"] }
]
}
The second composition says, in a machine readable way, that the dataset's contents are not fully known. That beats a list that silently omits training data.
The same model in SPDX 3.0.1 JSON-LD, valid against the SPDX 3.0.1 JSON schema and the SHACL check in spdx3-validate 0.0.7:
{
"@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld",
"@graph": [
{
"type": "CreationInfo",
"@id": "_:creationinfo",
"specVersion": "3.0.1",
"created": "2026-10-23T09:00:00Z",
"createdBy": ["https://example.com/spdx/Organization/example-corp"]
},
{
"type": "Organization",
"spdxId": "https://example.com/spdx/Organization/example-corp",
"name": "Example Corp",
"creationInfo": "_:creationinfo"
},
{
"type": "ai_AIPackage",
"spdxId": "https://example.com/spdx/AIPackage/intent-classifier-2026.10.1",
"name": "intent-classifier",
"creationInfo": "_:creationinfo",
"software_packageVersion": "2026.10.1",
"software_downloadLocation": "https://models.example.com/intent-classifier/2026.10.1",
"software_primaryPurpose": "model",
"releaseTime": "2026-10-01T00:00:00Z",
"suppliedBy": "https://example.com/spdx/Organization/example-corp",
"ai_typeOfModel": ["transformer"],
"ai_informationAboutTraining": "Fine-tuned on internal support tickets, 2025",
"ai_limitation": "English only",
"ai_safetyRiskAssessment": "low",
"ai_useSensitivePersonalInformation": "no"
}
]
}
This is a fragment for illustration: a complete SPDX 3 document would normally also carry an SpdxDocument element and relationships to the dataset.
CBOM (CycloneDX cryptographic assets)
What it is for: finding weak, deprecated or quantum vulnerable cryptography, expiring certificates, and where each is used. The CycloneDX CBOM page frames it around post-quantum readiness and "deprecated algorithms or expired certificates".
Schema required: each crypto component needs type: cryptographic-asset, a name, and cryptoProperties.assetType, which must be one of algorithm, certificate, protocol or related-crypto-material.
Review required, by asset type:
| Asset type | Fields a reviewer needs | Why |
|---|---|---|
algorithm | primitive, parameterSetIdentifier (key or output size), mode, padding where it applies, cryptoFunctions, classicalSecurityLevel, nistQuantumSecurityLevel, oid | "RSA" alone cannot be judged; RSA 1024 and RSA 4096 are different findings |
certificate | subjectName, issuerName, notValidBefore, notValidAfter, signatureAlgorithmRef, subjectPublicKeyRef, certificateFormat | Expiry and signature algorithm drive most certificate findings |
protocol | type, version, cipherSuites with algorithm refs | TLS 1.0 and TLS 1.3 are not the same risk |
related-crypto-material | type, size, state, algorithmRef, lifecycle dates | A key's size and state matter more than its existence |
Then the links: dependencies should show which software component uses or provides each crypto asset. CycloneDX 1.6 and 1.7 add a provides array to dependency entries for this, alongside dependsOn. A CBOM with fifty algorithms and no links to code tells you what exists but not where to fix it.
Useful: component.evidence.occurrences with file and line locations, and evidence.identity with the detection technique. The 1.6 schema's technique values include source-code-analysis, binary-analysis, instrumentation, dynamic-analysis and others. A CBOM that says how each asset was found lets you judge the false positive rate. In CycloneDX 1.7, certificates also gain fields such as serialNumber, fingerprint, certificateState and lifecycle dates.
What an SBOM score misses: everything above. A purl coverage metric would score a CBOM at zero or ignore it.
Minimal example, valid against the CycloneDX 1.6 and 1.7 JSON schemas:
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:9c2f3b1e-5d0a-4b7e-8f61-2a0d4c9e7b13",
"version": 1,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"component": { "type": "application", "bom-ref": "app", "name": "payments-api", "version": "7.3.1" }
},
"components": [
{
"type": "cryptographic-asset",
"bom-ref": "crypto/algorithm/aes-256-gcm",
"name": "AES-256-GCM",
"cryptoProperties": {
"assetType": "algorithm",
"algorithmProperties": {
"primitive": "ae",
"parameterSetIdentifier": "256",
"mode": "gcm",
"executionEnvironment": "software-plain-ram",
"implementationPlatform": "x86_64",
"cryptoFunctions": ["encrypt", "decrypt"],
"classicalSecurityLevel": 256,
"nistQuantumSecurityLevel": 5
},
"oid": "2.16.840.1.101.3.4.1.46"
}
},
{
"type": "cryptographic-asset",
"bom-ref": "crypto/protocol/tls-1.3",
"name": "TLS",
"version": "1.3",
"cryptoProperties": {
"assetType": "protocol",
"protocolProperties": {
"type": "tls",
"version": "1.3",
"cipherSuites": [{ "name": "TLS_AES_256_GCM_SHA384", "algorithms": ["crypto/algorithm/aes-256-gcm"], "identifiers": ["0x13", "0x02"] }]
}
}
}
],
"dependencies": [
{ "ref": "app", "dependsOn": ["crypto/protocol/tls-1.3"] },
{ "ref": "crypto/protocol/tls-1.3", "dependsOn": ["crypto/algorithm/aes-256-gcm"] },
{ "ref": "crypto/algorithm/aes-256-gcm", "dependsOn": [] }
],
"compositions": [{ "aggregate": "incomplete_first_party_only", "assemblies": ["app"] }]
}
When we changed assetType to "cipher", both schemas rejected it. A wrong nistQuantumSecurityLevel would have passed.
SaaSBOM (CycloneDX services)
What it is for: knowing which services a hosted product runs and calls, where the trust boundaries are, and which classes of data cross them. CISA's 2026 document notes the shared responsibility between SaaS producers and operators and the "high-frequency changes" in cloud software, which makes freshness part of quality.
Schema required: a service needs only name. Each entry in a service's data array needs flow (inbound, outbound, bi-directional or unknown) and classification.
Review required: provider, version where the service is versioned, endpoints, authenticated, x-trust-boundary, and data flows with classifications for every service that handles sensitive data. Third party services the product calls, not just the vendor's own. dependencies between services. A compositions entry, usually scoped to third party services.
Useful: trustZone (present in the 1.6 and 1.7 schemas), licenses for any software component behind the service, nested services, and a link to the underlying SBOM for each first party service through externalReferences.
What an SBOM score misses: whether authenticated is set at all. Absence is not false; it means nobody said. Whether data classifications use a vocabulary the reader understands: the schema types classification as a free string, so "PII", "pii" and "personal" can all appear in one file.
Minimal example, valid against the CycloneDX 1.6 and 1.7 JSON schemas:
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:5b7e0c2a-9f14-4d3b-a6c8-0e1f2d3c4b5a",
"version": 3,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"component": { "type": "application", "bom-ref": "svc-root", "name": "billing-platform", "version": "2026.10.23" }
},
"services": [
{
"bom-ref": "svc-invoice",
"provider": { "name": "Example Corp" },
"name": "invoice-api",
"version": "3.4.0",
"endpoints": ["https://api.example.com/v3/invoices"],
"authenticated": true,
"x-trust-boundary": true,
"data": [
{ "flow": "inbound", "classification": "PII" },
{ "flow": "outbound", "classification": "PCI" }
]
},
{
"bom-ref": "svc-tax",
"provider": { "name": "Third-party tax provider" },
"name": "tax-rates",
"endpoints": ["https://tax.example.net/rates"],
"authenticated": true,
"x-trust-boundary": true,
"data": [{ "flow": "outbound", "classification": "public" }]
}
],
"dependencies": [
{ "ref": "svc-root", "dependsOn": ["svc-invoice"] },
{ "ref": "svc-invoice", "dependsOn": ["svc-tax"] },
{ "ref": "svc-tax", "dependsOn": [] }
],
"compositions": [{ "aggregate": "incomplete_third_party_only", "assemblies": ["svc-tax"] }]
}
Note "version": 3: a SaaSBOM's document version should climb with every deployment that changes the service graph.
HBOM (CycloneDX hardware)
What it is for: knowing which hardware parts and firmware are in a device, for lifecycle, end of life and firmware vulnerability work. Safeguard does not generate HBOMs or scan firmware; this checklist is for reviewing HBOMs you receive from suppliers.
Schema required: type and name per component. Hardware uses the device, device-driver and firmware component types.
Review required: manufacturer for each device (the schema has a manufacturer field on components in 1.6 and 1.7), a revision or version, a part number, firmware version and hashes, and dependencies linking the device to its boards and firmware. There is no dedicated part number field in the schema, so suppliers use properties; agree the property name with them, or every HBOM will spell it differently.
Useful: externalReferences to datasheets and advisories, end of life dates in properties, and an explicit composition per sub-assembly.
Privacy and scope check: decide whether the HBOM describes a model (a product line) or a unit (one serial numbered device). Unit level HBOMs can expose raw identifiers. cdxgen's HBOM rule pack includes HBS-004, "HBOM exposes raw hardware identifiers", and HBS-006 for cellular or subscriber identifiers, and its docs recommend sharing redacted HBOMs externally unless raw identifiers are required.
Minimal example, valid against the CycloneDX 1.6 and 1.7 JSON schemas:
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:0d4e2a6b-7c19-4f58-9b3e-61a2c8d7e5f4",
"version": 1,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"component": { "type": "device", "bom-ref": "gw", "name": "edge-gateway", "version": "rev-C", "manufacturer": { "name": "Example Devices Inc" } }
},
"components": [
{
"type": "device",
"bom-ref": "gw/soc",
"name": "application-processor",
"version": "B1",
"manufacturer": { "name": "Example Silicon Ltd" },
"properties": [{ "name": "example:hbom:part-number", "value": "XS-4410-B1" }]
},
{
"type": "firmware",
"bom-ref": "gw/soc/bootloader",
"name": "bootloader",
"version": "2026.04.2",
"hashes": [{ "alg": "SHA-256", "content": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae" }]
}
],
"dependencies": [
{ "ref": "gw", "dependsOn": ["gw/soc"] },
{ "ref": "gw/soc", "dependsOn": ["gw/soc/bootloader"] },
{ "ref": "gw/soc/bootloader", "dependsOn": [] }
],
"compositions": [{ "aggregate": "incomplete", "assemblies": ["gw"] }]
}
OBOM (operations and runtime)
What it is for: describing what is deployed and running. Safeguard does not generate OBOMs either. Review required: a metadata.lifecycles phase that marks it as an operational snapshot, a trustworthy timestamp, operating-system and platform components with versions, and a clear scope (which host, cluster or environment). Useful: listening services, configuration files as hashed file components, and a link to the build time SBOM. Watch sharing: cdxgen's obom-runtime rules inspect systemd units, sudoers rules and exposed listeners, which is valuable inside your perimeter and risky outside it.
Which checks matter per BOM type
| Check | SBOM | AIBOM | CBOM | SaaSBOM | HBOM | OBOM |
|---|---|---|---|---|---|---|
| Schema valid for declared spec version | Gate | Gate | Gate | Gate | Gate | Gate |
| Author, tool, timestamp, document version, signature | High | High | High | High | High | High |
| Primary identity field | purl or CPE | Version plus hash or pinned purl | OID plus parameters | Name plus endpoint | Manufacturer plus part number and revision | OS and platform name plus version |
| Relationships resolve (no dangling refs) | High | High | High | High | High | Medium |
| Compositions present and honest | High | High | High | High | High | Medium |
| Type depth fields | Licenses, hashes | Model card, datasets | Algorithm parameters, certificate dates | Auth, trust boundary, data flows | Firmware versions and hashes | Lifecycle phase, configuration |
| Freshness expectation | Per release | Per model version | Per release and on certificate rotation | Per deployment or snapshot schedule | Per hardware revision | Per snapshot |
| Sharing sensitivity | Low to medium | Medium (dataset names) | High (crypto weaknesses) | High (endpoints) | Medium to high (identifiers) | High (host configuration) |
Commands: validating and auditing each BOM type
Everything below uses the official open source tools of each standard. Run them on BOMs you receive before you load them anywhere else.
Step 1: schema validation with CycloneDX CLI
The CycloneDX CLI validate command takes an input file, a format and a spec version. Its README lists versions v1_0 through v1_7 and says the default is v1.7, so pass the version the BOM declares:
# Validate a CycloneDX 1.6 JSON BOM and fail the build on errors
cyclonedx-cli validate --input-file aibom.json --input-format json --input-version v1_6 --fail-on-errors
# Same for the other types
for f in cbom.json saasbom.json hbom.json; do
cyclonedx-cli validate --input-file "$f" --input-format json --input-version v1_6 --fail-on-errors || exit 1
done
A quick way to read the declared version first, so you never validate against the wrong schema:
jq -r '.specVersion' aibom.json
Step 2: deep and compliance validation with cdx-validate
cdx-validate ships with cdxgen. Its guide describes three layers: schema validation, deep validation of metadata, purl, bom-ref and property consistency, and compliance rule packs for OWASP SCVS (all 87 controls across L1 to L3) and EU Cyber Resilience Act SBOM expectations (8 controls). Rules that cannot be decided from a BOM alone come back as manual review items.
npm install -g @cyclonedx/cdxgen
# Table and scorecard
cdx-validate -i saasbom.json
# Machine-readable output for CI, fail at medium or above, strict on schema and deep errors
cdx-validate -i saasbom.json -r json --fail-severity medium --strict > saasbom.validate.json
# Verify the signature and the structure together
cdx-validate -i saasbom.json --public-key supplier_public.pem
The guide documents the exit codes: 0 for pass, 2 for schema or deep failure under --strict, 3 for findings at or above the fail severity, 4 for a required signature that failed. Those map cleanly to CI gates.
The SCVS and CRA packs are written for software BOMs, so on an AIBOM, CBOM or HBOM read them as document hygiene, not a type specific score.
Step 3: type specific audits with cdxgen BOM audit
cdxgen's BOM audit evaluates a BOM against YAML rules written as JSONata expressions. It can run at generation time with --bom-audit, or later on a saved BOM with cdx-audit. The documented categories that matter here:
| BOM type | Category or alias | Example rules from the docs |
|---|---|---|
| AIBOM | ai-bom (alias for ai-governance, ai-security, ai-performance, ai-agent, mcp-server) | AIG-001 prompt or model routing file in the BOM; AIG-002 inference service without an explicit model ID; AIS-001 insecure remote HTTP transport; AIP-002 large local model without quantization metadata |
| CBOM | cbom (alias for cbom-security, cbom-compliance) | CBS-001 weak or deprecated algorithm such as MD5, SHA-1, RC4, DES or Blowfish; CBS-003 symmetric level below 128 bits or RSA below 2048 bits; CBS-004 SSL, TLS 1.0 or TLS 1.1; CBC-001 expired certificate; CBC-002 certificate expiring within 30 days |
| HBOM | hbom (alias for hbom-security, hbom-performance, hbom-compliance) | HBS-001 storage explicitly unencrypted; HBS-004 raw hardware identifiers exposed |
| OBOM | obom-runtime | Linux, Windows and macOS host posture rules |
Re-auditing BOMs you received from a supplier:
# AIBOM
cdx-audit --bom aibom.json --direct-bom-audit --categories ai-bom
# CBOM
cdx-audit --bom cbom.json --direct-bom-audit --categories cbom
# HBOM
cdx-audit --bom hbom.json --direct-bom-audit --categories hbom
The docs also show audit findings written back into the BOM as CycloneDX annotations (for spec version 1.4 and later), with properties such as cdx:audit:ruleId and cdx:audit:severity. That gives you a record of what you checked, attached to the document itself.
Two cautions from the same docs. First, these rules mostly look for risky content; they are not a completeness test. A CBOM that lists only strong algorithms passes cbom-security whether or not it found every algorithm. Second, several rules read cdxgen specific cdx: properties. A BOM from another generator will not carry those properties, so some rules have nothing to evaluate. A clean audit on a foreign BOM can mean "nothing to check" as easily as "nothing wrong".
Step 4: SPDX 3 validation
The SPDX 3.0.1 specification defines conformance as JSON schema validation plus SHACL validation. You can run both with general tools or with spdx3-validate, a community tool on PyPI whose README says it adds context aware checks such as handling identifiers defined in an ExternalMap and validating merged documents.
python3 -m venv .venv && . .venv/bin/activate
pip install spdx3-validate check-jsonschema
# Structural check against the published SPDX 3.0.1 JSON schema
check-jsonschema --schemafile https://spdx.org/schema/3.0.1/spdx-json-schema.json ai.spdx3.json
# Structural plus SHACL check
spdx3-validate --json ai.spdx3.json
Remember the test above: both passed a document missing releaseTime. Add a field presence check of your own for the AI profile:
jq -e '
[."@graph"[] | select(.type == "ai_AIPackage")
| select((.releaseTime and .suppliedBy and .software_packageVersion
and .software_downloadLocation and .software_primaryPurpose) | not)]
| length == 0' ai.spdx3.json
For SPDX 2.x documents, the SPDX organisation's tools-python validates 2.2 and 2.3 with pyspdxtools -i <file>. Its README says v0.8 can write SPDX 3.0 but not read it, so use it for 2.x only.
Step 5: your own semantic checks with jq
A few checks every BOM type needs, which take one line each:
# Every ref used in dependencies must exist as a component, service or the metadata component
jq -e '
([.metadata.component."bom-ref"] + [.components[]?."bom-ref"] + [.services[]?."bom-ref"]) as $refs
| [.dependencies[]? | (.ref, .dependsOn[]?, .provides[]?)] - $refs | length == 0' bom.json
# A compositions block must exist
jq -e '(.compositions // []) | length > 0' bom.json
# AIBOM: every model has a version and either a hash or a purl
jq -e '[.components[]? | select(.type == "machine-learning-model")
| select(.version == null or (.hashes == null and .purl == null))] | length == 0' aibom.json
# CBOM: every algorithm has a parameter set or a security level
jq -e '[.components[]? | select(.cryptoProperties.assetType == "algorithm")
| select(.cryptoProperties.algorithmProperties.parameterSetIdentifier == null
and .cryptoProperties.algorithmProperties.classicalSecurityLevel == null)] | length == 0' cbom.json
# SaaSBOM: every service says whether it is authenticated and whether it crosses a trust boundary
jq -e '[.services[]? | select(.authenticated == null or ."x-trust-boundary" == null)] | length == 0' saasbom.json
# HBOM: every firmware component has a version and a hash
jq -e '[.components[]? | select(.type == "firmware") | select(.version == null or .hashes == null)] | length == 0' hbom.json
Schema validation never checks for dangling references, and they are common in BOMs merged from several tools.
Step 6: put it in CI
A GitHub Actions job that gates incoming supplier BOMs stored in a supplier-boms/ folder:
name: check-supplier-boms
on:
pull_request:
paths: ["supplier-boms/**"]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- run: npm install -g @cyclonedx/cdxgen
- name: Schema and deep validation
run: |
for f in supplier-boms/*.cdx.json; do
# fails on high findings, including an unsigned BOM
cdx-validate -i "$f" --strict --fail-severity high
done
- name: Type audits
run: |
for f in supplier-boms/*.cbom.cdx.json; do
cdx-audit --bom "$f" --direct-bom-audit --categories cbom
done
for f in supplier-boms/*.aibom.cdx.json; do
cdx-audit --bom "$f" --direct-bom-audit --categories ai-bom
done
- name: Semantic checks
run: |
for f in supplier-boms/*.cdx.json; do
jq -e '(.compositions // []) | length > 0' "$f"
done
Pin actions to full commit SHAs in your real workflow; cdxgen's own ci-permission rules flag actions pinned to mutable tags.
A scoring rubric a buyer can apply
This rubric is built so that you can paste it into a supplier questionnaire or contract annex. It has a hard gate and 100 points.
Gate (pass or reject): the BOM validates against the schema of the spec version it declares, and the declared version is not deprecated. The 2026 minimum elements say organizations should avoid accepting SBOMs for new software generated in deprecated format versions. A BOM that fails the gate is returned, not scored.
| Area | Points | How to score |
|---|---|---|
| Document identity and integrity | 15 | Author (3), tool name and version (3), timestamp (2), serial number and document version (2), signature that verifies against a key you trust (5) |
| Item identity | 20 | Share of items with the type's primary identity field (see the table above), scaled to 20. For example, a CBOM where 9 of 10 algorithms have OID plus parameters scores 18 |
| Relationships | 15 | No dangling refs (5), every item reachable from the root (5), type specific links present: model to dataset, code to crypto asset, service to service, device to firmware (5) |
| Completeness statement | 15 | Compositions present (5), aggregates specific rather than unknown or not_specified everywhere (5), withheld information stated as withheld (5) |
| Type depth | 25 | Use the "review required" list for the type. Score the share of items with all review required fields filled, scaled to 25 |
| Freshness and fit | 10 | Generated for the exact release, model version, deployment or hardware revision you are buying (5), age within the agreed window (5) |
Bands: 85 and above, accept. 70 to 84, accept with a remediation request and a date. 50 to 69, accept only for inventory, not for risk decisions. Below 50, return it.
Two rules keep it fair: score each type against its own "review required" list, and score unknowns stated in compositions higher than silent omissions, or you reward suppliers for hiding gaps.
The OpenSSF SBOM Everywhere SIG maintains a catalog of SBOM tooling if you need more checkers. For an outside benchmark on document hygiene, cdx-validate reports per benchmark percentages for SCVS and CRA, and the OWASP AIBOM Generator, now hosted by the OWASP GenAI Security Project, calculates an "AIBOM completeness" score with recommendations for AIBOMs it generates from Hugging Face models. Treat outside scores as one input. They measure what their authors chose to measure.
Common failure patterns per type
AIBOM
- The model is listed as "gpt" or "llama" with no version, or with
latest. You cannot match it to anything. - Hosted models are missing entirely because the generator only scanned files on disk. A model called over an API is still a dependency.
- Datasets are absent and there is no composition saying so.
- The BOM was generated once at launch, and the model has been retrained or swapped since.
- Prompt files, model routing configuration and agent tool configuration are not inventoried. cdxgen's AI categories treat these as part of the AI inventory (
AIG-001for prompt and routing files,mcp-serverrules for MCP configuration).
CBOM
- Algorithm names with no parameters: "RSA", "AES", "SHA".
- Algorithms found by text search that appear only in comments, tests or dead code, with no
evidencesaying how they were detected. - The application's own crypto calls are listed, but the TLS library and operating system providers are not, and compositions claim
complete. - The CBOM is shared outside the company without review, handing a reader a list of weak spots.
SaaSBOM
- Only first party services are listed.
authenticatedandx-trust-boundaryare omitted, which reads as unknown, not as safe.- Classifications use free text that changes from release to release.
- The snapshot is months old for a service that deploys daily, and
versionat the top never changes.
HBOM
- Unit level serial numbers and MAC addresses in an HBOM meant for a product line.
- Firmware listed by name with no version or hash, so it cannot be matched to advisories.
- Sub-assemblies from suppliers appear as single opaque devices, with no composition saying the inside is unknown.
OBOM
- No lifecycle phase, so a build time inventory is mistaken for a runtime one.
- One OBOM stands in for a fleet of hosts that have drifted apart.
Common mistakes
- Running SBOM scorers on non-SBOM BOMs and trusting the number. The score measures package fields. Use the type's own checklist.
- Validating against the wrong spec version. CycloneDX CLI defaults to v1.7 in its current README. Read
specVersionand pass it explicitly. - Treating "schema valid" as "reviewed". Our SPDX 3 test shows even required model fields can slip through. Add semantic checks.
- Ignoring compositions, or demanding
complete. A supplier who writesincomplete_third_party_onlyis being honest. Reward that over a falsecomplete. - Accepting unsigned BOMs. The 2026 minimum elements add an SBOM Author Signature element. A BOM that cannot be tied to its author has weak evidential value.
- Merging BOM types without checking refs. A CBOM merged into an SBOM often leaves
dependenciespointing at refs that were renamed during the merge. - Sharing sensitive BOM types like SBOMs. CBOMs, SaaSBOMs, OBOMs and unit level HBOMs can expose weaknesses, endpoints and identifiers. Decide the audience before you publish.
- No freshness rule. A SaaSBOM or AIBOM without an agreed maximum age goes stale quietly.
FAQ
What is the difference between SBOM quality and AIBOM quality?
SBOM quality is mostly about identifying packages for vulnerability and license matching. AIBOM quality is about pinning exact models and datasets, linking them, and filling in the model card. Document level checks are shared; content checks differ.
How do I validate a CBOM?
Run cyclonedx-cli validate for its declared version, then cdx-audit --categories cbom for weak algorithms, old protocols and certificate expiry, then check that every algorithm has parameters and every asset links to the code that uses it.
Does a schema valid BOM mean the BOM is complete?
No. Schema validation checks structure. Completeness is something the author states, in CycloneDX through compositions, and something you test by comparing the BOM against what you know is in the product.
What fields are required in a CycloneDX ML-BOM?
The schema only requires type and name. For review you need an exact version, supplier, hash or pinned purl, license, model card task and linked datasets, plus dependencies and compositions.
What is the SPDX 3 AI profile and how do I validate it?
It is the part of SPDX 3.0.1 that describes AI packages and their properties, such as type of model, limitations and safety risk assessment. Validate it with the SPDX 3.0.1 JSON schema and the SHACL model, for example with spdx3-validate, then add your own checks for the fields the AIPackage page lists with a minimum count of 1.
Can cdxgen audit a BOM it did not generate?
Yes. cdx-audit --bom file.json --direct-bom-audit --categories <category> runs the rules on any CycloneDX BOM. Some rules read cdxgen specific properties and will have nothing to check on BOMs from other generators.
How do CycloneDX compositions show completeness?
Each composition gives an aggregate value, such as complete, incomplete_first_party_only or unknown, for a list of assemblies, dependencies or vulnerabilities. Consumers should treat a BOM with no compositions as unknown.
Do the CISA minimum elements cover AIBOMs and SaaSBOMs?
They apply to all software and say AI and SaaS may need more, but do not define the extra elements. AI elements are in separate G7 guidance from May 2026.
Where Safeguard fits, honestly
Safeguard's fit here is partial. SBOM generation and analysis is generally available (SPDX in JSON, XML and RDF, CycloneDX in JSON and XML, 30+ export formats including CSV, Excel and PDF, continuous scanning on push, schedule or release). The Third-Party Risk Manager, also generally available, lets you onboard vendors, request SBOMs, projects or products from them, and score and monitor supplier risk. CSAF and VEX upload, policies and gates, and attestation and signing (SLSA, Sigstore keyless with Fulcio and Rekor, in-toto, X.509) are generally available too. AI-BOM discovery is in early access: it inventories AI assets across code (SDK imports), cloud (SageMaker, Bedrock, Vertex), SaaS (shadow AI) and vector databases, and expresses them as a CycloneDX ML-BOM and in the SPDX AI profile.
Safeguard does not generate HBOMs, CBOMs or any cryptographic inventory, OBOMs, MBOMs, KBOMs, SaaSBOMs or dataset BOMs, and it does not scan firmware. For those types, use the open source tools above.
Fix it without waiting for upstream
A BOM of any type only tells you what is there; when it points at a vulnerable open source dependency inside a model server, a crypto library or a service, someone still has to ship the fixed build. For the CBOM, HBOM and SaaSBOM parts of a product the fix often comes from a vendor, so the steps below apply mainly to the open source software you build yourself.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. You install it, open the interactive terminal, ask it to fix the lodash CVE in a repository, and it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, and prints a summary without pushing anything. The same approval driven flow is how you would act on a vulnerable library that an AIBOM or SBOM review turned up in your own code.
Click through it at your own pace, or open it full screen.
The second demo sets up the Safeguard runner on your own machine, approves it with its device code, and then scans from the terminal. For BOM review this matters when supplier BOMs or the code behind them must stay on your own hardware.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and shows how you choose which tools Claude can use. Once connected, you can ask about projects, findings and the components in your SBOMs from the chat you already use, which is a quick way to follow up on what a BOM review flagged.
Click through it at your own pace, or open it full screen.
You can get the apps at safeguard.sh/download and read the CLI docs at docs.safeguard.sh/docs/cli.
Quick check
A supplier's CBOM passes the CycloneDX 1.6 schema and a cdxgen cbom audit with no findings. Is it good enough?
Not yet. The schema checks structure and the audit checks for weak or expired crypto in what is listed. Neither tells you whether the list is complete. Check the compositions, check that every algorithm has parameters and every asset links to code, and ask how assets were detected.
An AIBOM has no compositions block. What should you assume about its completeness?
Assume unknown. Without compositions the author has made no completeness claim, so you cannot conclude that a model or dataset is absent just because it is not listed.
Sources
- CISA, 2026 Minimum Elements for a Software Bill of Materials (SBOM) and the guidance PDF
- CISA, Software Bill of Materials for AI, Minimum Elements (resource page)
- CISA, Software Transparency in SaaS Environments
- CISA SBOM hub
- CycloneDX specification repository, JSON schemas for 1.6 and 1.7
- Ecma International, ECMA-424 CycloneDX Bill of materials specification
- CycloneDX, Cryptography Bill of Materials (CBOM)
- CycloneDX, Software as a Service (SaaSBOM)
- CycloneDX, Hardware Bill of Materials (HBOM)
- CycloneDX, Machine Learning Bill of Materials (ML-BOM)
- CycloneDX, Operations Bill of Materials (OBOM)
- CycloneDX CLI README
- cdxgen README
- cdxgen BOM audit documentation
- cdxgen cdx-validate guide
- SPDX 3.0.1 specification, serialization and validation
- SPDX 3.0.1 specification, AIPackage class
- SPDX tools-python README
- spdx3-validate README
- OWASP GenAI Security Project, AI Bill of Materials initiative
- OWASP AIBOM Generator repository
- OpenSSF SBOM Everywhere SIG
Related reading on this blog: SBOM quality metrics beyond completeness, SBOM validation quality checks, CycloneDX compositions, Validate an SBOM before you accept it and How to tell if an SBOM is complete.
Last reviewed: 23 October 2026.