GitHub Security Advisory: GHSA-j7w6-vpvq-j3gm
Package: diffusers (PIP)
Severity: HIGH (CVSS 8.8)
CWE: CWE-94: Improper Control of Generation of Code ('Code Injection')
Vulnerable versions: < 0.38.0
Patched in: 0.38.0

Summary: Diffusers has a `trust_remote_code` bypass via `custom_pipeline` and local custom components

Details:
## 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