CVE-2026-44827: Diffusers: None.py Trust Remote Code Bypass
Background
This vulnerability is found in the DiffusionPipeline.frompretrained flow, which is used to load a pipeline from the HuggingFace Hub.
This function accepts an optional custompipeline keyword argument: the name of a Python file in the repo that contains a custom class inheriting from DiffusionPipeline. An equivalent flow is triggered when the classname field in modelindex.json (the repo config file) is set to a custom class.
Any attempt to use a custom pipeline throws the following exception, requesting that trustremotecode is also passed:
python DiffusionPipeline.frompretrained( pretrainedmodelnameorpath='ido-shani/custom-pipeline', custompipeline="custom" )
ValueError: The repository for ido-shani/custom-pipeline contains custom code in custom.py which must be executed to correctly load the model. You can inspect the repository content at https://hf.co/ido-shani/custom-pipeline/blob/main/custom.py. Please pass the argument trustremotecode=True to allow custom code to be run.
The vulnerability is a silent RCE - it allows arbitrary code to be loaded through the custom\pipeline flow from a Hub repo, with no custompipeline or trustremotecode kwargs and nothing suspicious in the config. The frompretrained call succeeds and returns a functional pipeline.
Naive Flow
First, all relevant arguments are popped from kwargs and stored in local variables.
Given a pretrainedmodelnameorpath that is a Hub repo ID, DiffusionPipeline.download() is called. This function serves two roles: it orchestrates downloading relevant model files, and it is the security gatekeeper for trustremotecode. It is called even if the model is already cached; in that case it exits early. If the repo contains custom code, it checks whether trustremotecode was passed and raises otherwise:
python pipelineutils.py:1645-1652 loadpipefromhub = custompipeline is not None and f"{custompipeline}.py" in filenames
...
if loadpipefromhub and not trustremotecode: raise ValueError(...)
It then runs getpipelineclass, which returns the class object of the pipeline in order to inspect its init signature and determine which component files need to be downloaded. As part of building the allowpatterns list used to filter the snapshot download to necessary files only, the custom pipeline file is explicitly included if present:
python pipelineutils.py:1707 allowpatterns += [f"{custompipeline}.py"] if f"{custompipeline}.py" in filenames else []
The function then checks if all expected files are already present, and either exits early or triggers a snapshot download with those patterns.
The next step in frompretrained is loading the pipeline class a second time, this time to actually instantiate it. Before calling getpipelineclass again, resolvecustompipelineandcls is called to translate the custompipeline name into a local path, since the files have already been downloaded:
python pipelineloadingutils.py:965-974 def resolvecustompipelineandcls(folder, config, custompipeline): customclassname = None if os.path.isfile(os.path.join(folder, f"{custompipeline}.py")): custompipeline = os.path.join(folder, f"{custompipeline}.py") elif isinstance(config["classname"], (list, tuple)) and os.path.isfile( os.path.join(folder, f"{config['classname'][0]}.py") ): custompipeline = os.path.join(folder, f"{config['classname'][0]}.py") customclassname = config["classname"][1]
return custompipeline, customclassname
When customclassname is None (i.e. custompipeline was given as a kwarg rather than via the config), getpipelineclass will scan the file and automatically identify the class that subclasses DiffusionPipeline.
Once this is done, getpipelineclass is invoked with the resolved local path, which loads the custom code, retrieves the class object, and proceeds with instantiation.
The Vulnerability
resolvecustompipelineandcls receives custompipeline from the kwargs - when not supplied it defaults to None. That None is used in string formatting: f"{None}.py" = "None.py".
If the repo contains a file with this name, it will be detected as a custom pipeline.
This is only reached on the second invocation of getpipelineclass (inside frompretrained, after download() returns). The trust\remote\code check lives entirely in download(), which evaluated custompipeline is None -> False and skipped it. By the time resolvecustompipelineandcls runs, it is no longer relevant.
As a bonus, None.py even gets downloaded automatically when the model isn't cached yet. This isn't strictly required - it is quite plausible that the victim has already run hf download <model> and has all files locally - but if they haven't, revisiting the allowpatterns line above shows it makes the same error: f"{None}.py" = "None.py" is added to allowpatterns and fetched.
What should None.py contain? To avoid breaking the pipeline load, it must define a class inheriting from DiffusionPipeline. To avoid leaving suspicious clues in the config, that class should shadow one that already exists in diffusers. The following satisfies both requirements:
python from diffusers import FluxPipeline as FluxPipeline
class FluxPipeline(FluxPipeline): pass
INSERT MALICIOUS CODE HERE import pathlib pathlib.Path("/tmp/pwned").writetext(":)")
With this, modelindex.json can contain "classname": "FluxPipeline" - appearing to use the standard diffusers class - and the resulting pipeline is fully functional (it is also functional when running as a local directory). This has been verified against an extracted version of DDUF/tiny-flux-dev-pipe-dduf.
All the attacker needs the victim to run is:
python from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.frompretrained('ido-shani/none-py-trust-remote-code-bypass')
PoC
- Upload this zip as a model to the hub. https://drive.google.com/file/d/1mULARMLJJUTCi57xIv0wtDauko-JW0h7/view?usp=sharing - Run DiffusionPipeline.frompretrained on the uploaded model hub identifier. - RCE occured; /tmp/pwned was created. If you are running the exploit on windows, change the path touched in None.py.
Impact
The vulnerability is a silent RCE - it allows arbitrary code to be loaded through the custom\pipeline flow from a Hub repo, with no custompipeline or trustremotecode kwargs and nothing suspicious in the config. The frompretrained call succeeds and returns a functional pipeline.
Occurrences
https://github.com/huggingface/diffusers/blob/e1b5db52bda85d47a4f8f75954f77e672a8f7f1c/src/diffusers/pipelines/pipelineloadingutils.py#L976
Patches
Yes. Fixed in diffusers 0.38.0 via PR #13448. All users on versions < 0.38.0 should upgrade:
bash pip install --upgrade "diffusers>=0.38.0"
The fix moves the trustremotecode gate out of DiffusionPipeline.download() and into getcachedmodulefile in src/diffusers/utils/dynamicmodulesutils.py, which is the actual chokepoint for every dynamic module load (local, Hub, or community mirror). All three variants now raise ValueError when trustremotecode=False instead of executing untrusted code.
Workarounds
If upgrading immediately is not possible:
- Only call frompretrained with pretrainedmodelnameorpath, custompipeline, and local snapshot directories from sources you fully trust and have audited. - Do not pass custompipeline= pointing at a Hub repository different from the primary pretrainedmodelnameorpath unless you have read its pipeline.py. - Before calling frompretrained on a local snapshot, inspect the snapshot for unexpected .py files, especially under component subdirectories (unet/, scheduler/, etc.) and at the snapshot root.
Why this should have a dedicated CVE
GHSA-j7w6-vpvq-j3gm is a distinct defect from CVE-2026-44513. CVE-2026-44513 is a misplaced-security-gate bug requiring a user-supplied custompipeline argument or a config entry declaring custom code. GHSA-j7w6 is a string-formatting bug where the default custompipeline=None is interpolated into the filename None.py, allowing silent RCE on a fully default frompretrained('repo') call with no kwargs and a modelindex.json that shadows a legitimate class. The root cause root cause and trigger are different, although the fix applied to address CVE-2026-44513 also addresses this vulnerability.
Other sources
Diffusers is the a library for pretrained diffusion models. Prior to 0.38.0, diffusers 0.37.0 allows remote code execution without the trustremotecode=True safeguard when loading pipelines from Hugging Face Hub repositories. The resolvecustompipelineandcls function in pipelineloadingutils.py performs string interpolation on the custompipeline parameter using f"{custompipeline}.py". When custompipeline is not supplied by the user, it defaults to None, which Python interpolates as the literal string "None.py". If an attacker publishes a Hub repository containing a file named None.py with a class that subclasses DiffusionPipeline, the file is automatically downloaded and executed during a standard DiffusionPipeline.frompretrained() call with no additional keyword arguments. The trustremotecode check in DiffusionPipeline.download() is bypassed because it evaluates custompipeline is not None as False (since the kwarg was never supplied), while the downstream code path that actually loads the module resolves the None value into a valid filename. An attacker can achieve silent arbitrary code execution by publishing a malicious model repository with a None.py file and a standard-looking modelindex.json that references a legitimate pipeline class name, requiring only that a victim calls frompretrained on the repository. This vulnerability is fixed in 0.38.0.
— MITRE
Affected Software
Event History
Frequently Asked Questions
What is the severity of CVE-2026-44827?
CVE-2026-44827 has a high severity score of 8.8.
What does CVE-2026-44827 affect?
CVE-2026-44827 affects the Diffusers library by HuggingFace, specifically in the loading of pipelines.
How can CVE-2026-44827 be mitigated?
To mitigate CVE-2026-44827, avoid using untrusted input for the custom_pipeline argument when calling DiffusionPipeline.from_pretrained.
Who is affected by CVE-2026-44827?
Developers using the HuggingFace Diffusers library are affected by CVE-2026-44827.
What type of vulnerability is CVE-2026-44827?
CVE-2026-44827 is a code injection vulnerability that allows remote code execution.