CVE-2026-9856 Overview
CVE-2026-9856 is a path traversal vulnerability [CWE-22] in the Hugging Face transformers library affecting versions <=5.8.0.dev0. The flaw resides in the save_pretrained() methods of PreTrainedTokenizerBase and ProcessorMixin, where keys from the chat_template dictionary are used directly as filenames without validation. An attacker who publishes a malicious repository on the Hugging Face Hub can craft a tokenizer_config.json with keys containing traversal sequences. When a victim downloads and saves the tokenizer or processor, attacker-controlled keys escape the intended save directory, enabling arbitrary file writes with attacker-controlled content.
Critical Impact
Arbitrary file writes on victim systems downloading malicious Hugging Face models, affecting processors including Idefics, Florence, Gemma, Phi, and Qwen-VL.
Affected Products
- huggingface/transformers versions <=5.8.0.dev0
- All processors inheriting from ProcessorMixin (Idefics, Florence, Gemma, Phi, Qwen-VL)
- Any tokenizer using PreTrainedTokenizerBase.save_pretrained()
Discovery Timeline
- 2026-08-02 - CVE CVE-2026-9856 published to NVD
- 2026-08-03 - Last updated in NVD database
Technical Details for CVE-2026-9856
Vulnerability Analysis
The vulnerability occurs during serialization of chat templates to disk. When a tokenizer or processor is saved, the library iterates over the chat_template dictionary and constructs a filename by concatenating each dictionary key with the .jinja extension inside the target directory. The keys originate from an untrusted tokenizer_config.json file downloaded from the Hugging Face Hub. Because the code does not validate whether resolved paths remain within the intended directory, keys such as ../../etc/cron.d/attacker cause writes outside the save location. The Common Weakness Enumeration classifies this pattern as CWE-22 (Improper Limitation of a Pathname to a Restricted Directory).
Root Cause
The root cause is missing path canonicalization on dictionary keys sourced from external configuration. The functions treat template_name as trusted metadata rather than attacker-controlled input. No check compared the resolved parent of template_filepath against the intended chat_template_dir before invoking open() for writing.
Attack Vector
An attacker publishes a Hugging Face Hub repository containing a crafted tokenizer_config.json whose chat_template dictionary keys include directory traversal sequences. A victim then calls AutoTokenizer.from_pretrained() or AutoProcessor.from_pretrained() against the malicious repository and later invokes save_pretrained(). The traversal keys resolve outside the target directory, allowing overwrite of arbitrary files the process can write. User interaction is required, but exploitation only depends on the common workflow of downloading and re-saving a model.
else:
os.makedirs(chat_template_dir, exist_ok=True)
template_filepath = os.path.join(chat_template_dir, f"{template_name}.jinja")
+ # template_name is an untrusted dict key; reject path traversal (CWE-22)
+ if Path(template_filepath).resolve().parent != Path(chat_template_dir).resolve():
+ raise ValueError(f"Invalid chat template name: {template_name!r}")
with open(template_filepath, "w", encoding="utf-8") as f:
f.write(template)
logger.info(f"chat template saved in {template_filepath}")
Source: GitHub Commit eaaaf84 — patch applied to src/transformers/processing_utils.py and src/transformers/tokenization_utils_base.py.
Detection Methods for CVE-2026-9856
Indicators of Compromise
- Files with .jinja extension appearing outside expected model save directories.
- tokenizer_config.json files containing chat_template dictionary keys with ../, ..\, or absolute path segments.
- Unexpected writes to sensitive locations (for example, ~/.ssh/authorized_keys, cron directories, or Python site-packages) originating from ML pipeline processes.
- Model repositories on Hugging Face Hub referencing unusual template names in tokenizer configs.
Detection Strategies
- Scan downloaded tokenizer_config.json files for chat_template keys containing path separator characters before invoking save_pretrained().
- Monitor file system activity of Python processes running transformers, alerting on writes outside declared model directories.
- Audit installed transformers versions across ML workstations and inference nodes for versions <=5.8.0.dev0.
Monitoring Recommendations
- Log all model downloads from external hubs and correlate with subsequent file creation events on the host.
- Enable file integrity monitoring on system directories, startup locations, and user profile paths on hosts that run ML training or inference workloads.
- Track process lineage where Python interpreters spawn writes to non-standard directories under a data-science service account.
How to Mitigate CVE-2026-9856
Immediate Actions Required
- Upgrade transformers to a version that includes commit eaaaf84 on all training, inference, and developer systems.
- Restrict Hugging Face Hub downloads to a curated allowlist of trusted organizations and repositories.
- Run model-loading code under least-privilege service accounts with no write access to system or user startup locations.
- Review historical save_pretrained() output directories for unexpected .jinja files written outside expected paths.
Patch Information
The fix is available in GitHub commit eaaaf84 titled "Fix path traversal when saving named chat templates (#46191)". The patch adds a resolved-path check in both src/transformers/processing_utils.py and src/transformers/tokenization_utils_base.py, rejecting any template_name whose resolved parent directory does not equal the intended chat_template_dir. Additional context is available in the Huntr Bug Bounty Listing.
Workarounds
- Validate chat_template dictionary keys against a strict allowlist (alphanumeric, underscore, hyphen) before calling save_pretrained().
- Execute model save operations inside disposable containers or sandboxes with read-only mounts for sensitive host paths.
- Post-process saved directories to detect and remove files whose canonical paths fall outside the intended output directory.
# Upgrade transformers to a patched build
pip install --upgrade "transformers>5.8.0.dev0"
# Verify patched code is present
python -c "import transformers, inspect; print(inspect.getsourcefile(transformers))"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

