Skip to main content

An AI bill of materials, built from your code

· 8 min read
Vulkro
Security research

Ask a team which AI their product uses and you will usually get one answer: the provider on the invoice. Then look at the code. There is the SDK everyone knows about, a second provider someone added for embeddings, a model name hardcoded in a background job, a LangChain agent in an internal tool, a .pt file checked into a test folder, and an MCP server declared in a config file that nobody remembers adding.

None of that is unusual. AI components arrive the way dependencies always have: one import at a time, by whoever needed them that week. The difference is that more people now want a list. Regulators, customers' security reviews and your own incident response all start with the same question, and "we use one provider" is rarely the full answer.

So Vulkro builds the list from the code itself. --format aibom writes an AI bill of materials: the LLM SDKs, model identifiers, model files and MCP servers a project uses, each with the file and line that proves it is there.

Why the inventory comes first​

You cannot assess, document or defend a component you have not found. That is true of open-source dependencies, which is why the SBOM became routine, and it is true of AI components.

Regulation sorts obligations by what you have. The EU AI Act entered into force on 1 August 2024. The European Commission's overview lays out four risk levels (unacceptable, high, transparency and minimal) and dates that differ by category: obligations for general-purpose AI models from 2 August 2025, most of the Act from 2 August 2026, and, after the 2026 amendment that moved them, high-risk systems in the Annex III areas from 2 December 2027. Every one of those steps starts with knowing which AI systems you build or deploy and what they are built from. A scanner cannot classify your system for you, and the AIBOM does not try to. It answers the earlier question: what is actually in the code.

The supply chain is now partly AI. The OWASP Top 10 for LLM Applications 2025 has a supply-chain entry (LLM03), and the OWASP Top 10 for Agentic Applications, published by the OWASP GenAI Security Project in December 2025 (announcement), has one for agents (ASI04): models, MCP servers and tools that come from someone else. A vulnerable SDK version, a model file in an unsafe format, or an MCP server you did not mean to ship are supply-chain problems you can only fix once you know they exist.

Model files can run code. Many model formats are not data in the safe sense. Python's own documentation warns that the pickle module is not secure and that unpickling untrusted data can execute arbitrary code (Python docs), and several common checkpoint formats are pickle underneath. A .pt file committed to a repository is something that will run when loaded.

The format: a CycloneDX ML-BOM​

There was no need to invent a format. CycloneDX added machine-learning support in version 1.5, released in June 2023, with machine-learning-model as a component type (CycloneDX 1.5 announcement). The project calls this capability the ML-BOM and describes it as model and dataset transparency for security, privacy and safety (CycloneDX ML-BOM).

Vulkro writes a CycloneDX 1.6 ML-BOM, so the result can go into the same tooling and the same evidence folder as your regular SBOM:

vulkro scan . --format aibom > aibom.cdx.json
# or, next to your other SBOM output
vulkro sbom . --format aibom -o aibom.cdx.json

What goes into it​

Four kinds of component, each with file and line evidence:

AI SDKs and frameworks. LLM provider SDKs (for example OpenAI, Anthropic, Google, Mistral, Cohere, AWS Bedrock, Azure OpenAI and Ollama), agent and orchestration frameworks (LangChain, LlamaIndex, the Vercel AI SDK), ML frameworks and runtimes (Hugging Face transformers, PyTorch, TensorFlow, ONNX Runtime) and MCP SDKs. They are found in two places: the dependency manifests, and import statements in your own code. That second source matters. An SDK that is imported but never declared, because it arrived as a transitive dependency or someone installed it by hand, is still listed.

Model identifiers. Provider model names written as string literals in code ("gpt-4o", "claude-sonnet-4-5", "gemini-2.5-pro" and so on) and Hugging Face repository ids passed to from_pretrained, pipeline or SentenceTransformer. This is the part that usually surprises people: the background job pinned to an older model, the second provider used only in one script.

Model files in the repository. .safetensors, .pt, .pth, .onnx, .gguf, .ggml, .h5, .keras, .tflite and .mlmodel files, each with a SHA-256 hash when it is small enough to hash, and pickle-based formats flagged because they execute code when loaded.

MCP servers and tools. Servers declared in the project's MCP configs (.mcp.json, mcp.json, .cursor/mcp.json, .vscode/mcp.json) and servers defined in code (new McpServer, FastMCP(...), NewMCPServer(...)), with the tools each one registers. A tool your code exposes to a model is part of your AI surface, and since 0.30.0 it is also an entry point the scan follows (agent tools are entry points).

A component in the output looks roughly like this (trimmed):

{
"type": "machine-learning-model",
"name": "gpt-4o",
"evidence": {
"occurrences": [
{ "location": "src/jobs/summarise.ts:42" }
]
}
}

The output is deterministic for a given tree, and building it is read-only and offline: nothing is executed, loaded or fetched. Model files are hashed, never opened as models.

Mapping findings to the OWASP AI lists​

An inventory says what is there. The next question is whether any of it is used unsafely, and auditors increasingly ask that question in the vocabulary of the two OWASP lists. vulkro compliance-pack has a framework for each:

vulkro compliance-pack . --framework owasp-llm-2025 -o ./evidence/llm
vulkro compliance-pack . --framework owasp-agentic-2026 -o ./evidence/agentic

Each writes a folder with a manifest, a summary, a CSV of findings and one file per control (LLM01 to LLM10, or ASI01 to ASI10), with file and line evidence (reference). A control passes with no findings, is partial with only Medium or Low findings and fails with any High or Critical one.

Some examples of what lands where:

  • LLM01 and ASI01: untrusted input reaching a prompt.
  • LLM05 and ASI05: model output reaching HTML, SQL, a shell, eval or a deserializer.
  • LLM06 and ASI02: powerful tools with no guard, including a tool whose argument reaches a shell, a query or a file.
  • LLM07: credentials written into a system prompt.
  • LLM10: model calls with no token, rate or size limit, from the LLM defense pack.
  • LLM03 and ASI04: the supply-chain controls, where the AIBOM supplies the list and the dependency and malicious-package checks supply the findings.

What a code scan cannot answer​

We would rather say this plainly than let a green control imply more than it means.

Some controls are mostly outside the code. Misinformation (LLM09) is not measured by a code scan. Data and model poisoning (LLM04) is mostly an operational control, and the scan adds little. Cascading failures between agents (ASI08) is an architecture concern. Rogue agents (ASI10) are detected at runtime; the scan can only supply what each agent is able to reach. The packs say this per control instead of marking those controls as passed.

The inventory has edges too. It finds what is written in the repository: a model name built at runtime from configuration, or a provider called over raw HTTP with no SDK, will not appear as a named component. And the AIBOM does not decide your risk category under the AI Act. That depends on what the system is used for, which is not in the code.

A compliance pack is one input to an audit, not the audit. It covers what is in the code, with evidence, so the people doing the rest of the work start from facts instead of a questionnaire.

Where to see it​

Run vulkro scan . --format aibom on the repository you know best and count the components you did not expect. Then run a compliance pack against one of the OWASP lists and read the controls marked partial or failed.

The output formats reference lists every format, including the regular SBOM and VEX outputs, and the compliance overview covers every framework. To install Vulkro, start here.

Sources​