Search repos, object storage and artifact registries for common model file extensions, then record each file's format, source and owner. Pay first attention to pickle based formats, because loading them can execute code. Files that were downloaded from a public hub and never reviewed are the usual shadow AI finding.
Why model files count as shadow dependencies
A model file is code-adjacent data your application loads at runtime. It came from somewhere, someone chose it, and it will change how your product behaves. If nobody wrote that down, it is a dependency with no record.
Hugging Face's security documentation explains the sharpest risk. Pickle is a widely used serialization format in machine learning, and it is the default format for PyTorch model weights. Loading a pickle can run arbitrary code, so the documentation advises loading models only from users and organizations you trust. It also describes safetensors as a simpler serialization format intended to avoid this.
OWASP adds that published models carry no strong provenance guarantees and that static inspection of a model offers little assurance, which is why recording origin and hashes matters.
Step 1: list candidate files
Start with extensions. These are common, not exhaustive, so check what your own teams use:
| Extension | Usually means |
|---|---|
| .pkl, .pickle | Python pickle, can execute code on load |
| .pt, .pth, .bin | Often PyTorch weights, frequently pickle based |
| .safetensors | Safetensors weights |
| .gguf | A quantized model format used by local runners |
| .onnx | ONNX model |
| .h5, .keras | Keras models |
Search each repo, including history, since a file removed last month may still be in the object store. Large files often live outside git, so also list your object storage buckets, container images and artifact registries.
git ls-files | grep -E '\.(pkl|pickle|pt|pth|bin|safetensors|gguf|onnx|h5|keras)$'
Step 2: look for load calls
A file in a bucket matters only if something loads it. Search code for model loading calls and for downloads from a model hub, and note the model identifier each one uses. A model identifier in code is an inventory entry even when no file is committed.
Step 3: record the minimum
For each model, record:
- Name and version or revision.
- Where it came from (public hub, internal training, a vendor).
- File format.
- A hash of the file.
- Who owns it and which service loads it.
- License, as noted by its source.
A one-page intake record keeps this consistent. See a one page model intake record.
Step 4: reduce the risky formats
Where you can choose, prefer formats that do not execute code on load. Where you cannot, load only from sources you trust, pin the revision, and keep the hash. Hugging Face notes that its Hub pickle scan is not foolproof, so treat a scan result as one input, not a clearance. For a deeper look, see can you sign pickle files with HMAC for internal model handoffs.
Quick check
Is a .bin file always a pickle?
No. The extension is a hint, not proof. Check the loader that reads it and the format documentation of its source.
Do models called over an API need this?
They need an inventory entry too, but there is no file to find. See do you need an AI BOM entry for a model you only call through an API.
Safeguard offers asset discovery and SBOM and dependency scanning, and you can try a free scan at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.