AIBOM Incident Scoping Lab
When an AI supply-chain advisory drops, the AIBOM is how you answer “are we affected?” in minutes instead of days. Here you get 10 systems and 5 advisories. Decide what is in scope, what each system can reach, and what to contain first, against the clock.
Who this is for: SOC analysts and incident responders who will be handed an advisory and a BOM, AppSec and AI-security engineers who keep the fleet’s BOMs, and CISOs who want to know whether those BOMs would hold up in an incident. Every system belongs to Northwind Health, a fictional company.
What this lab is
- An advisory lands. You have 10 fictional AI systems, each with a CycloneDX 1.7 ML-BOM excerpt, and a scope rule from the incident lead.
- For each system you call it in scope, not in scope, or "can’t tell from the BOM". For anything you keep in scope you name what it can reach (MCP servers, RAG indexes, data stores), a priority and the first containment step.
- 5 advisories: the real vLLM model-loading RCE (CVE-2025-24357 and its fix bypass) plus four labelled fictional ones: a malicious pickle in a hub model, a compromised MCP server package, a poisoned public dataset and a base-model licence change.
- Some BOMs leave out the runtime, the services or a revision. The lesson: you cannot scope what you did not record. "Can’t tell" is a correct answer, and it stays in scope until someone checks.
- Each round is scored for accuracy and speed. The debrief names the BOM field that decided every call, and the scoping report exports as PDF, CSV or JPG.
The advisories
Rookie gets the first three against six systems, Pro the first four against eight, Elite all five against ten.
- vLLM loads a malicious checkpoint with torch.load (real; all levels). In scope: vLLM below 0.8.0 AND the server can load pickle weights, meaning the weights are a PyTorch .bin, or the load format is not pinned to safetensors (vLLM’s "auto" falls back to .bin when no safetensors file is found). A server below 0.8.0 that is pinned to safetensors and holds only safetensors is not in scope today: record a VEX not_affected verdict and upgrade in the normal window.
- Malicious pickle in a popular hub model (fictional; all levels). In scope: any system whose lineage includes clinical-ner-base at revision 4e1f2a7 loaded from pytorch_model.bin, and any system that runs a model built from it (the training host ran the attacker’s code and could have altered the output). Earlier revisions, and anything loaded from model.safetensors, are not in scope.
- Compromised MCP server package (fictional; all levels). In scope: every MCP server built on mcp-bridge-kit 3.1.4 or 3.1.5. The tokens that server held are exposed, so whatever the server can reach is exposed too.
- Poisoned public dataset (fictional; Pro and Elite). In scope: any model trained or fine-tuned on patient-faq 3.0.0 or 3.1.0, any model built on top of one of those, and any retrieval index built from those versions.
- Base model licence change (fictional; Elite). In scope: any system whose lineage includes Fern-3B-Instruct at revision 7c5e1aa or later. Northwind’s use is commercial. The licence name written in a BOM before 2026-10-26 does not decide it; the revision does.
The fleet
Ten Northwind Health systems. The first is the Triage Assistant used across the AIBOM Library. Some BOMs are complete; some leave a section out, as real ones do.
| System | What it does | Who sees its output | What its BOM leaves out |
|---|---|---|---|
| Triage Assistant 2.3.0 | Drafts replies to support tickets. The canonical AIBOM Library example. | Drafts; a human agent approves every reply. | Nothing this lab needs |
| Claims Letter Drafter 1.1 | Drafts claim decision letters and files them in the claims system. | Letters written into the claims system for a claims handler to send. | The package and version claims-mcp is built on |
| Ticket Router 1.4 | Classifies incoming tickets and routes them to a team. | A team label on each ticket. | Runtime (no serving stack or library versions); Services (no services section) |
| Note Redactor 2.0 | Finds and masks personal details in nurse-line call transcripts. | Redacted transcripts, used by other systems. | Nothing this lab needs |
| Appointment Agent 1.2 | Books, moves and cancels appointments through a chat. | Changes appointments directly. | Nothing this lab needs |
| Intake Form Extractor 1.0 | Pulls fields out of scanned intake forms. | Structured fields for a human to check. | Nothing this lab needs |
| HR Policy Q&A 1.0 | Answers staff questions about HR policy. | Answers to staff. | Nothing this lab needs |
| Nurse Line Summariser 0.9 | Summarises redacted call transcripts for the next nurse on shift. | Summaries read by nurses. | The base model revision; The version of openhealth-qa/patient-faq it was trained on |
| Claims Status Bot 1.0 | Tells customers where their claim is, in the web chat. | Answers straight to customers, no human check. | The package and version claims-mcp is built on |
| Patient FAQ Answers 2.1 | Answers general questions on the public website from a curated FAQ. | Answers straight to the public, no human check. | Nothing this lab needs |
Which BOM field decides each kind of advisory
The same five questions come up in every AI supply-chain incident. Field paths are CycloneDX 1.7. The AIBOM Format Field Map shows the SPDX 3.0 equivalents.
| Advisory type | The field that decides scope | If it is missing |
|---|---|---|
| Inference server vulnerability (e.g. the vLLM advisories) | The serving framework and its libraries with exact versions (components[] type framework/library, version, purl), plus the load-format setting as a property | Without a runtime section you cannot match the version range at all |
| Malicious file in a hub model | pedigree.ancestors[] with a purl that carries the revision, plus which file was loaded; dependencies[] for models used as pipeline steps | Without a revision you cannot tell a clean download from a bad one |
| Compromised MCP server or tool package | services[] for each tool server (endpoints, authenticated, x-trust-boundary, data, permissions), and dependencies[] from the service to the package it is built on | A service with no package behind it, or no services section, leaves the question open |
| Poisoned dataset | modelCard.modelParameters.datasets[] with the dataset version, pedigree.ancestors[] for inherited training, and dependencies[] from a RAG service to its source data | A dataset reference with no version cannot be matched to a bad release |
| Base model licence change | pedigree.ancestors[].purl with the revision, and licenses[] as declared at build time | A licence name copied at build time can be out of date; the revision is what you match |
You cannot scope what you did not record
The Ticket Router’s BOM was written at build time. It records the model, its DistilBERT base and its training data, and nothing about how it is served. For the hub-model and licence advisories that is enough: the lineage answers them. For the vLLM and MCP advisories it is not, and no amount of reading changes that. The honest call is “can’t tell”, and the action is to find out on the host and add the section.
Two smaller gaps do the same damage. A purl with no revision cannot be matched to a bad upload or a relicensed release. A dataset reference with no version cannot be matched to a poisoned one. A version number costs nothing to record at build time; finding it during an incident costs hours. How to record each of these is in AIBOM identity, provenance and lineage and the AIBOM security and runtime section. The walkthrough of the whole response, from “which model was running” to what to pull, is Using an AIBOM in incident response.
How scoring works
- 2 points per system for the scope call. Saying “in scope” where the BOM cannot tell, or “can’t tell” where it shows the system is in scope, gets 1: safe, but not what the record says.
- For every system that is in scope or cannot be ruled out, and that you kept in scope: 1 point for the exact reach, 1 for the containment step, 1 for the priority.
- Each advisory shows your accuracy and your time against par. Your overall percentage feeds the same S to D grade as every PlayCISO lab, with a bonus for speed.
- To clear the lab you need 75% and no exposed system left out of scope. Leaving one out is the mistake that matters in a real incident.
The debrief sends you on: lineage misses to the lineage guide, runtime and services misses to the security and runtime guide, and the fleet’s BOM gaps to the AIBOM Maturity Self-Assessment so you can check your own. To practise reviewing a single BOM for gaps before release, try Spot the Gaps: AIBOM Lab. Everything else is on the AIBOM Library hub.
Sources
Checked on 29 October 2026. Every BOM in the lab validates against the CycloneDX 1.7 JSON schema (bom-1.7.schema.json). The four fictional advisories cite no source because they did not happen.
- GHSA-rh4j-5rhw-hr54 / CVE-2025-24357: vLLM torch.load RCE in hf_model_weights_iterator, fixed in 0.7.0
- GHSA-ggpf-24jw-3fcw: CVE-2025-24357 fix bypass with PyTorch below 2.6.0, fixed in vLLM 0.8.0
- GHSA-53q9-r3pm-6pq6 / CVE-2025-32434: torch.load with weights_only=True RCE, fixed in PyTorch 2.6.0
- vLLM v0.6.3 engine/arg_utils.py: --load-format "auto" falls back to the PyTorch bin format
- vLLM v0.7.3 requirements-cuda.txt (torch == 2.5.1)
- vLLM v0.8.0 requirements/cuda.txt (torch==2.6.0)
- CycloneDX 1.7 JSON reference: component, pedigree, modelCard, service, dependencies
- Hugging Face Hub docs: pickle scanning
- Model Context Protocol: versioning
Frequently asked questions
How do you use an AIBOM in incident response?
Match the advisory against the fields that decide it. For an inference-server vulnerability, that is the serving framework and library versions and how weights are loaded. For a malicious or relicensed model, it is the base-model revision in the lineage, followed through every fine-tune. For a compromised tool package, it is the dependency from each MCP server to the package it is built on. Then read the services section for what each affected system can reach, and contain the ones with the most reach first.
Which systems are affected by CVE-2025-24357 in vLLM?
The advisory covers vLLM before 0.7.0, where the weight loader called torch.load without weights_only, so a malicious pickle checkpoint could run code. A follow-up advisory (GHSA-ggpf-24jw-3fcw) found the fix did not hold with PyTorch below 2.6.0; it is fixed in vLLM 0.8.0. The vLLM advisory notes that safetensors models are not vulnerable. So you need the vLLM version, the torch version and whether the server can load a .bin file, which is why the load-format setting belongs in the AIBOM.
What if the AIBOM does not record what I need?
Then you cannot rule the system out. Treat it as in scope, find what actually runs on the host, and add the missing section to the BOM so the next advisory takes minutes. In the lab, "can’t tell from the BOM" is the correct call for those systems, and unrecorded reach counts as high when you set priorities.
Does a fine-tuned model inherit a problem in its base model or training data?
Yes. A fine-tune carries its base model’s weights, and a model built on a compromised host or trained on poisoned data passes that on to anything built from it. The lab’s Claims Status Bot is a fine-tune of a fine-tune; its own training data is clean, but its parent was trained on the poisoned dataset. Follow pedigree up the chain and dependencies across pipeline steps.
Are the systems and advisories real?
Northwind Health and all ten systems are fictional. The vLLM and PyTorch advisories are real and cited. The other four advisories (the hub model medtext-labs/clinical-ner-base, the npm package mcp-bridge-kit, the dataset openhealth-qa/patient-faq and the model fernlab/Fern-3B-Instruct) are invented and labelled as fictional; none of those names existed on Hugging Face or npm when we checked. The public base models (Mistral-7B-Instruct-v0.3 and DistilBERT) and the library versions are real.
How is this different from Spot the Gaps: AIBOM Lab?
Spot the Gaps trains you to review one document for what is missing before release. This lab trains you to use a whole fleet of BOMs under time pressure, after something has gone wrong: which systems are affected, what they can reach, and what to do first.
More free security tools
The tools people use most on PlayCISO.
Catch injected instructions hiding in content your AI agent should only read.
Score your readiness against the first international standard for AI security threats.
Critical CVEs across 21 major vendors, month by month — and what the 2026 surge means.
Build an ordered ruleset, replay labelled packets and catch shadowed and any-to-any rules.
Self-assess all six Functions, from Govern to Recover, and see your weakest one.
Search 172 threat actors across Microsoft, CrowdStrike, Mandiant and community names.