The AIBOM Security and Runtime Section
The security and runtime section of an AIBOM records what can run code when the model loads, what the model can reach once it runs, which known vulnerabilities apply, and how to prove the files are the ones you built. Record the weights’ serialization format and the loader setting that enforces it, the inference server and its libraries, each tool, MCP server and retrieval index as a service with its trust boundary, vulnerabilities with a VEX verdict, and links to the model signature and build provenance.
The security and runtime section of an AIBOM answers four questions: what runs code when the model loads, what the model can reach once it runs, which known vulnerabilities apply, and how you prove the files are the ones you built. Record the weights’ format and the loader setting that enforces it, the inference server and libraries, every tool, MCP server and retrieval index as a service with its trust boundary, each vulnerability with a VEX verdict, and links to the signature and build provenance.
Who this is for: AppSec and AI-security engineers who own the model pipeline, and SOC analysts who will open this section first when something goes wrong.
What runs code when the model loads
A weights file is not always just data. The serialization format decides whether loading it can execute code.
| Format | Can loading it run code? | AIBOM stance |
|---|---|---|
| Python pickle (PyTorch .bin, .pt, .pth) | Yes. Unpickling can execute code. Since PyTorch 2.6, torch.load defaults to weights_only=True; CVE-2025-32434 showed weights_only=True could still lead to code execution before 2.6.0. | Avoid for third-party models. If you must, load in an isolated job and convert to safetensors. |
| safetensors (.safetensors) | No. A header and raw tensor data; the project describes it as storing tensors “safely (as opposed to pickle)”. | The default to require. Pin the loader to it. |
| GGUF (.gguf) | No code by design: tensors plus a standard set of metadata, built for GGML and llama.cpp. | Treat the parser and quantisation kernels as attack surface, like any file parser. |
Neither CycloneDX nor SPDX has a field for the weights’ format. Northwind’s files say “Weights in safetensors format” in the description. A policy cannot test a sentence, so add a namespaced property, such as northwind:weights:format = safetensors.
The format of the file is only half of it. The loader decides what it will accept. In vLLM 0.6.3, --load-format auto “will try to load the weights in the safetensors format and fall back to the pytorch bin format if safetensors format is not available”. A model card that says “safetensors” does not stop a swapped directory with a .bin file from loading. Northwind (fictionally) runs vLLM with --load-format safetensors. That setting belongs in the AIBOM too, for example as northwind:vllm:load-format = safetensors, because the next section depends on it.
Before a third-party model reaches your loader, the Model Risk Scanner reads its Hugging Face metadata for custom code, licence and gating.
Known vulnerabilities, with a verdict
Northwind’s AIBOM lists vLLM 0.6.3, which pins torch == 2.4.0, plus transformers, safetensors and peft at exact versions. That precision is what makes vulnerability matching work. On 29 October 2026, OSV listed 69 distinct advisories whose affected ranges include vLLM 0.6.3. One is directly about loading models:
- CVE-2025-24357 (GHSA-rh4j-5rhw-hr54, rated high): vLLM’s weight loader called
torch.loadwithweights_onlyleft at False, so a malicious checkpoint could run code. Fixed in vLLM 0.7.0. The advisory notes that “most models now use the safetensors format, which is not vulnerable to this issue”. - GHSA-ggpf-24jw-3fcw (rated critical): the 0.7.0 fix used
weights_only=True, which did not hold before PyTorch 2.6.0. Fixed in vLLM 0.8.0. - CVE-2025-32434 (GHSA-53q9-r3pm-6pq6, rated critical) in PyTorch itself:
torch.loadwithweights_only=Truecould still lead to remote code execution. Fixed in PyTorch 2.6.0. Northwind’s torch 2.4.0 is in range.
A scanner reading the AIBOM will flag all three. The AIBOM’s job is to record whether they matter here, which is what VEX (Vulnerability Exploitability eXchange) does. In CycloneDX it is a vulnerabilities entry with an analysis:
{
"bom-ref": "vuln:CVE-2025-24357",
"id": "CVE-2025-24357",
"source": { "name": "NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24357" },
"references": [
{
"id": "GHSA-rh4j-5rhw-hr54",
"source": {
"name": "GitHub Advisory Database",
"url": "https://github.com/advisories/GHSA-rh4j-5rhw-hr54"
}
}
],
"ratings": [{ "source": { "name": "GitHub Advisory Database" }, "severity": "high" }],
"cwes": [502],
"description": "vllm: Malicious model to RCE by torch.load in hf_model_weights_iterator",
"affects": [{ "ref": "pkg:pypi/vllm@0.6.3" }],
"analysis": {
"state": "not_affected",
"justification": "requires_configuration",
"detail": "FICTIONAL EXAMPLE. vLLM runs with --load-format safetensors and the model directory holds only .safetensors weights, so the torch.load path for .bin checkpoints is never used. Upgrade planned."
}
}
A VEX entry for Northwind’s AIBOM. We wrote it for this post; the CVE, advisory, CWE and rating are real, the analysis is Northwind’s fictional assessment. It validates against the CycloneDX 1.7 schema inside the full Northwind BOM.
The verdict not_affected with justification requires_configuration is only as good as the configuration behind it, which is why the load-format setting goes in the AIBOM. SPDX 3.0 expresses the same thing with a security_Vulnerability element and a VexNotAffectedVulnAssessmentRelationship; its closest justification is vulnerableCodeNotInExecutePath, because SPDX’s list has no “requires configuration” value. Either way, one advisory out of 69 is not a reason to stay on 0.6.3. Record the verdicts, then plan the upgrade.
Agents, tools, MCP servers and RAG indexes
What a model can reach matters as much as what it is. Neither CycloneDX 1.7 nor SPDX 3.0.1 has a type for agents, tools or Model Context Protocol (MCP) servers. In CycloneDX, model each one as a service: endpoints, authenticated, x-trust-boundary (does using it cross a trust boundary?), trustZone, and a data entry per flow with a direction and a classification. SPDX 3.0.1 has no service class, so Northwind’s SPDX file models the RAG index as a DatasetPackage and the MCP server as a software package. Here are Northwind’s two services:
[
{
"bom-ref": "service:northwind-helpcentre-index",
"provider": { "name": "Northwind Health", "url": ["https://northwind-health.example"] },
"name": "northwind-helpcentre-index",
"description": "Retrieval (RAG) index over the public help centre, rebuilt weekly. Supplies passages to the prompt.",
"endpoints": [
"https://rag.internal.northwind-health.example/v1/northwind-helpcentre-index/query"
],
"authenticated": true,
"x-trust-boundary": false,
"data": [
{ "flow": "outbound", "classification": "public", "name": "Help-centre passages" }
],
"properties": [{ "name": "playciso:rag:refresh", "value": "weekly" }]
},
{
"bom-ref": "service:ticketing-mcp",
"provider": { "name": "Northwind Health", "url": ["https://northwind-health.example"] },
"name": "ticketing-mcp",
"description": "Model Context Protocol (MCP) tool server. Lets the assistant read tickets and write draft replies.",
"endpoints": ["https://mcp.internal.northwind-health.example/ticketing-mcp"],
"authenticated": true,
"x-trust-boundary": true,
"data": [
{
"flow": "bi-directional",
"classification": "confidential",
"name": "Support tickets and draft replies"
}
],
"properties": [
{ "name": "playciso:mcp:permissions", "value": "tickets:read, tickets:write" }
]
}
]
Excerpt, 36 lines: the services array from Northwind’s CycloneDX 1.7 file. Keys not shown are left out; every value shown is exactly as in the full file (519 lines pretty-printed), which is in the Pro AIBOM Template Library.
Three things to add beyond the canonical file:
- The tool list and the protocol revision. Record which tools
ticketing-mcpexposes, which of them write, and the MCP revision it implements. MCP is versioned by date; the current revision is 2026-07-28. - A narrower write scope. The stated purpose is drafting replies, but the recorded permission is
tickets:write. The AIBOM makes the gap visible: a draft-only scope would match the intended use. The MCP Scan checks a server’s hygiene from one standard handshake. - A build ID for each index rebuild.
northwind-helpcentre-indexis rebuilt weekly from the public help centre, so what the model reads changes every week while the AIBOM stays the same. Record an ID or hash per build, so an incident can be tied to the content that was live.
Why the combination matters: public help-centre text goes into the prompt, and the model can call a tool that writes tickets. Anyone who can influence that text can try to plant instructions. Article 15(5) of the AI Act names “inputs designed to cause the AI model to make a mistake” among the AI-specific attacks a high-risk system’s cybersecurity measures should address. Northwind’s answer is a human who approves every reply, recorded in SPDX as ai_autonomyType: no. The AIBOM shows the risk and the control side by side.
Signing and provenance: what to link
- Sign the model files. OpenSSF model signing (the
model_signingpackage; 1.0.0 released on PyPI on 4 April 2025) writes a Sigstore bundle. Inside is an in-toto statement with predicate typehttps://model_signing/signature/v1.0: in release 1.1.1 its subject is a single digest over the whole model, and the per-file paths and digests sit inpredicate.resources. Verification checks the signature, then re-hashes every file. With Sigstore, the signing event is logged in a public transparency log. - Attest the build. SLSA provenance (predicate type
https://slsa.dev/provenance/v1) in an in-toto attestation records the builder and its inputs: the base model revision and the dataset hashes. - Link both from the AIBOM. CycloneDX 1.7 has
externalReferencestypesdigital-signatureandattestation. SPDX 3.0.1 has no dedicated type, so Northwind usesotherwith acontentType. - Sign the AIBOM too. CycloneDX’s own
signature(JSON Signature Format) signs the BOM data, not the weights. An unsigned AIBOM can be edited to match a swapped model.
[
{
"type": "model-card",
"url": "https://models.northwind-health.example/northwind/triage-assistant/2.3.0/MODEL_CARD.md"
},
{
"type": "digital-signature",
"url": "https://models.northwind-health.example/northwind/triage-assistant/2.3.0/model.sig",
"comment": "Sigstore bundle from OpenSSF model signing, produced in CI."
},
{
"type": "attestation",
"url": "https://ci.northwind-health.example/attestations/triage-assistant-2.3.0.intoto.jsonl",
"comment": "SLSA build provenance as an in-toto attestation."
},
{
"type": "distribution",
"url": "https://models.northwind-health.example/northwind/triage-assistant/2.3.0/model.safetensors"
}
]
Excerpt, 20 lines: the model component’s externalReferences from Northwind’s CycloneDX 1.7 file. Keys not shown are left out; every value shown is exactly as in the full file (519 lines pretty-printed), which is in the Pro AIBOM Template Library.
The control is at deploy time: verify the signature and the expected signing identity before vLLM loads anything.
Northwind’s security section: recorded and missing
| Item | In Northwind’s files | What to add |
|---|---|---|
| Weights format | In the model description only (“Weights in safetensors format”) | A property a policy can test, and the loader setting that enforces it |
| Inference server and libraries | Yes: vllm 0.6.3, torch 2.4.0, transformers 4.45.2, safetensors 0.4.5; peft 0.13.2 with scope excluded (training only) | Nothing. This is what makes the vulnerability match possible |
| Known vulnerabilities | No vulnerabilities section | An entry per advisory with a VEX verdict, and the upgrade decision |
| Model signature | Yes: externalReferences type digital-signature to a Sigstore bundle | The signing identity a verifier should expect |
| Build provenance | Yes: type attestation to SLSA provenance in an in-toto statement | Nothing |
| Signature on the AIBOM itself | No | Sign the BOM (CycloneDX JSF signature, or a detached signature) |
| ticketing-mcp | Yes: a service, authenticated, x-trust-boundary: true, permissions tickets:read, tickets:write | The tool list, the MCP protocol revision, and a narrower write scope |
| northwind-helpcentre-index | Yes: a service, authenticated, public data, rebuilt weekly | A build ID per weekly rebuild, and who can edit the help centre |
| Human in the loop | Yes: SPDX ai_autonomyType: no | Nothing |
Fictional example. Northwind Health, Triage Assistant and its datasets, services, scores and URLs are invented for illustration; the base model and open-source libraries are real.
Finding gaps like these before an auditor or an attacker does is a skill you can practise in Spot the Gaps: AIBOM Lab (Pro).
Security and runtime checklist
- The weights’ format is a testable property, and the loader is pinned to it.
- The inference server and every library are listed with exact versions and purls.
- Each advisory that matches has a VEX verdict with a justification you can show.
- Every tool, MCP server and retrieval index is a service with endpoints, authentication, trust boundary, data flows and permissions.
- Write permissions match the intended use.
- Retrieval content is identified per build.
- The model is signed, the build is attested, both are linked, and the AIBOM itself is signed.
The rest of the AIBOM
Security and runtime is one section of four. Identity and lineage pins which model and which base. The data section covers what it learned from. Evaluation and intended use covers what it is fit to do. For every field of the CycloneDX file, read CycloneDX ML-BOM: fields and a worked example; to lint your own file, use the AIBOM Validator (Pro). All guides by role are on the AIBOM Library hub.
Frequently asked questions
What security information should an AIBOM contain?
The serialization format of the weights and the loader setting that enforces it; the inference server and its libraries with exact versions; known vulnerabilities with an exploitability verdict; every tool, MCP server and retrieval index the model can reach, with its trust boundary and permissions; and links to the model signature and build provenance.
Is safetensors safe from pickle attacks?
The safetensors format stores tensors only, so loading it does not run code the way unpickling a PyTorch .bin file can. The vLLM advisory for CVE-2025-24357 notes that safetensors models are not vulnerable to that issue. But loaders can fall back: in vLLM 0.6.3 the default load format “auto” falls back to the PyTorch bin format when no safetensors files are found, so pin the format.
How do I record an MCP server in an AIBOM?
Neither CycloneDX 1.7 nor SPDX 3.0.1 has a type for MCP servers. In CycloneDX, model it as a service with its endpoints, authenticated, x-trust-boundary, trustZone and a data entry for each flow, and add the tools it exposes, their permissions and the MCP protocol revision as properties. SPDX 3.0.1 has no service class, so use a software package.
How do I record known vulnerabilities in an AIBOM?
In CycloneDX, add a vulnerabilities entry with the ID, source, affected component (affects.ref) and an analysis with state, justification and detail, which is a VEX statement. In SPDX 3.0, use a security_Vulnerability element and a VEX assessment relationship such as VexNotAffectedVulnAssessmentRelationship.
How do I link a model signature to an AIBOM?
Sign the model files, for example with OpenSSF model signing, which produces a Sigstore bundle over the digest of every file. Then link that bundle from the model component: CycloneDX has externalReferences types digital-signature and attestation; SPDX 3.0.1 has no dedicated type, so use externalRef type other with a contentType. Sign the AIBOM itself as well.
Should a RAG index be in the AIBOM?
Yes. A retrieval index changes what the model says without changing its weights, and its content may come from sources outside your control. Record what it is built from, how often it is rebuilt, who can change the source, and an identifier for each build so an incident can be tied to the content that was live.
Sources
- Hugging Face Hub docs: pickle scanning
- huggingface/safetensors
- PyTorch v2.6.0 torch/serialization.py (weights_only default changed to True)
- GHSA-53q9-r3pm-6pq6 / CVE-2025-32434: torch.load with weights_only=True RCE, fixed in PyTorch 2.6.0
- GHSA-rh4j-5rhw-hr54 / CVE-2025-24357: vLLM torch.load RCE, fixed in 0.7.0
- GHSA-ggpf-24jw-3fcw: CVE-2025-24357 fix bypass with PyTorch < 2.6.0, fixed in vLLM 0.8.0
- OSV: vulnerabilities for the PyPI package vllm
- vLLM v0.6.3 engine/arg_utils.py (--load-format)
- vLLM v0.6.3 requirements-cuda.txt (torch == 2.4.0)
- CycloneDX 1.7 JSON schema (bom-1.7.schema.json): component, pedigree, modelCard, componentData, service, vulnerability
- SPDX 3.0.1: VexNotAffectedVulnAssessmentRelationship
- SPDX 3.0.1: VexJustificationType
- SPDX 3.0.1: ExternalRefType
- SPDX 3.0.1: autonomyType
- sigstore/model-transparency: OpenSSF model signing (model_signing)
- model-signing on PyPI (1.0.0 released 4 April 2025)
- SLSA v1.0 provenance (predicateType https://slsa.dev/provenance/v1)
- Model Context Protocol: versioning (current revision 2026-07-28)
- AI Act Article 15 (AI Act Service Desk)
Checked on 29 October 2026. PlayCISO is not affiliated with any standards body, vendor or project named here. This is general information, not legal advice.
Ready to practise the decisions these articles describe?
Run your first War Room free →One run free with a free account · more runs with Pro