CVE-2026-44827: Diffusers Diffusers Has A `trust_remote_code` Bypass Vulnerability

HIGH
Advisory Identifier: GHSA-j7w6-vpvq-j3gm
CVSS 8.8
CWE-94
Published
Advisory Summary

CVE-2026-44827 is a HIGH (CVSS 8.8) diffusers has a `trust_remote_code` bypass vulnerability (CWE-94: Improper Control of Generation of Code ('Code Injection')) affecting Diffusers libraries in the PyPI ecosystem. The issue involves diffusers has a `trust_remote_code` bypass via `custom_pipeline` and local custom components. Remediation is available by upgrading affected packages to version 0.38.0.

CVE-2026-44827 at a Glance

CVE Identifier CVE-2026-44827
GHSA Identifier GHSA-j7w6-vpvq-j3gm
Severity HIGH
CVSS v3.1 Base Score 8.8
CWE Classification CWE-94: Improper Control of Generation of Code ('Code Injection')
Affected Product Diffusers
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 8.8

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-44827

To remediate this vulnerability, upgrade all affected Diffusers dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

diffusers fixed: 0.38.0
pip install diffusers>=0.38.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show diffusers

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2026-44827, inspect your installed packages using pip:

pip show diffusers

Analyze the terminal output to locate any version of Diffusers in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.38.0), your application is affected.

Note: The pip show diffusers command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-94: Improper Control of Generation of Code ('Code Injection'), where the product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. In Diffusers, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Background This vulnerability is found in the `DiffusionPipeline.from_pretrained` flow, which is used to load a pipeline from the HuggingFace Hub. This function accepts an optional `custom_pipeline` 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 `_class_name` field in `model_index.json` (the repo config file) is set to a custom class. Any attempt to use a custom pipeline throws the following exception, requesting that `trust_remote_code` is also passed: ```python DiffusionPipeline.from_pretrained( pretrained_model_name_or_path='ido-shani/custom-pipeline', custom_pipeline="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 `trust_remote_code=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 `custom_pipeline` or `trust_remote_code` kwargs and nothing suspicious in the config. The `from_pretrained` call succeeds and returns a functional pipeline. ## Naive Flow First, all relevant arguments are popped from kwargs and stored in local variables. Given a `pretrained_model_name_or_path` 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 `trust_remote_code`. 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 `trust_remote_code` was passed and raises otherwise: ```python # pipeline_utils.py:1645-1652 load_pipe_from_hub = custom_pipeline is not None and f"{custom_pipeline}.py" in filenames ... if load_pipe_from_hub and not trust_remote_code: raise ValueError(...) ``` It then runs `_get_pipeline_class`, 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 `allow_patterns` list used to filter the snapshot download to necessary files only, the custom pipeline file is explicitly included if present: ```python # pipeline_utils.py:1707 allow_patterns += [f"{custom_pipeline}.py"] if f"{custom_pipeline}.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 `from_pretrained` is loading the pipeline class a second time, this time to actually instantiate it. Before calling `_get_pipeline_class` again, `_resolve_custom_pipeline_and_cls` is called to translate the `custom_pipeline` name into a local path, since the files have already been downloaded: ```python # pipeline_loading_utils.py:965-974 def _resolve_custom_pipeline_and_cls(folder, config, custom_pipeline): custom_class_name = None if os.path.isfile(os.path.join(folder, f"{custom_pipeline}.py")): custom_pipeline = os.path.join(folder, f"{custom_pipeline}.py") elif isinstance(config["_class_name"], (list, tuple)) and os.path.isfile( os.path.join(folder, f"{config['_class_name'][0]}.py") ): custom_pipeline = os.path.join(folder, f"{config['_class_name'][0]}.py") custom_class_name = config["_class_name"][1] return custom_pipeline, custom_class_name ``` When `custom_class_name` is `None` (i.e. `custom_pipeline` was given as a kwarg rather than via the config), `_get_pipeline_class` will scan the file and automatically identify the class that subclasses `DiffusionPipeline`. Once this is done, `_get_pipeline_class` is invoked with the resolved local path, which loads the custom code, retrieves the class object, and proceeds with instantiation. ## The Vulnerability `_resolve_custom_pipeline_and_cls` receives `custom_pipeline` 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 `_get_pipeline_class` (inside `from_pretrained`, after `download()` returns). The trust\_remote\_code check lives entirely in `download()`, which evaluated `custom_pipeline is None -> False` and skipped it. By the time `_resolve_custom_pipeline_and_cls` 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 `allow_patterns` line above shows it makes the same error: `f"{None}.py"` = `"None.py"` is added to `allow_patterns` 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").write_text(":)") ``` With this, `model_index.json` can contain `"_class_name": "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](https://huggingface.co/DDUF/tiny-flux-dev-pipe-dduf). All the attacker needs the victim to run is: ```python from diffusers import DiffusionPipeline pipeline = DiffusionPipeline.from_pretrained('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.from_pretrained` 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 `custom_pipeline` or `trust_remote_code` kwargs and nothing suspicious in the config. The `from_pretrained` call succeeds and returns a functional pipeline. # Occurrences https://github.com/huggingface/diffusers/blob/e1b5db52bda85d47a4f8f75954f77e672a8f7f1c/src/diffusers/pipelines/pipeline_loading_utils.py#L976 # Patches Yes. Fixed in **diffusers 0.38.0** via [PR #13448](https://github.com/huggingface/diffusers/pull/13448). All users on versions `< 0.38.0` should upgrade: ```bash pip install --upgrade "diffusers>=0.38.0" ``` The fix moves the `trust_remote_code` gate out of `DiffusionPipeline.download()` and into `get_cached_module_file` in `src/diffusers/utils/dynamic_modules_utils.py`, which is the actual chokepoint for every dynamic module load (local, Hub, or community mirror). All three variants now raise `ValueError` when trust_remote_code=False instead of executing untrusted code. # Workarounds If upgrading immediately is not possible: - Only call `from_pretrained` with `pretrained_model_name_or_path`, `custom_pipeline`, and local snapshot directories from sources you fully trust and have audited. - Do not pass `custom_pipeline=` pointing at a Hub repository different from the primary `pretrained_model_name_or_path` unless you have read its `pipeline.py`. - Before calling `from_pretrained` 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 `custom_pipeline` argument or a config entry declaring custom code. GHSA-j7w6 is a string-formatting bug where the default custom_pipeline=None is interpolated into the filename `None.py`, allowing silent RCE on a fully default `from_pretrained('repo')` call with no kwargs and a `model_index.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.

CVE-2026-44827 Affected Diffusers Versions

1 component(s)
Fixed Versions:
0.38.0
Product / Package Ecosystem Affected Versions Fixed Version
diffusers
PyPI
< 0.38.0
0.38.0

References

Frequently Asked Questions

What is CVE-2026-44827?
CVE-2026-44827 is a security vulnerability in Diffusers involving Diffusers has a `trust_remote_code` bypass via `custom_pipeline` and local custom components under CWE-94: Improper Control of Generation of Code ('Code Injection').
Which Diffusers versions are affected?
Affected versions include releases prior to 0.38.0 across affected packages: diffusers.
What version fixes CVE-2026-44827?
Upgrading to 0.38.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is diffusers affected?
Yes, diffusers is affected in releases prior to 0.38.0. Upgrade your dependency to 0.38.0 or later.
What is the CVSS score of CVE-2026-44827?
CVE-2026-44827 has a CVSS v3.1 base score of 8.8 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-44827?
CVE-2026-44827 is classified under CWE-94: Improper Control of Generation of Code ('Code Injection') (The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.).
How can I check PyPI dependencies for CVE-2026-44827?
Run the dependency tree command: 'pip show diffusers'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-j7w6-vpvq-j3gm",
  "modified" : "2026-06-05T17:53:00Z",
  "published" : "2026-05-07T02:24:22Z",
  "withdrawn" : "2026-05-07T05:25:32Z",
  "aliases" : [ "CVE-2026-44827" ],
  "summary" : "Diffusers has a `trust_remote_code` bypass via `custom_pipeline` and local custom components",
  "details" : "## Background\n\nThis vulnerability is found in the `DiffusionPipeline.from_pretrained` flow, which is used to load a pipeline from the HuggingFace Hub.\n\nThis function accepts an optional `custom_pipeline` 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 `_class_name` field in `model_index.json` (the repo config file) is set to a custom class.\n\nAny attempt to use a custom pipeline throws the following exception, requesting that `trust_remote_code` is also passed:\n\n```python\nDiffusionPipeline.from_pretrained(\n    pretrained_model_name_or_path='ido-shani/custom-pipeline',\n    custom_pipeline=\"custom\"\n)\n\nValueError: The repository for ido-shani/custom-pipeline contains custom code in\ncustom.py which must be executed to correctly load the model. You can inspect the\nrepository content at https://hf.co/ido-shani/custom-pipeline/blob/main/custom.py.\nPlease pass the argument `trust_remote_code=True` to allow custom code to be run.\n```\n\nThe vulnerability is a silent RCE - it allows arbitrary code to be loaded through the custom\\_pipeline flow from a Hub repo, with no `custom_pipeline` or `trust_remote_code` kwargs and nothing suspicious in the config. The `from_pretrained` call succeeds and returns a functional pipeline.\n\n## Naive Flow\n\nFirst, all relevant arguments are popped from kwargs and stored in local variables.\n\nGiven a `pretrained_model_name_or_path` 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 `trust_remote_code`. 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 `trust_remote_code` was passed and raises otherwise:\n\n```python\n# pipeline_utils.py:1645-1652\nload_pipe_from_hub = custom_pipeline is not None and f\"{custom_pipeline}.py\" in filenames\n\n...\n\nif load_pipe_from_hub and not trust_remote_code:\n    raise ValueError(...)\n```\n\nIt then runs `_get_pipeline_class`, 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 `allow_patterns` list used to filter the snapshot download to necessary files only, the custom pipeline file is explicitly included if present:\n\n```python\n# pipeline_utils.py:1707\nallow_patterns += [f\"{custom_pipeline}.py\"] if f\"{custom_pipeline}.py\" in filenames else []\n```\n\nThe function then checks if all expected files are already present, and either exits early or triggers a snapshot download with those patterns.\n\nThe next step in `from_pretrained` is loading the pipeline class a second time, this time to actually instantiate it. Before calling `_get_pipeline_class` again, `_resolve_custom_pipeline_and_cls` is called to translate the `custom_pipeline` name into a local path, since the files have already been downloaded:\n\n```python\n# pipeline_loading_utils.py:965-974\ndef _resolve_custom_pipeline_and_cls(folder, config, custom_pipeline):\n    custom_class_name = None\n    if os.path.isfile(os.path.join(folder, f\"{custom_pipeline}.py\")):\n        custom_pipeline = os.path.join(folder, f\"{custom_pipeline}.py\")\n    elif isinstance(config[\"_class_name\"], (list, tuple)) and os.path.isfile(\n        os.path.join(folder, f\"{config['_class_name'][0]}.py\")\n    ):\n        custom_pipeline = os.path.join(folder, f\"{config['_class_name'][0]}.py\")\n        custom_class_name = config[\"_class_name\"][1]\n\n    return custom_pipeline, custom_class_name\n```\n\nWhen `custom_class_name` is `None` (i.e. `custom_pipeline` was given as a kwarg rather than via the config), `_get_pipeline_class` will scan the file and automatically identify the class that subclasses `DiffusionPipeline`.\n\nOnce this is done, `_get_pipeline_class` is invoked with the resolved local path, which loads the custom code, retrieves the class object, and proceeds with instantiation.\n\n## The Vulnerability\n\n`_resolve_custom_pipeline_and_cls` receives `custom_pipeline` from the kwargs - when not supplied it defaults to `None`. That `None` is used in string formatting: `f\"{None}.py\"` = `\"None.py\"`.\n\n**If the repo contains a file with this name, it will be detected as a custom pipeline.**\n\nThis is only reached on the second invocation of `_get_pipeline_class` (inside `from_pretrained`, after `download()` returns). The trust\\_remote\\_code check lives entirely in `download()`, which evaluated `custom_pipeline is None -> False` and skipped it. By the time `_resolve_custom_pipeline_and_cls` runs, it is no longer relevant.\n\nAs 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 `allow_patterns` line above shows it makes the same error: `f\"{None}.py\"` = `\"None.py\"` is added to `allow_patterns` and fetched.\n\nWhat 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:\n\n```python\nfrom diffusers import FluxPipeline as _FluxPipeline\n\nclass FluxPipeline(_FluxPipeline):\n    pass\n\n# INSERT MALICIOUS CODE HERE\nimport pathlib\npathlib.Path(\"/tmp/pwned\").write_text(\":)\")\n```\n\nWith this, `model_index.json` can contain `\"_class_name\": \"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](https://huggingface.co/DDUF/tiny-flux-dev-pipe-dduf).\n\nAll the attacker needs the victim to run is:\n\n```python\nfrom diffusers import DiffusionPipeline\n\npipeline = DiffusionPipeline.from_pretrained('ido-shani/none-py-trust-remote-code-bypass')\n```\n\n## PoC\n\n-   Upload this zip as a model to the hub. https://drive.google.com/file/d/1mULARMLJJUTCi57xIv0wtDauko-JW0h7/view?usp=sharing\n-   Run `DiffusionPipeline.from_pretrained` on the uploaded model hub identifier.\n-   RCE occured; `/tmp/pwned` was created. If you are running the exploit on windows, change the path touched in `None.py`.\n\n# Impact\n\nThe vulnerability is a silent RCE - it allows arbitrary code to be loaded through the custom\\_pipeline flow from a Hub repo, with no `custom_pipeline` or `trust_remote_code` kwargs and nothing suspicious in the config. The `from_pretrained` call succeeds and returns a functional pipeline.\n\n# Occurrences\n\nhttps://github.com/huggingface/diffusers/blob/e1b5db52bda85d47a4f8f75954f77e672a8f7f1c/src/diffusers/pipelines/pipeline_loading_utils.py#L976\n\n# Patches\n\nYes. Fixed in **diffusers 0.38.0** via [PR #13448](https://github.com/huggingface/diffusers/pull/13448). All users on versions `< 0.38.0` should upgrade:\n\n```bash\npip install --upgrade \"diffusers>=0.38.0\"\n```\n\nThe fix moves the `trust_remote_code` gate out of `DiffusionPipeline.download()` and into `get_cached_module_file` in `src/diffusers/utils/dynamic_modules_utils.py`, which is the actual chokepoint for every dynamic module load (local, Hub, or community mirror). All three variants now raise `ValueError` when trust_remote_code=False instead of executing untrusted code.  \n\n# Workarounds\n\nIf upgrading immediately is not possible:\n\n- Only call `from_pretrained` with `pretrained_model_name_or_path`, `custom_pipeline`, and local snapshot directories from sources you fully trust and have audited.\n- Do not pass `custom_pipeline=` pointing at a Hub repository different from the primary `pretrained_model_name_or_path` unless you have read its `pipeline.py`.\n- Before calling `from_pretrained` on a local snapshot, inspect the snapshot for unexpected `*.py` files, especially under component subdirectories (`unet/`, `scheduler/`, etc.) and at the snapshot root.\n\n# Why this should have a dedicated CVE\n\nGHSA-j7w6-vpvq-j3gm is a distinct defect from CVE-2026-44513. CVE-2026-44513 is a misplaced-security-gate bug requiring a user-supplied `custom_pipeline` argument or a config entry declaring custom code. GHSA-j7w6 is a string-formatting bug where the default custom_pipeline=None is interpolated into the filename `None.py`, allowing silent RCE on a fully default `from_pretrained('repo')` call with no kwargs and a `model_index.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.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "diffusers"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.38.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/huggingface/diffusers/security/advisories/GHSA-j7w6-vpvq-j3gm"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-44827"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/huggingface/diffusers/pull/13448"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/huggingface/diffusers/commit/a37f6f8394ac2a7ee8360c3abea811efe54512b1"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/huggingface/diffusers"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/huggingface/diffusers/releases/tag/v0.38.0"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/pypa/advisory-database/tree/main/vulns/diffusers/PYSEC-2026-41.yaml"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-94" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-07T02:24:22Z",
    "nvd_published_at" : "2026-05-14T17:16:23Z"
  }
}