The Vulnerability
Zafran Labs discovered that Hugging Face's diffusers library — with ~200,000 daily installations — has a fundamental flaw in how it enforces the trust_remote_code security boundary. Three CVEs were assigned:
- CVE-2026-44827 (CVSS 8.8) — Code injection via
None.py - CVE-2026-45804 (CVSS 7.5) — Race condition (TOCTOU)
- CVE-2026-44513 (CVSS 8.8) — Code injection via
custom_pipeline
How trust_remote_code Works
When loading a model with custom pipelines, diffusers checks if the repository contains custom code:
from diffusers import DiffusionPipeline
This triggers the trust check
pipe = DiffusionPipeline.from_pretrained(
"stable-diffusion-v1-5/stable-diffusion-v1-5",
custom_pipeline="text-to-video",
trust_remote_code=False
)
If trust_remote_code=False (or omitted), the loader should refuse to execute custom code.
The TOCTOU Flaw (CVE-2026-45804)
The root cause: from_pretrained() splits the operation into two non-atomic HTTP requests:
# Phase 1: Download config and CHECK for custom code
config = hf_hub_download(repo_id, "config.json")
trust_remote_code check happens HERE
Phase 2: Download the actual model files
snapshot_download(repo_id)
Code execution happens HERE
The window between Phase 1 and Phase 2 is exploitable:
Timeline:
t0: Config downloaded, trust check passes (no custom code)
t1: Attacker pushes malicious pipeline.py to repo
t2: snapshot_download fetches and executes the malicious code
Window: ~0.3 seconds locally, but can be wider with slow connections or cache invalidation.
Exploitation
# Attacker controls a model repo
1. Initial commit: clean config, no custom code
2. Victim loads model -> trust check passes
3. Attacker pushes pipeline.py between the two HTTP calls
4. snapshot_download executes the malicious code
The None.py Bypass (CVE-2026-44827)
A simpler attack: name the malicious file None.py.
# The gate in download() checks:
f"{custom_pipeline}.py" in filenames
This looks for "pipeline.py" — misses "None.py"
But the loader resolves and imports None.py anyway
The security check and code execution use different resolution logic.
The Cross-Repo Variant (CVE-2026-44513)
# Attacker creates:
- repoA: clean config, no custom code
- repoB: malicious pipeline.py
Victim loads:
pipe = DiffusionPipeline.from_pretrained(
"repoA", # trust check runs against repoA
custom_pipeline="repoB/pipeline", # code fetched from repoB
)
The trust gate evaluates against repoA's file list. The pipeline is fetched from repoB. The gate never sees the malicious code.
Impact
- 200,000+ daily installations of diffusers
- Production pipelines, CI/CD systems, and container images affected
- Can turn a routine model download into initial access
Remediation
Update to diffusers >= 0.38.0 (released May 2026):
pip install diffusers>=0.38.0
Additional protections:
- Always pin specific revisions when loading from Hub
- Never use
custom_pipelinepointing to a different repo - Inspect local snapshots before loading
- Use
trust_remote_code=False(already default, but be explicit)
Lessons
- Security checks must be atomic with the operations they protect
- TOCTOU in package managers is a critical supply chain risk
- AI libraries need the same scrutiny as traditional package managers
- trust_remote_code is not enough if the enforcement is flawed
Based on Zafran Labs disclosure and CVE-2026-44827, CVE-2026-45804, CVE-2026-44513.