Ringkasan Utama
CVE-2026-4372 menimpa Hugging Face transformers sebelum 5.3.0: config.json buatan penyerang bisa mengarahkan field attention internal ke repository Hub milik penyerang, lalu from_pretrained mengunduh dan menjalankan kode Python-nya walau trust_remote_code mati. Upgrade ke 5.3.0 atau lebih baru, pin revision model ke commit hash yang sudah direview, dan muat dari snapshot yang diunduh lebih dulu.
Kalimat di advisory CVE-2026-4372 yang membuat saya berhenti adalah yang menyebut celah ini terjangkau lewat pemakaian standar yang terdokumentasi. Bukan rantai exploit buatan tangan dan bukan flag aneh: hanya pemanggilan from_pretrained biasa pada sebuah model repository, dengan trust_remote_code tetap di default False. Kebanyakan host inference yang akan saya audit menjadikan default itu satu-satunya pagar.
Post ini membaca sumber primer dengan teliti: advisory GitLab dan GitHub, commit fix a7f8e7f, dan dokumentasi Hugging Face terbaru soal remote code, kernel, offline mode dan pickle scanning. Tidak ada exploit di sini, dan saya memisahkan apa yang dinyatakan advisory, apa yang terlihat di diff, dan apa yang tidak bisa saya konfirmasi. Tujuannya checklist loading yang bisa langsung dipakai di host inference self-hosted.
Apa isi advisory sebenarnya
Advisory GitLab, terbit 26 Mei 2026 dan diperbarui 1 Juli 2026, menyebut isu ini sebagai remote code execution di HuggingFace transformers. Isinya: config.json berbahaya bisa mengisi field _attn_implementation_internal dengan ID repository Hub milik penyerang. Saat korban memuat model itu dengan AutoModelForCausalLM.from_pretrained(), library mengunduh dan menjalankan kode Python dari repository tersebut dengan hak akses sistem operasi korban. Advisory menyebut penyebabnya: deserialisasi atribut config tanpa filter, sanitasi field internal yang lemah, dan eksekusi kernel hasil unduhan tanpa sandbox.
Tiga fakta menentukan eksposur Anda. Semua versi sebelum 5.3.0 terdampak, dan 5.3.0 satu-satunya versi perbaikan yang tercantum. Skor dasar CVSS 3.0 adalah 7.8 (High), vector AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, dengan CWE-502 dan CWE-1066. Dan advisory menyatakan celah ini melewati mekanisme trust_remote_code dan terjadi pada pemakaian standar, jadi orang yang memuat model bisa tidak sadar. Entri GitHub Advisory Database GHSA-29pf-2h5f-8g72 sejalan soal versi dan skor, mencantumkan seorang analyst sebagai kredit, menautkan laporan bounty huntr, dan EPSS 0,599 persen saat saya membacanya.
Kenapa trust_remote_code salah tempat untuk dicek
Review yang hanya melakukan grep trust_remote_code=True lalu berhenti menganggap satu flag sebagai seluruh batas keamanan. Dokumentasi menunjukkan minimal tiga jalur terpisah di mana pemuatan model bisa menjalankan kode, masing-masing dengan gate sendiri atau tanpa gate sama sekali. Tabel berikut mendaftarkannya, dan baris terakhir adalah yang ditambahkan CVE ini.
| Jalur | Pemicunya | Gate-nya | Yang tidak dicakup gate |
|---|---|---|---|
| Custom modelling code | Memuat repo yang membawa file modelling sendiri | trust_remote_code, default False; docs bilang aktifkan hanya untuk repository yang Anda percaya dan kodenya sudah Anda baca | Tidak ada yang lain: ia hanya menjaga jalur ini |
| Hub attention kernel | String attn_implementation berbentuk org/repo | Docs terbaru: kernels-community dipercaya, repo lain butuh allow_all_kernels=True | Hanya berguna kalau kode penegaknya berjalan pada nilai yang sampai ke sana |
| Weights berformat pickle | Memuat checkpoint pickle seperti pytorch_model.bin | Tanpa flag. Hub menampilkan import pickle per file dan menjalankan scan ClamAV | Docs Hub sendiri menyebut scan itu tidak 100 persen aman |
| Field internal config.json (CVE-2026-4372) | Config Hub yang membawa key _attn_implementation_internal | Menurut advisory, trust_remote_code tidak menjaganya di versi sebelum 5.3.0 | Semuanya: nilainya datang sebagai data, bukan flag yang Anda berikan |
Pelajarannya ada pada titik di mana data tak tepercaya berubah menjadi jalur kode. Flag yang Anda berikan sendiri mudah diaudit. String yang datang di dalam file unduhan lalu diperlakukan sebagai konfigurasi tidak mudah, itulah sebabnya fix harus mengubah cara config dibaca, bukan menambah warning.
Apa yang diubah commit fix
Commit-nya a7f8e7f, berjudul hanya fix security vuln, menyentuh dua file dengan 22 baris ditambah dan 9 dihapus. Pesannya tidak menyebut CVE, jadi semua yang di bawah ini berasal dari diff dan komentar kodenya, bukan dari tulisan analisis. Dua guard ditambahkan, dan yang kedua lebih informatif.
# Condensed from the diff of commit a7f8e7f. Not a verbatim patch.
# src/transformers/configuration_utils.py, in the loop that applies extra
# kwargs from a Hub config as attributes on the config object.
# Wrong (before): every key from the downloaded config became an attribute.
setattr(self, key, value)
# Right (after): the two internal fields are skipped. The diff comment says
# these should use the public field instead.
if key not in ("_attn_implementation_internal", "_experts_implementation_internal"):
setattr(self, key, value) # still inside the existing try/except
# src/transformers/integrations/hub_kernels.py, is_kernel(attn_implementation)
# Before: one regex; any string shaped like org/repo[@rev][:name] was a Hub kernel.
# After: None returns False; the org name is captured; anything outside
# "kernels-community" logs warning_once ("For security reasons, we
# currently only accept kernels from the kernels-community ...")
# and returns False.Guard pertama membuat pemuatan konfigurasi mengabaikan dua field internal, sehingga config Hub tidak lagi bisa mengisinya. Komentar di diff bilang field itu seharusnya memakai field publik. Guard kedua mempersempit apa yang dihitung sebagai Hub kernel: nama org kini ditangkap, dan apa pun di luar kernels-community mencatat warning lalu mengembalikan False, dengan komentar alasan bahwa memuat kernel acak membuka arbitrary code execution. Bersama-sama keduanya memotong jalur di sumber dan di tujuannya.
Jangan simpulkan ini bukti bahwa tidak ada jalur lain. Advisory menyebut tiga penyebab, dan commit menangani dua yang terlihat di diff. Saya tidak mengaudit 5.3.0 di luar commit itu, dan advisory tidak mencantumkan rilis perbaikan di jalur 4.x, jadi host yang terkunci di 4.x tidak punya fix selain upgrade major.
Cek apakah host Anda terpapar
Dua hal layak dicek di setiap mesin yang memuat model: versi library terpasang, dan config di cache yang membawa key internal dengan nilai berbentuk repository. Script di bawah hanya memakai standard library. Ia membaca lokasi cache seperti yang dijelaskan dokumentasi Hugging Face, HF_HUB_CACHE dulu, lalu HF_HOME dengan subfolder hub, dan tidak mereproduksi apa pun yang berbahaya.
#!/usr/bin/env python3
"""Audit one host for CVE-2026-4372 exposure. Read-only; standard library only.
Exit code 1 means: upgrade, or review a flagged cache entry."""
import json
import os
import re
import sys
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
FIXED = (5, 3, 0) # the advisories list 5.3.0 as the fixed version
INTERNAL_KEYS = {"_attn_implementation_internal", "_experts_implementation_internal"}
TRUSTED_ORG = "kernels-community"
def parse(v: str) -> tuple:
# "5.3.0rc1" -> (5, 3, 0). re.match avoids reading "0rc1" as 1.
return tuple(int(re.match(r"\d+", p).group()) if re.match(r"\d+", p) else 0
for p in v.split(".")[:3])
def cache_root() -> Path:
# Order from the huggingface_hub docs: HF_HUB_CACHE, else HF_HOME/hub.
# XDG_CACHE_HOME only matters when HF_HOME is unset and is not handled here.
if os.environ.get("HF_HUB_CACHE"):
return Path(os.environ["HF_HUB_CACHE"])
home = os.environ.get("HF_HOME") or str(Path.home() / ".cache" / "huggingface")
return Path(home) / "hub"
def internal_values(node, path=""):
if isinstance(node, dict):
for key, val in node.items():
if key in INTERNAL_KEYS:
yield f"{path}/{key}", val
yield from internal_values(val, f"{path}/{key}")
elif isinstance(node, list):
for i, val in enumerate(node):
yield from internal_values(val, f"{path}[{i}]")
def main() -> int:
bad = 0
try:
installed = version("transformers")
status = "OK" if parse(installed) >= FIXED else "VULNERABLE"
bad += status == "VULNERABLE"
print(f"transformers {installed}: {status} (fixed in 5.3.0)")
except PackageNotFoundError:
print("transformers not installed in this interpreter")
for cfg in cache_root().rglob("config.json"):
try:
data = json.loads(cfg.read_text())
except (OSError, ValueError):
continue
for where, val in internal_values(data):
# A repo-shaped value outside the trusted org is a lead, not proof.
if isinstance(val, str) and "/" in val and val.split("/")[0] != TRUSTED_ORG:
bad += 1
print(f"REVIEW {cfg} {where} = {val}")
return 1 if bad else 0
if __name__ == "__main__":
sys.exit(main())Anggap temuan sebagai petunjuk untuk direview, bukan bukti kompromi. Dari sumber yang ada saya tidak bisa memastikan apakah config sah pernah memuat key ini, jadi script hanya menandai nilai berbentuk repo dari luar kernels-community. Kalau ada yang ditandai, cari model repository asal cache itu dan siapa yang menerbitkan commit-nya. Script ini juga tidak melihat XDG_CACHE_HOME, yang menurut docs hanya berlaku kalau HF_HOME tidak diset.
Kegagalan klasiknya: mengaudit flag, bukan datanya
Kegagalan yang saya maksud adalah review yang lolos karena setiap call site bersih. Semua from_pretrained di codebase tidak memakai trust_remote_code, grep membuktikannya, dan host dinyatakan aman. Di bawah advisory ini grep itu tidak cukup, karena input berbahayanya adalah field di dalam file yang diambil saat load.
Ada trade-off kedua yang lebih sunyi pada pinning. Dokumentasi menyebut argumen revision menerima branch, tag atau commit id. Branch atau tag bisa bergeser, jadi config.json yang bersih saat review bisa diganti belakangan, sedangkan commit hash tidak. Docs instalasi menambahkan bahwa dengan commit hash Transformers memakai cache lokal untuk file commit itu, sementara branch atau tag butuh satu pengecekan ke Hub saat load. Pin ke hash menukar kenyamanan dengan config yang bisa Anda nalar.
Upgrade juga ada ongkosnya. Satu-satunya rilis perbaikan di advisory adalah 5.3.0, jadi tim di major version lebih lama harus melakukan upgrade major untuk menutup ini. Saya tidak memverifikasi apa lagi yang berubah di antara dua jalur itu, jadi jalankan test pemuatan model di staging dulu dan jangan menganggap lompatannya gratis.
Checklist loading untuk inference self-hosted
Pola yang akan saya pakai memisahkan loading menjadi tahap build online dan runtime offline. Di tahap build, job CI terautentikasi mengunduh snapshot pada commit hash yang sudah direview manusia. Di runtime, container memakai HF_HUB_OFFLINE=1, yang menurut docs mencegah panggilan HTTP ke Hub, ditambah local_files_only supaya load gagal dengan jelas kalau ada yang kurang, bukan menghubungi luar.
# --- build stage (CI, online): download once, at a commit a human reviewed ---
from huggingface_hub import snapshot_download
REVIEWED = {
# model repo -> full commit hash recorded in version control, never "main"
"meta-llama/Llama-3.2-1B": "PASTE_THE_REVIEWED_40_CHAR_COMMIT_HASH",
}
for repo_id, commit in REVIEWED.items():
snapshot_download(repo_id=repo_id, revision=commit)
# --- runtime stage (container, offline) ---
# Dockerfile, not Python: huggingface_hub reads its variables at import time,
# so os.environ["HF_HUB_OFFLINE"] set after the import does nothing.
# ENV HF_HUB_OFFLINE=1 USE_HUB_KERNELS=0 HF_HOME=/models/hf
from transformers import AutoModelForCausalLM
MODEL_ID = "meta-llama/Llama-3.2-1B"
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
revision=REVIEWED[MODEL_ID], # a hash cannot move; a branch or tag can
local_files_only=True, # fail loudly instead of reaching out
attn_implementation="sdpa", # built-in PyTorch attention, no Hub kernel
# trust_remote_code stays at its default False. Do not add allow_all_kernels.
)Dua detail penting. Docs menyatakan huggingface_hub membaca environment variable saat import, jadi mengatur HF_HUB_OFFLINE di dalam Python setelah import tidak berpengaruh; taruh di environment container. Dan docs offline mencatat bahwa branch atau tag masih bisa ter-resolve lewat cache, alasan lain untuk pin hash. Saya tidak memastikan apakah offline mode juga memblokir library kernels mengambil kernel, jadi checklist ini juga mematikan Hub kernel dengan USE_HUB_KERNELS=0, yang menurut panduan kernels menonaktifkannya di semua tempat tanpa mengubah kode.
Tambahkan pengecekan CI yang menggagalkan build jika ada trust_remote_code=True atau allow_all_kernels=True secara literal di repository Anda. Ini tidak menangkap nilai yang datang lewat data, tetapi mencegah dua opt-in terdokumentasi itu ditambahkan diam-diam lewat template config.
Apa yang dihentikan tiap kontrol, dan apa yang masih terbuka
Tidak ada satu kontrol yang menutup semua jalur, dan beberapa saling tumpang tindih. Tabel ini adalah pembacaan saya atas dokumentasi, lengkap dengan batasnya, supaya Anda bisa memilih mana yang dipakai dan celah mana yang diterima.
| Kontrol | Yang dihentikan | Yang masih terbuka | Dasar |
|---|---|---|---|
| Upgrade ke 5.3.0 atau lebih baru | Jalur config.json yang dijelaskan di CVE-2026-4372 | Weights pickle dan jalur lain yang belum ditemukan | Advisory |
| Pin revision ke commit hash yang sudah direview | Repository yang mengubah config setelah Anda review | Commit yang direview asal-asalan | Docs auto classes dan instalasi Transformers |
| Unduh dulu, lalu HF_HUB_OFFLINE=1 dan local_files_only | Fetch tak terduga dari Hub saat startup | Artefak buruk yang sudah masuk image. Fetch kernel belum terkonfirmasi | Docs instalasi dan huggingface_hub |
| USE_HUB_KERNELS=0 | Hub kernel di semua tempat, menurut panduan kernels | Apa pun yang bukan kernel, dan kecepatan yang diberikan kernel itu | Panduan loading kernels |
| Jangan pernah mengirim allow_all_kernels atau trust_remote_code sebagai True | Dua opt-in terdokumentasi menuju kode pihak ketiga | Data yang sampai ke jalur kode tanpa opt-in, yaitu CVE ini | Panduan loading kernels dan docs auto classes |
| Publisher tepercaya dan signed commit untuk weights | Pemakaian sembarangan checkpoint pickle tak dikenal | Tanda tangan membuktikan asal, bukan bahwa file aman | Dok pickle scanning Hub |
Satu kontrol tambahan berasal dari saya, bukan dari sumber: batasi akses jaringan keluar dari host inference ke artifact store yang benar-benar Anda pakai. Kode yang diunduh saat load harus datang dari suatu tempat, dan egress allow-list mengubah fetch diam-diam menjadi yang terblokir dan tercatat. Saya belum mengujinya terhadap CVE ini, jadi hitung sebagai defence in depth, bukan fix.
Yang dikerjakan hari Senin
Kerjakan berurutan. Dua langkah pertama hanya butuh beberapa menit dan memberi tahu apakah sisanya mendesak.
- Jalankan script audit di setiap host yang memuat model, termasuk laptop yang membangun image, dan catat versi library yang dilaporkan.
- Upgrade yang di bawah 5.3.0 di host staging, jalankan test pemuatan model dan generation, lalu promosikan. Tambahkan batas bawah versi di file constraints supaya resolve berikutnya tidak mundur.
- Ganti referensi branch dan tag di loader dengan commit hash yang sudah direview, dan simpan hash itu di version control di samping kode.
- Pindahkan proses download ke CI, masukkan snapshot ke image atau volume, dan set HF_HUB_OFFLINE=1, USE_HUB_KERNELS=0 serta local_files_only di runtime.
- Tambahkan grep CI untuk dua opt-in itu, dan tentukan siapa yang memegang review sebuah model repository sebelum hash-nya boleh masuk daftar.
# 1. What is installed, and is it below 5.3.0?
python -m pip show transformers | grep -i '^version'
python audit_transformers_cve.py ; echo "exit=$?"
# 2. Hold the floor so a later resolve cannot go backwards.
echo 'transformers>=5.3.0' >> constraints.txt
python -m pip install -c constraints.txt -U transformers
# 3. CI guard: fail the build on the two documented opt-ins to third-party code.
if grep -rnE 'trust_remote_code *= *True|allow_all_kernels *= *True' src/ ; then
echo "third-party code opt-in found" >&2 ; exit 1
fiKalau audit menemukan config cache yang mencurigakan, anggap host berpotensi terdampak: cari repository asalnya, rotasi kredensial yang bisa dibaca host itu, dan periksa apa saja yang boleh dijangkau prosesnya. Itu praktik insiden yang umum, bukan sesuatu yang diminta advisory.
Aturan yang saya ambil: flag keamanan hanya melindungi jalur yang dibuatnya. Upgrade ke 5.3.0 atau lebih baru, muat snapshot yang di-pin dari runtime offline, dan audit data yang dibaca loader seteliti argumen yang Anda berikan.
Sumber dan bacaan lanjutan
- GitLab Advisory Database: CVE-2026-4372 di transformers
- GitHub Advisory Database: GHSA-29pf-2h5f-8g72
- Commit a7f8e7f di huggingface/transformers: diff fix keamanan
- Docs Hugging Face Transformers: loading kernels dan allow_all_kernels
- Docs Hugging Face Transformers: auto classes, trust_remote_code dan revision
- Docs Hugging Face Transformers: instalasi, cache dan offline mode
- Docs huggingface_hub: environment variables, termasuk HF_HUB_OFFLINE
- Docs Hugging Face Hub: pickle scanning

