Safeguard
AI Security•

Transformers CVE-2026-80047: Remote Code Cached Before You Say No

Hugging Face Transformers wrote a repo's custom_generate/generate.py to disk before asking for trust_remote_code consent. Fixed in 5.17.0, though the advisory still lists no patch.

Safeguard Security Team
5 min read

On 1 September 2026, CERT/CC published VU#456290 and GitHub published GHSA-x9r9-c232-4q39 for CVE-2026-80047 in Hugging Face Transformers. When code asked a model to use a custom generation method from a Hub repository, Transformers downloaded and cached that repository's custom_generate/generate.py before checking whether the user had agreed to run remote code.

Execution was still gated by consent. The file write was not. CERT/CC's concern is that the file persists in the module cache and could later be picked up during a trusted load. GitHub rates it 7.8, high.

What's affected

  • Package: transformers on PyPI.
  • Listed range: the GHSA and the CERT/CC note both list 4.49.0 through 5.8.1. Neither lists a fixed version. The CERT/CC note, last revised 22 September, says no vendor patch or advisory was available when it was written.
  • What actually changed: pull request #48620, "Avoid unconditionally downloading remote hub file", was merged on 8 September 2026 as commit cbc1651. We compared the source of several releases. In 5.17.0 (uploaded 9 September), generation/utils.py checks that the file exists with has_file() and only downloads it after the trust check. In 5.9.0, 5.12.0 and 5.16.1, the same function still calls get_cached_module_file() before resolve_trust_remote_code().

That gap matters for scanners. Versions 5.9.0 through 5.16.1 contain the old ordering but sit outside the published range, so a tool that reads only the advisory range will call them clean. The current release is 5.20.1.

How to check if you're exposed

Start with the version:

pip show transformers | grep -i ^version
python -c "import transformers; print(transformers.__version__)"

Treat 4.49.0 up to and including 5.16.1 as carrying the old behavior. Then check whether the vulnerable path is reachable. Two entry points call load_custom_generate():

  • model.generate(custom_generate="org/repo", ...) calls it with whatever trust_remote_code you passed. On old versions, the file is fetched before the consent check raises. This is the path the advisory is about.
  • from_pretrained(..., trust_remote_code=True) calls it only after you've already agreed to remote code, so the ordering bug doesn't change much there.

Search your code for the first pattern:

grep -rnE 'custom_generate\s*=' --include=*.py .

If the repo id passed to custom_generate is a constant you reviewed, exposure is low. If it comes from user input, a config file others can edit, or a model card, it's reachable.

Then look at what's already cached:

MODS="${HF_MODULES_CACHE:-${HF_HOME:-$HOME/.cache/huggingface}/modules}"
find "$MODS" -name 'generate.py' -print 2>/dev/null
ls -la "$MODS/transformers_modules" 2>/dev/null

Any generate.py from a repository you never chose to trust is the artifact this bug leaves behind.

What to do now

  1. Upgrade to transformers 5.17.0 or later. That release contains the fix even though the advisory doesn't name it.
  2. Clear the modules cache on shared machines, CI runners and notebook servers. CERT/CC's workaround is to periodically inspect or clear ~/.cache/huggingface/modules.
  3. Don't pass untrusted repo ids to custom_generate. Keep an allowlist of reviewed repositories and pin a revision.
  4. Give CI and inference jobs a fresh, per-job HF_HOME so one job's cached modules can't be reused by another.
  5. Add a manual override in your vulnerability tooling for 5.9.0 through 5.16.1 if it relies on the GHSA range alone.

Why it matters

Remote code in model repositories is a supply chain channel, and trust_remote_code is the gate. The bug here was ordering: fetch, then ask. Other loading paths in Transformers, such as AutoModel and AutoTokenizer, already asked first, which is why the inconsistency stood out to the reporter.

There's a second lesson in the version data. When a maintainer fixes a bug without updating the advisory, the published range and the real fix drift apart. Advisory ranges are a starting point. Checking whether your code calls the affected function, and whether the fix is in the source you ship, gets you a better answer than the range alone.

Try it yourself

Run the grep above on one repository and count how many transformers imports actually reach custom_generate. Most projects that depend on Transformers never call it. That difference between "package present" and "function called" is what reachability analysis measures:

Click through it at your own pace, or open it full screen.

Quick check

Does CVE-2026-80047 let a malicious repo run code without consent?

Not directly. Execution was still gated by the trust check. The flaw is that the file was written to the module cache before consent, where it persisted and could be served during a later trusted load.

Which version contains the fix?

5.17.0, uploaded 9 September 2026. Pull request #48620 was merged the day before. The GHSA and CERT/CC note list no fixed version.

Is calling from_pretrained without trust_remote_code affected?

No. In the affected code, from_pretrained only calls load_custom_generate when trust_remote_code is true. The risky path is generate with a custom_generate repo id.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.