When you ship an AI feature you take on a chain of artefacts you did not make and mostly cannot inspect. The supply chain sits fourth on the OWASP 2026 list, above most of the failures teams spend their attention on, because a dependency that has been tampered with or has quietly changed defeats every control built on top of it.
What you are actually depending on
A single feature usually rests on a base model you did not train and whose training data you cannot see, weights downloaded from a hub, adapters or fine tunes layered on top, the datasets those were tuned on, an embedding model that decides what retrieval can find, a vector store, an orchestration library with its own transitive dependencies, and prompt templates copied from a repository or a blog post.
Each of those is a dependency in the ordinary software sense, and several are invisible to the tooling you already run. A vulnerability scanner reads your lockfile. It does not read the model file, the adapter, the index or the prompt template somebody pasted in during a busy week.
Where it goes wrong
Tampered or backdoored weights. Some model file formats execute code on load, which is an ordinary deserialisation problem wearing a new hat. The harder version is behavioural, since a fine tune can carry a response that appears only on a trigger phrase and is therefore invisible in any sample of normal output.
A card that does not match the artefact. Model cards are authored claims about training data, intended use and evaluation results, and nothing ties them to the bytes you downloaded. On a public hub the name is the weakest part of the identity, so the card you read may describe something else entirely.
Licensing that surfaces later. Terms vary on commercial use, redistribution and output, and they stack, since a fine tune carries the base model's licence along with whatever the tuning data imposed. The finding usually arrives from a buyer's legal review rather than from your own build.
Abandoned dependencies. An orchestration library with two maintainers and a large surface, an embedding model nobody has updated in two years, a vector store project that stopped shipping. The risk is the familiar one and this ecosystem produces it unusually fast.
A hosted model changing under you. This has no clean equivalent in ordinary software. A provider can update the model behind an endpoint name, retire the version you tested, or change a default safety setting. Your code did not change and your system now behaves differently, which is the failure that makes people distrust their own regression results.
The controls that answer it
Pin versions. A model version rather than an alias, a library version rather than a range, and the embedding model version in particular, because changing that one forces a reindex. Anything called latest moves without telling you.
Verify what you downloaded. Where a publisher gives checksums or signatures, check them and record the value you verified rather than the fact that you did. Prefer formats that do not execute on load, and open anything you are unsure about somewhere you would not mind losing.
Record what you depend on. A list of every model, adapter, dataset, embedding model, index and library the feature uses, with versions and sources. The value is not the document. It is being able to answer, on the day a hub pulls a model or a licence changes, whether you are affected, without somebody reading code to find out.
Evaluate a version, not a name. An eval result belongs to a specific version of a specific chain, and rerunning it after any change in that chain is what turns the inventory from paperwork into a control.
Provenance is cheap to establish on the day you take a dependency and close to impossible to reconstruct a year later, which is the argument for doing it while it still feels unnecessary.
Practise this
You need the feature you most recently shipped, a text file, Python for the second half, and fifteen minutes.
Write the inventory from memory first, one line per artefact with its version and where it came from, and only then check it against the code. Then take the files you ship but did not write, meaning prompt templates, adapters and index configuration, and record what they are today.
import hashlib, pathlib
# Point this at whatever your feature loads and you did not author.
for path in sorted(pathlib.Path("./prompts").rglob("*")):
if path.is_file():
digest = hashlib.sha256(path.read_bytes()).hexdigest()
print(f"{digest[:16]} {path}")
What to look for. The interesting output is the row you cannot complete. Somewhere in that inventory is an artefact whose version nobody pinned, whose origin nobody remembers, or whose licence nobody has read, and the gap between the list you wrote from memory and the list in the code is an honest measure of how well the chain is understood.
It teaches that what you depend on is a specific version of a specific artefact, and that the question a customer eventually asks can only be answered if somebody wrote the answer down at the time.