To generate an AIBOM with open source tools, run the OWASP AIBOM Generator on a Hugging Face model ID for a scored CycloneDX ML-BOM, and run cdxgen's aibom command on your repository to inventory the models, AI services, prompts and MCP configs it uses. Then add the fields no tool can infer, validate, sign and store it.
Both tools write CycloneDX. Neither one writes a native SPDX 3 AI profile document today, so if a customer or regulator asks for SPDX, you either convert (and accept that AI fields travel as extensions) or emit the ai_AIPackage elements yourself. This guide walks through all of it with real commands, two schema valid examples, and a CI workflow you can copy.
Key takeaways
- Two tools, two jobs. The OWASP AIBOM Generator describes one model on Hugging Face. cdxgen describes what a codebase uses: model references, inference services, agent frameworks, prompt files, MCP servers and local model files.
- Both emit CycloneDX. The OWASP generator's command line writes CycloneDX 1.6 and 1.7 files side by side. cdxgen defaults to CycloneDX 1.7 and accepts 1.6 and 2.0 as generation targets.
- The OWASP generator scores completeness. It weights five field categories to a 100 point total and defines basic, standard and advanced profiles with minimum scores of 40, 70 and 85.
- cdxgen audits as it generates. The
ai-bomaudit alias runs governance, security, performance, agent and MCP rule packs, and flags pickle checkpoints because loading them executes code. - Humans still own the hard fields. Intended use, out of scope use, limitations, training data provenance, sensitive data use, risk assessment and energy figures are rarely in machine readable metadata. Plan for a review step.
- SPDX 3 AI profile output is manual for now. cdxgen's SPDX 3.0.1 export declares the core and software profiles and carries ML metadata as extension properties; the OWASP generator's SPDX path is a placeholder in the code. A minimal hand written
ai_AIPackageexample is below. - Treat the AIBOM like a release artifact. Validate it against the schema, sign it (JSF with
cdx-sign, or a Sigstore bundle withcosign), and store it next to the build it describes.
What an AIBOM generator can and cannot know
An AI bill of materials records what an AI system is made of: the models, their versions and suppliers, the datasets they were trained or evaluated on, the software around them, and the context a reviewer needs to judge risk. If you need the basics first, start with our AIBOM explainer, the field by field guide to what goes in an ML-BOM for a model, and the CycloneDX and SPDX AIBOM comparison. This post assumes you know what an AIBOM is and want to produce one.
Generators work from evidence they can read: hub metadata, model card text, config.json and weight file headers for a model; manifests, imports, configuration and model files for a codebase. Anything never written down in a machine readable place, such as what the model must not be used for or which internal dataset a fine tune saw, has to come from a person.
That split shapes the whole workflow:
- Generate what can be generated.
- Measure what is missing.
- Fill the gaps by hand, in the same document, under review.
- Validate, sign, store, and regenerate when the model or code changes.
What the OWASP GenAI Security Project AIBOM initiative publishes
The AIBOM work at OWASP lives under the OWASP GenAI Security Project, not under a standalone owasp.org project page. Its home is genai.owasp.org/owasp-aibom, which describes the initiative as moving AIBOM "from a theoretical framework into a practical, community-driven implementation" backed by open source tooling and measurable completeness assessment. The page links a Slack channel, a GitHub organisation, an initiative charter, and the tool itself.
What it publishes today:
- The OWASP AIBOM Generator. The resource page, dated 17 December 2025, describes an open source tool that generates AIBOMs for models hosted on Hugging Face in CycloneDX format, with a human friendly viewer, JSON download, and a field checklist with completeness scoring.
- A hosted instance and source code. The repository README points to a live instance at owasp-genai-aibom.org, and the code is at github.com/GenAI-Security-Project/aibom-generator under the Apache 2.0 license. A Hugging Face Space for the generator also exists.
- A field mapping document. The repository's
docs/aibom-field-mapping/README.mddefines 24 language agnostic AIBOM fields and maps each one to CycloneDX 1.7 and SPDX 3.0.1 AI profile properties. It is the most useful single page in the project if you are designing your own AIBOM template, and we reuse it below.
Status. The OWASP GenAI Security Project's December 2025 blog post says the generator was introduced at RSAC 2025 and was then contributed to the OWASP GenAI Security Project, and lists planned work: better AI relevant field checks, better automated extraction, a roadmap of integration endpoints, and an AIBOM generation handbook. The repository was actively maintained when we checked it on 23 October 2026 (latest commit 17 October 2026), but it publishes no tagged releases, so you pin a commit rather than a version. Treat it as a working community tool, not a frozen standard. The standards it writes to are CycloneDX (ECMA-424) and SPDX; the initiative does not define its own format.
The two tools at a glance
| OWASP AIBOM Generator | cdxgen aibom / -t ai | |
|---|---|---|
| Home | OWASP GenAI Security Project | CycloneDX GitHub organisation |
| Main input | A Hugging Face model ID | A source directory, a Hugging Face URL or purl, a Modelfile, or a GGUF file |
| What it describes | One model and its card | A codebase's AI usage, plus single models when pointed at one |
| Output format | CycloneDX JSON | CycloneDX JSON (protobuf optional); SPDX 3.0.1 JSON-LD via --format spdx or cdx-convert |
| CycloneDX versions | Writes 1.6 and 1.7 files per run; validates against 1.6 | Defaults to 1.7; 1.6 and 2.0 as targets; 1.4 and 1.5 by downgrade |
| Quality signal | Weighted completeness score with improvement tips | BOM audit rule packs; cdx-validate benchmarks |
| Signing | None built in | cdx-sign and cdx-verify (JSF), automatic signing via environment variables |
| Interfaces | Web app, CLI, API endpoints | CLI, standalone binaries, container images, REPL, server |
| Runtime | Python 3.11 or later | Node.js 24 or later for the npm package, or a standalone binary |
They answer different questions, so many teams run both: the OWASP generator for each third party model they adopt, and cdxgen in CI for every application that calls a model.
Walkthrough 1: an AIBOM for a Hugging Face model with the OWASP AIBOM Generator
Install and run
The generator is a Python application. Clone it, pin the commit you reviewed, and install dependencies with pip or uv, as the README shows:
git clone https://github.com/GenAI-Security-Project/aibom-generator.git
cd aibom-generator
git rev-parse HEAD # record the commit you are running
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
# or, with uv:
# uv sync
The project requires Python 3.11 or later (from pyproject.toml) and builds every document through the official cyclonedx-python-lib, which also handles schema validation and SPDX license IDs.
Generate an AIBOM for a model:
python3 -m src.cli google-bert/bert-base-uncased
Override identity fields when you are documenting a model you host or have renamed internally, and choose where the output goes:
python3 -m src.cli google-bert/bert-base-uncased \
--name "ticket-router-encoder" \
--version "1.0.0" \
--manufacturer "Example Corp" \
--output aiboms/bert-base-uncased.json
The documented options are --output (-o), --name (-n), --version (-v), --manufacturer (-m), --inference (-i, AI assisted metadata extraction that needs an API key), --summarize (-s, description summarization), --test (-t, a predefined batch of models) and --verbose. Without --output, results go to an sboms/ directory.
Two details from the code are worth knowing before you wire this into anything:
- Each run writes two files. The command line controller exports the same internal AIBOM twice, once with
specVersion1.6 and once with 1.7, each with the matching$schemaURL. Pick one and be consistent. - The validation report is for 1.6. The validator is built against the CycloneDX 1.6 schema, and the report for the 1.7 file reuses that result. If you ship the 1.7 file, validate it yourself against the 1.7 schema (commands below).
The code reads an HF_TOKEN environment variable; set it for gated or private models and keep it out of logs.
Use the web app or hosted instance
python3 -m src.main starts the same generator as a local web app on port 8000, with a viewer and JSON download. The hosted instance does the same for public models; for anything internal, run it yourself.
Read the completeness score
The scoring is defined in src/models/field_registry.json, so you can read exactly how it works:
| Category | Weight (points) |
|---|---|
| Required fields | 20 |
| Metadata | 20 |
| Component basics | 20 |
| Model card | 30 |
| External references | 10 |
Within categories, fields are tiered as critical (weight 3), important (2) or supplementary (1), and the algorithm applies a penalty for missing critical fields. Three profiles set the bar:
| Profile | Minimum score | Required fields (abridged) |
|---|---|---|
| Basic | 40 | bomFormat, specVersion, serialNumber, version, name |
| Standard | 70 | Basic plus download location, primary purpose, supplier |
| Advanced | 85 | Standard plus type, purl, description, licenses, hyperparameters, technical limitations, energy consumption, safety risk assessment, type of model |
Use the score like test coverage: a low score reliably means a thin model card, and a high score means the fields are filled, not that they are true. Two practical rules:
- Make "standard" the bar for any third party model that reaches a production build.
- Keep the improvement tips the tool prints and turn them into review tasks for the model's owner.
Walkthrough 2: an AI-BOM from a repository with cdxgen
Install
cdxgen is the CycloneDX project's multi ecosystem generator. Its README lists an aibom command alongside cbom, obom, saasbom and hbom. Install it one of these ways:
# npm package (needs Node.js 24 or later)
npm install -g @cdxgen/cdxgen --ignore-scripts
# Homebrew
brew install cdxgen
# container image, scanning the current directory
docker run --rm -v "$(pwd)":/app:rw -t ghcr.io/cdxgen/cdxgen:master -r /app -o /app/bom.json
Standalone release binaries named aibom-linux-amd64, aibom-darwin-arm64 and aibom-windows-amd64.exe are also published, along with cdx-validate, cdx-sign, cdx-verify and cdx-convert binaries. In CI, pin a version: the npm package was at 13.4.0 when we checked on 23 October 2026. The older @cyclonedx/cdxgen package name still exists on npm but trails behind.
Scan a repository
cdxgen's AI-BOM guide gives three equivalent entry points:
# 1. The dedicated command (defaults: --type ai, --include-formulation,
# --bom-audit-categories ai-bom)
aibom .
# 2. The explicit project type, when you also want other cdxgen flags
cdxgen -t ai --include-formulation -o aibom.cdx.json .
# 3. A full recursive scan with the AI audit pack switched on
cdxgen -r --include-formulation -o aibom.cdx.json \
--bom-audit --bom-audit-categories ai-bom .
The project type aliases ai, aibom and ai-bom are interchangeable. Use aibom for a focused scan and cdxgen -t ai when you want to combine AI inventory with other project types or flags. To pin the CycloneDX version, add --spec-version 1.6 or --spec-version 1.7.
One trap from the README: --component-type machine-learning-model is a filter, not a discovery switch. Passing it does not turn on model discovery, and filtering to a type the scan did not produce gives you an empty component list.
Point it at a single model
The same command takes direct model inputs:
aibom pkg:huggingface/google-bert/bert-base-uncased@86b5e0934494bd15c9632b12f734a8a67f723594
aibom https://huggingface.co/google-bert/bert-base-uncased
aibom /absolute/path/to/Modelfile
aibom /absolute/path/to/model.gguf
For Hugging Face inputs, cdxgen resolves hub metadata into standard components: model repositories become machine-learning-model, dataset repositories become data, and Spaces become application. It keeps an explicit revision in the purl when you give one, turns model card dataset references into reusable data components with dependency edges, and normalises base_model, base_models and finetuned_from into pedigree.ancestors. Pinning the revision matters: a model ID without one points at whatever the default branch holds today.
What lands where in the output
With --include-formulation, AI and agent evidence goes into the standard CycloneDX formulation section (formulation[].components, formulation[].services, formulation[].workflows), and discovered services are also merged into the top level services array so ordinary BOM consumers see them.
According to the guide, a repository scan can record inference services found in source or configuration, model components and local model files, agent frameworks and SDKs, prompt and model routing files, agent instruction and skill files, and MCP configurations, services, Registry server.json manifests and MCPB or DXT bundles.
Local model files get format specific handling without loading tensors or running model code:
| File type | What cdxgen records |
|---|---|
| GGUF | Three layers: a file component for the artifact, a machine-learning-model component for the model, and modelCard plus pedigree fields such as architecture family, task, datasets and base model, with GGUF details in cdx:gguf:* properties |
| safetensors | Header summary: tensor count, data types, parameter totals (cdx:safetensors:*), and quantization method when present |
| ONNX | Exporter, exporter version, IR version and operator sets (cdx:onnx:*), read from the header without loading the graph |
| PyTorch pickle | Inventoried and flagged cdx:ai:unsafeDeserialization=true, because loading a pickle checkpoint executes code |
cdxgen deliberately leaves raw tokenizer vocabularies, chat templates and widget conversations out of the BOM, writing counts, booleans, URLs and bounded identifiers instead.
Run and rerun the AI audit
The ai-bom audit alias expands to five categories: ai-governance, ai-security, ai-performance, ai-agent and mcp-server. The guide lists examples of what they look for: prompt or routing configs shipped in build artifacts, AI services used without an explicit model identifier, remote AI endpoints over plain HTTP, pickle based model artifacts, very large context windows on local models, and large local models without quantization metadata. The rule packs carry guidance mappings to the OWASP Top 10 lists for LLM and agentic applications, the EU AI Act, the EU Cyber Resilience Act, NIST AI RMF and NIST SSDF.
You can audit a saved AI-BOM later without rescanning:
cdx-audit --bom aibom.cdx.json --direct-bom-audit --categories ai-bom
cdx-audit --bom aibom.cdx.json --direct-bom-audit --categories ai-governance
For an interactive look, cdxi has an .aibom command that renders pedigree trees, fine tune and distillation lineage, quantization, licenses and dataset summaries.
A minimal, schema valid CycloneDX 1.7 ML-BOM
Here is the shape both tools aim for, cut down to the fields that matter. It describes an internal application, ticket-router, that uses bert-base-uncased at a pinned Hugging Face revision, with the two pretraining datasets named on the model card as data components. We validated this exact document against the CycloneDX 1.7 JSON schema (bom-1.7.schema.json) on 23 October 2026.
{
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.7",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"component": {
"type": "application",
"bom-ref": "app-ticket-router",
"name": "ticket-router",
"version": "2.3.0"
}
},
"components": [
{
"type": "machine-learning-model",
"bom-ref": "model-bert-base-uncased",
"supplier": { "name": "Google" },
"group": "google-bert",
"name": "bert-base-uncased",
"version": "86b5e0934494bd15c9632b12f734a8a67f723594",
"purl": "pkg:huggingface/google-bert/bert-base-uncased@86b5e0934494bd15c9632b12f734a8a67f723594",
"licenses": [ { "license": { "id": "Apache-2.0" } } ],
"externalReferences": [
{ "type": "distribution", "url": "https://huggingface.co/google-bert/bert-base-uncased" }
],
"modelCard": {
"modelParameters": {
"approach": { "type": "self-supervised" },
"task": "fill-mask",
"architectureFamily": "BERT",
"modelArchitecture": "BertForMaskedLM",
"datasets": [ { "ref": "data-bookcorpus" }, { "ref": "data-wikipedia" } ],
"inputs": [ { "format": "text" } ],
"outputs": [ { "format": "text" } ]
},
"considerations": {
"useCases": [ "Fine-tuned here to route English support tickets to queues" ],
"technicalLimitations": [ "English only; predictions can reflect biases in the pretraining text" ]
}
}
},
{
"type": "data",
"bom-ref": "data-bookcorpus",
"name": "bookcorpus",
"data": [ { "type": "dataset", "name": "bookcorpus" } ]
},
{
"type": "data",
"bom-ref": "data-wikipedia",
"name": "wikipedia",
"data": [ { "type": "dataset", "name": "wikipedia" } ]
}
],
"dependencies": [
{ "ref": "app-ticket-router", "dependsOn": [ "model-bert-base-uncased" ] },
{ "ref": "model-bert-base-uncased", "dependsOn": [ "data-bookcorpus", "data-wikipedia" ] },
{ "ref": "data-bookcorpus" },
{ "ref": "data-wikipedia" }
]
}
A few choices in that example are deliberate:
- The version is the commit. Hugging Face model repositories are Git repositories. The revision hash (taken from the hub API on 23 October 2026) identifies exact weights; a tag like "main" does not. The purl carries the same revision.
- Datasets are components, referenced by
bom-ref.modelParameters.datasetsaccepts either inline data or a reference. References let several models share one dataset entry and let the dependency graph show model to data edges. approach.typeis an enum. The 1.7 schema acceptssupervised,unsupervised,reinforcement-learning,semi-supervisedandself-supervised. A free text value fails validation.useCasesandtechnicalLimitationsare ours, not the supplier's. They describe how this application uses the model. That is exactly the content a generator cannot produce.
For the full CycloneDX field set, including quantitativeAnalysis, fairnessAssessments and environmentalConsiderations, see our CycloneDX ML-BOM 1.7 implementation guide. If your model weights are split across many files, read how to hash a sharded model before you add hashes.
What each tool fills in, and which fields still need a human
This table uses the field list from the OWASP field mapping document. The "OWASP generator" and "cdxgen" columns describe where each tool can find the data according to its own documentation and code; real coverage depends on how complete the model card is.
| Field | CycloneDX 1.7 location | OWASP generator | cdxgen | Needs a human? |
|---|---|---|---|---|
| Model name, version | name, version | Yes, with overrides | Yes; revision kept from purl | Confirm the pinned revision |
| Supplier | supplier | Yes, with override | From hub metadata | Confirm the legal supplier |
| License | licenses | Yes, SPDX IDs | Yes | Check custom and gated licenses |
| Download location, source repo | externalReferences | Yes | Yes | Rarely |
| Task (primary purpose) | modelCard.modelParameters.task | Yes | From pipeline_tag, model-index or GGUF | Rarely |
| Type of model, architecture | approach, architectureFamily, modelArchitecture | From config | From config and GGUF | Sometimes |
| Hyperparameters | modelCard.modelParameters and properties | From config files | Partly, as properties | Often, for training values |
| Training and test datasets | modelCard.modelParameters.datasets, formulation | Names from the card | Names and data components from the card | Yes: provenance, consent, licensing |
| Model lineage | pedigree.ancestors | When declared | From base_model fields and GGUF | Confirm for internal fine tunes |
| Runtime dependencies | dependencies | Limited | Yes, from the codebase | Rarely |
| Performance metrics | modelCard.quantitativeAnalysis | When on the card | When on the card | Usually: your own evaluation |
| Intended use, out of scope use | considerations.useCases | Card text if present | Derived from task and tags | Yes |
| Known limitations | considerations.technicalLimitations | Card text if present | Rarely | Yes |
| Ethical and fairness considerations | ethicalConsiderations, fairnessAssessments | Rarely | Rarely | Yes |
| Energy consumption | environmentalConsiderations | When on the card | Bounded CO2 values when on the card | Usually |
| Sensitive personal data use | Properties (partial in CycloneDX) | Rarely | No | Yes |
| Safety risk assessment | Properties (partial in CycloneDX) | Rarely | No | Yes |
Training data
This is the field reviewers ask about first and the one tools fill worst. A model card that says "trained on Wikipedia" gives a generator a name, not a version, a snapshot date, a license position, or any statement about personal data. For third party models, record what the supplier published and link it. For models you train or fine tune, generate the dataset entries from your own pipeline: the dataset ID and version from your data catalogue, the preprocessing steps (CycloneDX puts these under formulation, SPDX under modelDataPreprocessing), and whether personal data was in scope. Our post on AI supply chain security for models and datasets covers why dataset provenance matters.
Intended use and out of scope use
cdxgen can infer useCases from a task and tags, and the OWASP generator lifts whatever the card says. Neither knows how your product uses the model. Write one or two plain sentences about the deployed use, and one about uses you rule out. Keep them in the same AIBOM so the statement travels with the artifact.
Limitations, risk and fairness
technicalLimitations, ethicalConsiderations and fairnessAssessments exist in CycloneDX; SPDX has limitation, safetyRiskAssessment and related properties. These need the people who evaluated the model. A useful convention is to have the model's owner sign off on these fields in the pull request that changes the AIBOM, so the review history sits in version control.
Why these fields matter beyond good practice
Regulation is the usual driver. For high risk AI systems, Article 11 of the EU AI Act (Regulation (EU) 2024/1689) requires technical documentation, and Annex IV lists its contents, including descriptions of the data used for training and its provenance. The Act does not require any specific BOM format. An AIBOM is a convenient, machine readable place to keep part of that evidence, not a substitute for the documentation itself. Read the regulation text on EUR-Lex for the exact obligations that apply to you.
How to emit the SPDX 3 AI profile equivalent
What the AI profile requires
SPDX 3.0.1 models an AI system or trained model as an AIPackage in the AI profile, a subclass of Software/Package. According to the AIPackage class page, these inherited properties become mandatory on an AI package: releaseTime, suppliedBy, downloadLocation, packageVersion and primaryPurpose, in addition to the usual spdxId, name and creationInfo. The AI profile page adds a conformance rule: every AIPackage must have exactly one hasConcludedLicense relationship and exactly one hasDeclaredLicense relationship.
The AI specific properties are all optional: autonomyType, domain, energyConsumption, hyperparameter, informationAboutApplication, informationAboutTraining, limitation, metric, metricDecisionThreshold, modelDataPreprocessing, modelExplainability, safetyRiskAssessment, standardCompliance, typeOfModel and useSensitivePersonalInformation. Training and test datasets are linked with trainedOn and testedOn relationships to dataset packages from the Dataset profile. Our SPDX 3.0 AI profile guide covers each property.
What the open source tools emit today
| Route | What you get | AI profile? |
|---|---|---|
cdxgen --format spdx or cdx-convert -i aibom.cdx.json -o aibom.spdx.json | SPDX 3.0.1 JSON-LD, validated by default | No. The document declares the core and software profiles plus extension; ML model data is preserved as CycloneDX property extensions on software packages |
| OWASP AIBOM Generator | CycloneDX only | No. The SPDX branch in src/utils/formatter.py is marked as a placeholder for future work |
| Hand written or scripted from your CycloneDX | Whatever you emit | Yes, if you write ai_AIPackage elements and the license relationships |
We checked the cdxgen converter source on 23 October 2026: it contains no ai_ properties, and its test suite confirms that machine-learning-model data survives as extension properties. That is a sensible lossless choice, but a consumer that expects AI profile properties will not find them. cdx-convert accepts only CycloneDX 1.6 or 1.7 JSON or protobuf as input for the SPDX path.
A minimal SPDX 3.0.1 AI profile document
This is the same model as the CycloneDX example, expressed natively in the AI profile. It validates against the SPDX 3.0.1 JSON schema (we checked on 23 October 2026), and it includes the two license relationships the AI profile requires. The releaseTime is the last modified time of that revision as reported by the hub. Replace the example.com IDs with a namespace you control.
{
"@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/agent/platform-team" ]
},
{
"type": "Organization",
"spdxId": "https://example.com/spdx/agent/platform-team",
"name": "Example platform team",
"creationInfo": "_:creationinfo"
},
{
"type": "Organization",
"spdxId": "https://example.com/spdx/agent/google",
"name": "Google",
"creationInfo": "_:creationinfo"
},
{
"type": "ai_AIPackage",
"spdxId": "https://example.com/spdx/ai/bert-base-uncased",
"creationInfo": "_:creationinfo",
"name": "bert-base-uncased",
"software_packageVersion": "86b5e0934494bd15c9632b12f734a8a67f723594",
"software_downloadLocation": "https://huggingface.co/google-bert/bert-base-uncased",
"software_packageUrl": "pkg:huggingface/google-bert/bert-base-uncased@86b5e0934494bd15c9632b12f734a8a67f723594",
"software_primaryPurpose": "model",
"suppliedBy": "https://example.com/spdx/agent/google",
"releaseTime": "2024-02-19T11:06:12Z",
"ai_typeOfModel": [ "transformer encoder" ],
"ai_domain": [ "natural language processing" ],
"ai_informationAboutTraining": "Self-supervised masked language modelling on BookCorpus and English Wikipedia",
"ai_limitation": "English only; predictions can reflect biases in the pretraining text",
"ai_autonomyType": "no",
"ai_useSensitivePersonalInformation": "noAssertion"
},
{
"type": "simplelicensing_LicenseExpression",
"spdxId": "https://example.com/spdx/license/apache-2.0",
"creationInfo": "_:creationinfo",
"simplelicensing_licenseExpression": "Apache-2.0"
},
{
"type": "Relationship",
"spdxId": "https://example.com/spdx/rel/declared",
"creationInfo": "_:creationinfo",
"from": "https://example.com/spdx/ai/bert-base-uncased",
"relationshipType": "hasDeclaredLicense",
"to": [ "https://example.com/spdx/license/apache-2.0" ]
},
{
"type": "Relationship",
"spdxId": "https://example.com/spdx/rel/concluded",
"creationInfo": "_:creationinfo",
"from": "https://example.com/spdx/ai/bert-base-uncased",
"relationshipType": "hasConcludedLicense",
"to": [ "https://example.com/spdx/license/apache-2.0" ]
},
{
"type": "software_Sbom",
"spdxId": "https://example.com/spdx/sbom/ticket-router-aibom",
"creationInfo": "_:creationinfo",
"profileConformance": [ "core", "software", "simpleLicensing", "ai" ],
"software_sbomType": [ "analyzed" ],
"rootElement": [ "https://example.com/spdx/ai/bert-base-uncased" ],
"element": [
"https://example.com/spdx/agent/google",
"https://example.com/spdx/ai/bert-base-uncased",
"https://example.com/spdx/license/apache-2.0",
"https://example.com/spdx/rel/declared",
"https://example.com/spdx/rel/concluded"
]
}
]
}
Two cautions. First, the JSON schema checks structure and required properties; it does not enforce profile conformance rules such as "exactly one hasConcludedLicense". Check those yourself or with a tool that applies the SPDX SHACL shapes. Second, if you also want the datasets, add Dataset profile packages for BookCorpus and Wikipedia and trainedOn relationships from the model, and declare the dataset profile.
Mapping CycloneDX fields to the AI profile
If you script the conversion from a CycloneDX AIBOM, the OWASP field mapping gives you a starting table. The most used rows:
| CycloneDX 1.7 | SPDX 3.0.1 AI profile | Note |
|---|---|---|
component.name, component.version | name, software_packageVersion | Direct |
component.description | summary | Direct |
externalReferences type distribution | software_downloadLocation | Required in SPDX |
component.supplier | suppliedBy (an Agent element) | Required in SPDX |
component.licenses | hasDeclaredLicense and hasConcludedLicense relationships | SPDX uses relationships |
modelCard.modelParameters.task | software_primaryPurpose plus ai_domain | SPDX purpose is an enum; put the task text in ai_domain or ai_informationAboutApplication |
modelCard.modelParameters.approach | ai_typeOfModel | Free text in SPDX |
modelCard.quantitativeAnalysis | ai_metric, ai_metricDecisionThreshold | CycloneDX thresholds are partial |
considerations.technicalLimitations | ai_limitation | Direct |
environmentalConsiderations | ai_energyConsumption | Structures differ |
formulation | ai_informationAboutTraining, ai_modelDataPreprocessing | Text in SPDX |
pedigree.ancestors | Lineage relationships between packages | Graph versus tree |
modelCard.modelParameters.datasets | trainedOn, testedOn relationships | SPDX separates training and test roles |
For the wider differences between the two formats, see our CycloneDX vs SPDX comparison.
How to validate, sign and store the result in CI
Validate
Validate every AIBOM against the schema for the version you claim. Three open source routes:
# CycloneDX CLI: schema validation, non zero exit on errors
cyclonedx validate --input-file aibom.cdx.json --input-version v1_7 --fail-on-errors
# cdxgen's validator: schema plus deep checks and benchmarks
cdx-validate -i aibom.cdx.json --strict --fail-severity high
# SPDX 3.0.1 JSON-LD: cdx-convert validates its own SPDX output by default
cdx-convert -i aibom.cdx.json -o aibom.spdx.json
cdx-validate exit codes are documented: 0 for a pass, 2 when schema or deep validation fails under --strict, 3 for findings at or above --fail-severity, and 4 when a required signature check fails. It can also emit SARIF (-r sarif -o results.sarif) for code scanning views, and run benchmark sets such as scvs-l2 and cra. For a hand written SPDX document like the one above, validate against the published SPDX 3.0.1 JSON schema with any JSON Schema 2020-12 validator, then check the profile rules.
Sign
There are two signing models, and they solve different problems.
Signatures inside the BOM (JSF). CycloneDX supports JSON Signature Format signatures embedded in the document. cdxgen's cdx-sign adds them, supports replace, signers (parallel) and chain (sequential) modes, and can sign nested components, services and annotations:
# builder signs at generation time
cdx-sign -i aibom.cdx.json -o aibom.signed.cdx.json -k builder_private.pem -a ES256
# model owner adds a second signature after reviewing the human fields
cdx-sign -i aibom.signed.cdx.json -k owner_private.pem -a ES256 \
--mode signers --verify-existing-with builder_public.pem
# consumer verifies structure and signatures together
cdx-validate -i aibom.signed.cdx.json --public-key builder_public.pem
The signers mode fits AIBOMs well: the build signs what it generated, and the reviewer adds a signature over the reviewed document. cdxgen can also sign automatically during generation when SBOM_SIGN_ALGORITHM and SBOM_SIGN_PRIVATE_KEY are set.
Detached Sigstore signatures and attestations. If your organisation already verifies artifacts with Sigstore, sign the AIBOM file as a blob with a keyless identity from CI, which writes a bundle you store next to it:
cosign sign-blob --yes --bundle aibom.cdx.json.sigstore.json aibom.cdx.json
cosign verify-blob --bundle aibom.cdx.json.sigstore.json \
--certificate-identity "https://github.com/ORG/REPO/.github/workflows/aibom.yml@refs/heads/main" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
aibom.cdx.json
When the model ships inside a container image, attach the AIBOM to the image as an in-toto attestation instead. cosign attest accepts --type cyclonedx (and spdxjson) as predicate types:
cosign attest --yes --type cyclonedx --predicate aibom.cdx.json registry.example.com/ticket-router@sha256:IMAGE_DIGEST
Always attest by digest, not by tag. Our post on signed SBOMs and attestations in procurement explains why buyers ask for them.
Store
Store the AIBOM next to what it describes, with the same version: as release assets (the BOM, its signature bundle and any SPDX copy), as an OCI attestation or a cdx-sign --attach reference on the image, and in a BOM repository that keeps history, so you can answer "which model revision did 2.3.0 ship with" months later.
Regenerate on every release and whenever a model revision, dataset or prompt configuration changes.
A GitHub Actions workflow
This workflow generates a CycloneDX 1.7 AI-BOM for the repository, fails the job on schema errors or high severity audit findings, signs the file keylessly, and uploads both as build artifacts. Pin action and package versions to the ones you have reviewed.
name: aibom
on:
push:
branches: [main]
release:
types: [published]
permissions:
contents: read
id-token: write # keyless Sigstore signing
jobs:
aibom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- name: Install cdxgen
run: npm install -g @cdxgen/cdxgen@13.4.0 --ignore-scripts
- name: Generate and audit the AI-BOM
run: |
cdxgen -t ai --include-formulation --spec-version 1.7 \
--bom-audit --bom-audit-categories ai-bom \
-o aibom.cdx.json .
- name: Validate
run: cdx-validate -i aibom.cdx.json --strict --fail-severity high
- name: Export SPDX 3.0.1 copy
run: cdx-convert -i aibom.cdx.json -o aibom.spdx.json
- uses: sigstore/cosign-installer@v3
- name: Sign
run: cosign sign-blob --yes --bundle aibom.cdx.json.sigstore.json aibom.cdx.json
- uses: actions/upload-artifact@v4
with:
name: aibom
path: |
aibom.cdx.json
aibom.cdx.json.sigstore.json
aibom.spdx.json
For the human fields, keep a small reviewed overlay file per model (intended use, limitations, data provenance) in the repository and merge it into the generated AIBOM in a step before validation. That keeps generated and authored content separate, and every change to the authored part goes through code review.
How Safeguard AI-BOM discovery fits
Safeguard's AI-BOM discovery is in early access. It inventories AI assets across code (SDK imports), cloud AI platforms such as SageMaker, Bedrock and Vertex, SaaS AI use (shadow AI) and vector databases, and outputs ML-BOM in CycloneDX and the SPDX AI profile. The open source tools in this post look at one model or one repository at a time; discovery is aimed at the estate wide question of where AI is in use at all, including in places nobody scanned. Because it is early access, ask what it covers in your environment before you plan around it.
Around that, several generally available Safeguard capabilities apply to the AIBOMs you generate yourself. SBOM generation and analysis reads and writes CycloneDX (JSON, XML) and SPDX (JSON, XML, RDF) and can scan continuously on push, schedule or release. License compliance parses SPDX expressions and applies license policy, which matters for models under custom or gated licenses. Attestation and signing supports Sigstore (Fulcio and Rekor keyless), in-toto, SLSA and X.509. Policies and gates can block a release that lacks a valid, signed BOM. Model scanning for LLMs, SLMs, skills and agents is generally available; AI-SPM model artifact scanning is in early access.
What Safeguard does not do here: it does not fill in intended use, limitations or training data provenance for you, it does not author dataset or data BOMs, and it does not convert between formats beyond its listed export formats. Those remain the work described above.
Common mistakes
- Unpinned model versions. Recording
bert-base-uncasedwithout a revision describes a moving target. Use the commit hash inversionand the purl. - Treating the completeness score as a pass mark for truth. A full field can still be wrong. The score measures presence, not accuracy.
- Shipping the 1.7 file on the strength of a 1.6 validation. The OWASP generator validates against 1.6. Validate the file you ship against its own schema.
- Using
--component-type machine-learning-modelto "turn on" model discovery. It only filters. Useaibomor-t ai. - Assuming the SPDX export is an AI profile document. cdxgen's SPDX 3.0.1 output carries ML data as extensions. If a consumer needs
ai_AIPackage, emit it. - Leaving the human fields blank, or copying the supplier's card verbatim. Supplier text describes the supplier's intended use, not yours.
- Generating once and never again. Prompt files, routing configs and model revisions change more often than library versions. Regenerate on every release.
- Forgetting API only models. If your application calls a hosted model it never downloads, record the service and model identifier anyway. Our post on AIBOM entries for models you only call through an API covers how.
FAQ
What is the OWASP AIBOM Generator?
It is an open source tool from the OWASP GenAI Security Project that generates an AI bill of materials in CycloneDX JSON for models hosted on Hugging Face, with a viewer, a JSON download, API endpoints and a completeness score. You can run it locally from its GitHub repository or use the hosted instance for public models.
Does the OWASP AIBOM Generator support SPDX?
Not as output today. Its field mapping document covers SPDX 3.0.1 AI profile fields, but the SPDX export path in the code is marked as a placeholder. It writes CycloneDX 1.6 and 1.7.
How do I generate an AI-BOM with cdxgen?
Install cdxgen and run aibom . in your repository, or cdxgen -t ai --include-formulation -o aibom.cdx.json . when you want to combine it with other flags. Add --bom-audit --bom-audit-categories ai-bom to run the AI rule packs during generation.
Can cdxgen generate an AIBOM for a Hugging Face model?
Yes. Pass a Hugging Face purl or URL, for example aibom pkg:huggingface/ORG/MODEL@REVISION. cdxgen maps the model to a machine-learning-model component, its datasets to data components and its base model to pedigree.ancestors.
Which CycloneDX version should an AIBOM use?
Use 1.7 unless a consumer requires 1.6. cdxgen defaults to 1.7, and both 1.6 and 1.7 include the machine-learning-model and data component types and the modelCard object. Whatever you choose, validate against that version's schema.
Which AIBOM fields can't be generated automatically?
Intended use, out of scope use, known limitations, ethical and fairness considerations, sensitive personal data use, safety risk assessment and, for most models, training data provenance and energy figures. Generators only copy these when a model card states them.
How do I convert a CycloneDX AIBOM to SPDX 3?
Run cdx-convert -i aibom.cdx.json -o aibom.spdx.json. Input must be CycloneDX 1.6 or 1.7 JSON or protobuf. The output is SPDX 3.0.1 JSON-LD with ML data kept as extension properties, not AI profile properties.
How do I sign an AIBOM?
Either embed a JSF signature with cdx-sign and verify with cdx-verify or cdx-validate --public-key, or sign the file as a blob with cosign sign-blob --bundle and verify with cosign verify-blob. For container images, attach it with cosign attest --type cyclonedx.
Does the EU AI Act require an AIBOM?
No specific format is required. The Act requires technical documentation for high risk AI systems, and an AIBOM can hold part of that evidence in machine readable form. Check the regulation text for your obligations.
Fix it without waiting for upstream
An AIBOM tells you which model revision, framework and inference library you ship, but it does not change any of them; when one of those Python or JavaScript dependencies has a known vulnerability, someone still has to ship the fixed build. The fit with model weights themselves is indirect, so read what follows as applying to the open source packages and container images around your models.
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 terminal session with the Safeguard CLI. It reads a vulnerability finding and its advisory, asks before each command, upgrades a dependency, runs the tests with your approval and pushes nothing. The same flow applies when your AI-BOM shows that the inference library or tokenizer package your service ships with has a known fix.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with a device code and runs scans from the terminal. That suits teams who keep model code and internal model IDs on their own systems and would rather not send them to a hosted service, which is the same reason to run the AIBOM generators locally.
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 lets you choose which tools Claude can use. Once connected, you can ask questions about the components and vulnerabilities in your Safeguard projects, which is useful when you want to know which applications ship a framework your AIBOM flagged.
Click through it at your own pace, or open it full screen.
To try it on your own projects, download Safeguard at safeguard.sh/download and read the CLI documentation.
Quick check
You need an SPDX 3 document with AI profile properties for a model. Will cdx-convert produce one from your cdxgen AI-BOM?
Not with AI profile properties. cdx-convert writes valid SPDX 3.0.1 JSON-LD that declares the core and software profiles, and it keeps ML model data as extension properties. To get ai_AIPackage elements with the required license relationships, emit them yourself, as in the example above.
The OWASP AIBOM Generator gives a model a score of 88. Does that mean the AIBOM is accurate?
No. The score measures how many weighted fields are present, against basic, standard and advanced profiles with minimums of 40, 70 and 85. It does not check whether the content is true. Intended use, limitations and training data still need review by the model's owner.
Sources
- OWASP GenAI Security Project, AI Bill of Materials initiative
- OWASP GenAI Security Project, OWASP AIBOM Generator resource page
- OWASP GenAI Security Project, Evolving AI Transparency: The Journey of the AIBOM Generator and Its New Home at OWASP (18 December 2025)
- OWASP AIBOM Generator repository, README, field mapping document and source
- cdxgen README
- cdxgen AI-BOM guide
- cdxgen cdx-convert guide
- cdxgen cdx-validate guide
- cdxgen cdx-sign guide
- CycloneDX, Machine Learning Bill of Materials (AI/ML-BOM) capability
- CycloneDX 1.7 JSON schema
- CycloneDX CLI README
- SPDX 3.0.1, AI profile
- SPDX 3.0.1, AIPackage class
- SPDX 3.0.1, Software Package class
- SPDX 3.0.1 JSON schema
- Sigstore, cosign attest command reference
- Sigstore, cosign sign-blob command reference
- Sigstore, cosign verify-blob command reference
- Sigstore documentation, signing other types
- Hugging Face Hub API, google-bert/bert-base-uncased model metadata (used for the revision hash and dataset names in the examples)
- Regulation (EU) 2024/1689 (EU AI Act), EUR-Lex
Last reviewed: 23 October 2026. Both generators change often; check the linked repositories for current flags and output before you pin them in CI.