Knihovna Transformers čte podle indexu i soubory mimo složku modelu a opravu nemá
Když Transformers načítá model rozdělený do víc souborů, vezme jejich jména z indexu ležícího vedle vah a připojí je ke složce modelu bez jediné kontroly. Jméno, které ze složky vede pryč, tak projde a knihovna otevře soubor někde jinde na disku. Nálezce chybu poslal do programu odměn v červnu, hlášení v repozitáři po měsíci ticha zavřel automat a záznam CVE-2026-75104 vyšel 17. srpna bez opravené verze.

Index říká, ve kterém souboru která váha leží
Váhy velkého modelu se do jednoho souboru nevejdou, a tak se ukládají po kusech. Vedle nich leží index – soubor model.safetensors.index.json nebo jeho starší obdoba pro formát .bin –, který u každého tenzoru uvádí, ve kterém kusu se má hledat. Knihovna Transformers od Hugging Face si při volání from_pretrained ten index přečte a otevře soubory, které v něm stojí. Jde o nejběžnější způsob, jak se dnes model načítá; za poslední měsíc si balíček z PyPI vzalo podle pypistats.org 195 692 642 stažení.
Jméno z indexu se připojí ke složce bez kontroly
Funkce get_checkpoint_shard_files v souboru src/transformers/utils/hub.py vezme hodnoty z indexu a u modelu, který leží v místní složce, je k ní prostě přilepí:
shard_filenames = sorted(set(index["weight_map"].values()))
...
if os.path.isdir(pretrained_model_name_or_path):
shard_filenames = [os.path.join(pretrained_model_name_or_path, subfolder, f) for f in shard_filenames]
Funkce os.path.join ale cestu nenarovnává. Když jméno v indexu obsahuje odkaz na nadřazený adresář, výsledek ze složky modelu vede pryč; když je jméno rovnou absolutní cesta, předchozí část se zahodí celá. Nikde v té funkci není normpath, commonpath ani nic, co by absolutní cestu odmítlo. Knihovna pak otevře, co v indexu stojí, a u souboru ve formátu safetensors načte i jeho obsah.
Druhá větev té funkce, tedy model stahovaný z Hugging Face podle jména, jde jinudy – jména se předávají stahovací funkci cached_files, ne spojení cest. Nálezce k tomu píše, že tudy se chyba využít nedá, protože se jména porovnávají se seznamem souborů na serveru. Doložený případ je tak složka s modelem, kterou někdo dostal jinak: rozbalený archiv, sdílený adresář na stroji s víc uživateli, naklonovaný repozitář.
Známka je střední, protože oběť musí model sama načíst
Popis v záznamu CVE-2026-75104 mluví o čtení souborů mimo složku modelu a o ohledávání souborového systému, ne o spuštění cizího kódu. Tomu odpovídají i známky: 6,8 podle CVSS 4.0 od firmy VulnCheck, která číslo přidělila, a 5,5 podle starší CVSS 3.1 v databázi GitHubu. Obě řadí útok mezi místní a obě počítají s tím, že oběť musí sama něco udělat – načíst model, který dostala od někoho cizího.
Hlášení zavřel časovač
Nálezce vystupující jako geo-chen píše, že chybu poslal 8. června do programu odměn, na který odkazuje bezpečnostní politika projektu, a odpovědi se nedočkal. Když se měsíc nic nedělo, otevřel 8. července v repozitáři dvě veřejná hlášení: číslo 47176 o čtení mimo složku a číslo 47177 o zápisu mimo složku při rozbalování archivu.
Odpověď přišla jen k tomu druhému. Vývojář projektu 9. července napsal, že převodní skripty se do vydávaných balíčků nedostávají, protože musí otevírat i nebezpečné formáty, a jsou určené pokročilým. Nálezce mu den nato odpověděl, že první hlášení se převodních skriptů netýká: jde o běžnou pomocnou funkci, která je v každém vydání a kterou zavolá obyčejné načtení modelu. Na to už nikdo nereagoval. Osmého srpna označil obě hlášení automat za odložená kvůli nečinnosti a 17. srpna je tentýž automat zavřel. Ještě týž den vyšel v databázi NVD záznam CVE.
Záznam, na který hlídač závislostí nereaguje
Ten záznam má v databázi GitHubu tři vlastnosti, které stojí za přečtení. Je označený jako nekontrolovaný (unreviewed), tedy převzatý automaticky z NVD. Nemá vyplněný ani jeden dotčený balíček. A podle vlastní dokumentace GitHubu platí, že „Dependabot pro nekontrolovaná hlášení upozornění nevytváří, protože se u tohohle druhu neověřuje platnost ani úplnost“ (přeloženo). Kdo se spoléhá na automatické hlášky o zranitelných závislostech, ten se o téhle chybě nedozví.
Rozsah dotčených verzí uvádí jen hlášení VulnCheck, a to slovy „transformers <= 5.15.0“. Vydání 5.15.1 je z 19. srpna, tedy dva dny po záznamu, takže do toho rozsahu nespadá. Stáhli jsme si soubor hub.py přímo z té značky vydání a popsaná funkce je v něm slovo od slova stejná jako předtím; totéž platí pro hlavní větev ke 20. srpnu. Od 8. července se toho souboru dotkl jediný commit, a byla to oprava vydávání starého souboru z cache na systémech jen pro čtení. Číslo verze, ve které je chyba spravená, tedy zatím žádné není.
Co s tím může udělat, kdo modely načítá
Dokud oprava nevyjde, zůstává jediné vodítko v tom, odkud složka s modelem přišla. Model stažený z Hugging Face podle jména jde druhou větví. U adresáře, který se objevil jinudy, se dá index otevřít obyčejným textovým editorem a podívat se, jestli jsou v weight_map jen holá jména souborů – ne cesty s lomítky, ne odkazy nahoru, ne absolutní cesty. Je to kontrola na pár vteřin a dá se automatizovat, ale vychází ze čtení kódu, ne z pokusu – sami jsme ji na podvrženém indexu nezkoušeli.
Zbytek je na projektu. Hlášení je veřejné od července, popis chyby včetně místa v kódu je v něm celý a oprava vypadá na pár řádků. Zavřel ho ale časovač, ne člověk, takže z pohledu repozitáře je věc vyřízená.
Zdroje: záznam CVE-2026-75104 v NVD, GHSA-fv5v-hfxp-5379, hlášení VulnCheck, hlášení 47176 a 47177 v repozitáři Transformers, zdrojový kód hub.py, historie vydání na PyPI.