# AI Bill of Materials (AIBOM)

> An AI bill of materials (AIBOM) is a machine-readable inventory of the models, datasets, and libraries an AI system uses, including each model's lineage.

Also known as: AI-BOM, AI BOM, ML-BOM, MLBOM, AI SBOM, SBOM for AI

Source: https://socket.dev/glossary/aibom

Last updated: 2026-11-03

An AI bill of materials (AIBOM), also called an AI-BOM, ML-BOM, or SBOM for AI, is a machine-readable inventory, usually [CycloneDX](https://socket.dev/glossary/cyclonedx) or [SPDX](https://socket.dev/glossary/spdx), of the models, datasets, and software an AI system runs on, including each model's revision, hashes, lineage, and training claims. An AIBOM records what a system contains, not what that content does.

The difference from an ordinary [SBOM](https://socket.dev/glossary/software-bill-of-materials) is what the inventory has to describe. A library is code you can read. A model is a file of weights whose behavior comes from data you cannot inspect, usually derived from another model, and sometimes stored in a format that executes code when loaded. Only three kinds of AIBOM field can be checked against bytes: the repository revision, the file hashes, and the file format. Everything about training data, bias, and limitations is text supplied by the model's author.

Most of that text is missing anyway. A July 2026 study generated AIBOMs for 97,940 Hugging Face models and measured a mean completeness score of 54.31 out of 100. Only 39.35% named any dataset ([arXiv:2607.17242](https://arxiv.org/html/2607.17242)).

Our position: produce an AIBOM for every AI system you ship, pin every model by commit and file hash, and read everything else as a claim. Then analyze the model files and libraries themselves. Below, a pickle checkpoint that runs a shell command on load gets a schema-valid AIBOM entry with a correct SHA-256.

## What is an AIBOM?

An AIBOM is the AI layer of a software inventory. It names every model, dataset, and AI framework a system depends on, precisely enough to look each one up after an incident.

Answering that requires knowing what a model actually is on disk. A model repository on the Hugging Face Hub is a Git repository. It holds weight files, a config, a tokenizer, a README model card with YAML metadata, and sometimes Python code. Revisions are Git commits, and `main` moves whenever the author pushes. The Hub hosted over 2 million models in 2025, as we noted [when we added Hugging Face scanning](https://socket.dev/blog/announcing-experimental-malware-scanning-for-hugging-face).

Weight files come in two families. Pickle-based files (`.bin`, `.pt`, `.ckpt`, `.pkl`) are programs for Python's unpickler, which can import and call any function. Data-only formats such as safetensors hold a JSON header and raw tensor bytes. A model's lineage is metadata too: the card's `base_model` field names the parent, and `base_model_relation` takes `adapter`, `merge`, `quantized`, or `finetune`. The [Hub's model card documentation](https://huggingface.co/docs/hub/model-cards) describes how it displays these fields, not how it verifies them.

So an AIBOM has to answer four questions per model: which exact revision, which files, derived from what, and trained on what. The first two can be pinned to bytes. The last two are assertions.

## AIBOM contents: the G7 clusters and the CycloneDX model card

The reference list of AIBOM contents is the G7's Software Bill of Materials for AI – Minimum Elements. The [G7 Cybersecurity Working Group published it on May 12, 2026](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/KI/SBOM-for-AI_minimum-elements.html), led by Germany's BSI and Italy's ACN. It sorts elements into seven clusters: metadata, system-level properties, models, dataset properties, infrastructure, security properties, and key performance indicators. It supplements general SBOM minimum elements rather than replacing them, and the authoring agencies describe the elements as not mandatory.

CycloneDX has carried AI components since version 1.5, released in [June 2023](https://cyclonedx.org/news/cyclonedx-v1.5-released). It added the component types `machine-learning-model` and `data` and a `modelCard` object. Here is one model component for a placeholder repository, `example-org/sentiment-mini`, validated against the CycloneDX 1.6 JSON schema with cyclonedx-python-lib 11.12.0. The hash is that of the demo checkpoint we build further down:

```json
{
  "type": "machine-learning-model",
  "bom-ref": "model-sentiment-mini",
  "name": "sentiment-mini",
  "version": "9f2c41d0b7e3a5c8d1e6f4a2b9c0d7e5f3a1b8c6",
  "purl": "pkg:huggingface/example-org/sentiment-mini@9f2c41d0b7e3a5c8d1e6f4a2b9c0d7e5f3a1b8c6",
  "licenses": [{ "license": { "id": "Apache-2.0" } }],
  "hashes": [{ "alg": "SHA-256", "content": "b3d0bef37e65517900c6f4ba2b568bc0ae38d9e0767c3bcab5b86227b5027a0a" }],
  "pedigree": {
    "ancestors": [{ "type": "machine-learning-model", "name": "distilbert-base-uncased",
                    "purl": "pkg:huggingface/distilbert/distilbert-base-uncased" }]
  },
  "modelCard": {
    "modelParameters": {
      "approach": { "type": "supervised" },
      "task": "text-classification",
      "architectureFamily": "distilbert",
      "datasets": [{ "ref": "data-reviews-2025" }]
    },
    "considerations": {
      "technicalLimitations": ["English only"],
      "ethicalConsiderations": [{ "name": "Trained on public product reviews; no PII filtering verified" }]
    }
  }
}
```

The `purl` uses the `huggingface` Package URL type, whose version is the repository's Git commit. The `pedigree.ancestors` entry records the base model. The dataset referenced by `data-reviews-2025` is a separate `data` component in the same document. The validator also accepted the ancestor with no revision at all. The `huggingface` PURL type makes the version optional, so nothing in the format forces lineage to be pinned.

SPDX added AI and Dataset profiles in SPDX 3.0, [released April 16, 2024](https://www.linuxfoundation.org/press/spdx-3-revolutionizes-software-management-in-systems-with-enhanced-functionality-and-streamlined-use-cases). The `AIPackage` class has properties such as `typeOfModel`, `informationAboutTraining`, `limitation`, `safetyRiskAssessment`, and `energyConsumption`. `DatasetPackage` requires only `datasetType`, with optional `knownBias`, `intendedUse`, and `dataCollectionProcess`. Relationships named `trainedOn` and `testedOn` link a model to its data.

## CycloneDX or SPDX for an AIBOM

The two formats cover the same ground with different shapes. Both rely on free-text strings for nearly everything beyond identity.

| Property | CycloneDX (1.5 and later) | SPDX 3.0 |
| --- | --- | --- |
| Model element | Component of type machine-learning-model with modelCard | AIPackage in the AI profile |
| Dataset element | Component of type data, with classification, sensitive data, and governance fields | DatasetPackage, where only datasetType is required |
| Model-to-data link | modelCard.modelParameters.datasets, inline or by reference | trainedOn and testedOn relationships |
| Lineage | pedigree.ancestors on the component | ancestorOf and descendantOf relationships |
| Model identity | PURL, including pkg:huggingface/...@\<commit\> | PURL via packageUrl, plus verifiedUsing for hashes |
| AI-specific fields with no direct counterpart | Fairness assessments, performance tradeoffs | Autonomy type, safety risk assessment |
| Same document as the software SBOM | Yes | Yes, as profiles in one SPDX 3 document |

Our verdict: use CycloneDX for AIBOMs that feed security tooling, because model identity is a PURL and the model, its datasets, and the surrounding libraries share one dependency graph. This matches our verdict for software SBOMs. Use SPDX 3.0 when a customer or regulator asks for SPDX, or when you need its dataset fields such as `knownBias` and `dataCollectionProcess` as named properties.

## What is the difference between an AIBOM and an SBOM?

An AIBOM is an SBOM that also describes models and datasets, the two components whose behavior cannot be read from source. A library's version identifies code you can review. A model's revision identifies weights whose behavior was set by training data, and the AIBOM can only report what the trainer says about that data.

Three more differences follow. Model identity lives on a hub with mutable branch names, so an unpinned reference like `main` names a moving target. Model lineage is a chain of derivations, such as fine-tune of a quantization of a base model, that package dependency graphs were not built to express. And model files can be executable: loading a pickle-based checkpoint runs code, with no install step involved. An AIBOM does not replace the SBOM. In CycloneDX, both live in one document.

## Which AIBOM fields can you actually verify?

You can verify an AIBOM's identity fields against bytes, and almost nothing else. The figure maps one illustrative AI service.

> An AIBOM for one AI service, drawn as a tree. Library versions, the model revision, and model file hashes are bound to bytes. The base model is named without a revision. Dataset, training, and limitation fields are self-reported text.

The figure shows an application, `review-triage-service 4.2.0`, with two children. The first is the library `pkg:pypi/litellm@1.107.0`, whose version and wheel hash match the registry, and which was a malicious release. The second is the model `sentiment-mini` pinned to commit `9f2c41d`. Under the model sit five entries. `model.safetensors` (SHA-256 `521ca9c2…`) and `model.pkl` (SHA-256 `b3d0bef3…`) are bound to bytes, and the pickle file calls `posix.system` when loaded. The base model `distilbert-base-uncased` is named without a revision. The dataset `reviews-2025` and the limitations, bias, and intended-use fields are self-reported text.

| AIBOM field | Example | Check it against | Verdict |
| --- | --- | --- | --- |
| Model revision | pkg:huggingface/...@9f2c41d… | The hub's Git commit | Verifiable when present. The PURL version is optional. |
| File hashes | SHA-256 of model.pkl | The file | Verifiable. Proves identity, not safety. |
| File format | .pkl versus .safetensors | The file's header or opcodes | Verifiable, and decisive for code execution on load |
| Libraries | pkg:pypi/litellm@1.107.0 | Registry hashes | Verifiable. Says nothing about intent. |
| Base model | pedigree.ancestors | Nothing, unless pinned by revision | Claim |
| Training datasets | modelParameters.datasets, trainedOn | Nothing in the weights | Claim |
| Limitations, bias, intended use | considerations, knownBias | Nothing | Claim |
| Performance metrics | quantitativeAnalysis, metric | Rerun the evaluation | Reproducible, at your cost |

The study cited above shows how thin the claim rows are in practice. Across 97,940 generated AIBOMs, license information appeared in 73.14%, dataset information in 39.35%, and the SPDX-aligned `informationAboutTraining` field in 0.00%. The authors generated the documents with the OWASP GenAI Security Project AIBOM Generator, which reads Hub metadata. An AIBOM generator can only copy what the model card says.

## A valid AIBOM for a model that runs a shell command

We built a two-tensor "model" in two formats. The pickle version carries one extra object whose `__reduce__` method tells the unpickler to call `os.system("id")`:

```python
import pickle, os, numpy as np
from safetensors.numpy import save_file
weights = {"classifier.weight": np.zeros((2, 4), dtype=np.float32),
           "classifier.bias": np.zeros((2,), dtype=np.float32)}

class Payload:
    def __reduce__(self):
        return (os.system, ("id",))

# A checkpoint whose state dict is fine, plus one entry that runs a command on load.
with open("model.pkl", "wb") as f:
    pickle.dump({"state_dict": weights, "_meta": Payload()}, f, protocol=4)

save_file(weights, "model.safetensors")
```

The AIBOM component shown earlier describes `model.pkl`. Its hash is correct and the document passes schema validation (Python 3.13, cyclonedx-python-lib 11.12.0, safetensors 0.8.0, numpy 2.5.3):

```console
$ sha256sum model.pkl
b3d0bef37e65517900c6f4ba2b568bc0ae38d9e0767c3bcab5b86227b5027a0a  model.pkl
$ python3 -c "from cyclonedx.validation.json import JsonStrictValidator; from cyclonedx.schema import SchemaVersion; print(JsonStrictValidator(SchemaVersion.V1_6).validate_str(open('aibom.cdx.json').read()))"
None
```

`None` means no validation errors. Now load the model the way a naive pipeline would:

```console
$ python3 -c "import pickle; pickle.load(open('model.pkl','rb'))"
uid=0(root) gid=0(root) groups=0(root)
```

The [Python documentation](https://docs.python.org/3/library/pickle.html) is blunt about this: "The `pickle` module is not secure. Only unpickle data you trust." The fix is to read what the file asks for before anything runs. This unpickler records every global the stream imports and hands back an inert stub instead:

```python
import pickle, sys

class Stub:
    def __init__(self, *args, **kwargs): pass
    def __setstate__(self, state): pass

class Recorder(pickle.Unpickler):
    """Print every global the pickle imports. Hand back a stub so nothing real runs."""
    def find_class(self, module, name):
        print(f"{module}.{name}")
        return Stub

with open(sys.argv[1], "rb") as f:
    Recorder(f).load()
```

```console
$ python3 pickle_imports.py model.pkl
numpy._core.multiarray._reconstruct
numpy.ndarray
numpy.dtype
posix.system
```

The first three imports rebuild the NumPy arrays and are normal. The fourth, `posix.system`, is the shell (on Windows it appears as `nt.system`). Benign checkpoints also import functions, so detection means allowlisting what tensor reconstruction needs, not flagging every import. PyTorch takes that approach: since version 2.6, `torch.load` defaults to `weights_only=True`, an unpickler that "is not allowed to dynamically import anything during unpickling" ([PyTorch serialization notes](https://docs.pytorch.org/docs/2.6/notes/serialization.html)). Older loaders and other frameworks do not. PyTorch's own `.pt` files are ZIP archives with a `data.pkl` inside, so the same analysis applies to them. Unsafe `torch.load` and pickle paths were behind the [PyTorch Lightning deserialization vulnerabilities](https://socket.dev/blog/pytorch-lightning-deserialization-vulnerabilities) disclosed under VU#252619 in April 2025.

The safetensors file has nothing to execute. Its first 8 bytes give a header length, followed by a JSON header and raw tensor bytes:

```text
{"classifier.bias": {"dtype": "F32", "shape": [2], "data_offsets": [0, 8]},
 "classifier.weight": {"dtype": "F32", "shape": [2, 4], "data_offsets": [8, 40]}}
```

Choosing safetensors removes load-time code execution from the weight files, not from the repository. Models that need `trust_remote_code=True` run Python files from the repository, and the [transformers security policy](https://github.com/huggingface/transformers/security) says to "always verify the content of the modeling files" and to set a revision. GGUF files carry chat templates that have been used to plant backdoors. These are the cases [malicious AI models](https://socket.dev/glossary/malicious-ai-models) exploit, and none of them shows up in an AIBOM as anything but a filename.

An AIBOM earns its keep as a work order for analysis. Socket's model scanner inspects Hugging Face model files for deserialization attacks, including pickle code execution, TensorFlow and Keras Lambda layer injection, and GGUF metadata payloads. Enterprise users can upload a CycloneDX AIBOM, and Socket scans the Hugging Face models it lists by PURL ([announcement](https://socket.dev/blog/announcing-experimental-malware-scanning-for-hugging-face), [changelog](https://socket.dev/changelog/experimental-malware-scanning-for-hugging-face-models)).

## The software half of an AIBOM fails the same way

The libraries that serve and train models are packages, and they get compromised like any other. In [the LiteLLM compromise](https://socket.dev/blog/litellm-compromise) on October 21, 2026, `litellm` 1.107.0 on [PyPI](https://socket.dev/glossary/python-package-index-pypi) shipped Python startup files that collect cloud credentials and AI-tool tokens. The payload runs whenever Python starts in the environment, even if nothing imports LiteLLM. The package receives about 23 million weekly downloads, and Socket's AI scanner flagged all seven wheels seven minutes after publication.

An AIBOM generated from an affected build that day lists `pkg:pypi/litellm@1.107.0` with correct hashes. A [malicious package](https://socket.dev/glossary/malicious-package) looks perfectly valid in any bill of materials. Matching it against CVE data finds nothing, because malware does not get a CVE. The release's container [provenance](https://socket.dev/glossary/package-provenance) did its job too: it faithfully recorded a source repository named `Berri-AI/litellm`, one hyphen away from the real `BerriAI/litellm`. Provenance tells you what a build consumed, not whether those inputs deserved trust.

[The lightning compromise](https://socket.dev/blog/lightning-pypi-package-compromised) on April 30, 2026 hit the training side. Versions 2.6.2 and 2.6.3 of the deep learning framework ran a downloader and an 11 MB obfuscated JavaScript payload when the module was imported. In both cases an inventory would have been accurate, and the answer had to come from analyzing the package before it was installed.

## Is an AIBOM required?

No law requires a document called an AIBOM, as of November 2026. The G7 minimum elements are voluntary. In the US, OMB memo M-26-05 of January 23, 2026 [rescinded the federal software supply chain mandates and made SBOMs optional](https://socket.dev/blog/federal-government-rescinds-software-supply-chain-mandates-makes-sboms-optional) in procurement.

The EU asks for adjacent artifacts. Since August 2, 2025, [Article 53(1)(d) of the AI Act](https://artificialintelligenceact.eu/article/53/) has required providers of general-purpose AI models to publish "a sufficiently detailed summary about the content used for training." That is a summary for the public, not a machine-readable inventory. Obligations for high-risk systems listed in Annex III now apply from December 2, 2027, after [Regulation (EU) 2026/1744](https://artificialintelligenceact.eu/ai-act-explorer/digital-omnibus/) delayed them. The [EU Cyber Resilience Act](https://socket.dev/glossary/eu-cyber-resilience-act) requires an SBOM for products with digital elements from December 11, 2027, whether or not they contain a model.

## Building an AIBOM that holds up

Generate the AIBOM from what the system loads, not from model cards. Record the resolved commit for every model and the SHA-256 of every file the loader opens. The commit pins the repository; the file hashes catch a mirror or cache that serves different bytes. Neither tells you the files are safe.

Record the format of every weight file, and refuse pickle where a safetensors file exists. In transformers, `use_safetensors=True` makes loading fail when no safetensors file is present. That closes the largest load-time code execution path. It does not cover repository Python code, GGUF templates, or the libraries around the model.

Pin ancestors by revision when you know them. When you do not, say so: a CycloneDX `compositions` entry with `"aggregate": "unknown"` marks the gap explicitly, which is more honest than a bare name that looks complete.

Keep each AIBOM with the release it describes. A hub author can force-push a branch or delete a repository; your record of which commit you shipped survives both.

The open problem is the data. Nothing in a set of weights proves what they were trained on. A training pipeline can sign an attestation listing its datasets, the way SLSA provenance attests to a build. That converts an unsigned claim into a signed one, and LiteLLM's provenance shows how faithfully such a record can capture a bad input. Until a third party can check training data against the weights, the dataset section of every AIBOM is testimony, and only the fields bound to bytes deserve automated trust.
