Do jména balíčku šlo ve stata-mcp schovat příkaz, který Stata rovnou spustila
Server stata-mcp pouští statistický program Stata z agentních nástrojů. Do verze 1.19.0 skládal jméno instalovaného balíčku rovnou do příkazu pro Statu, takže se do něj dal přidat druhý příkaz, a Stata umí spouštět i programy operačního systému. Vlastní seznam nebezpečných příkazů projekt měl, jenže se kontroloval jinde.
Server stata-mcp zpřístupňuje statistický program Stata agentním nástrojům. Model přes protokol MCP dostane sadu nástrojů, kterými spouští do-soubory, čte logy a doinstalovává balíčky. Jeden z nich, ado_package_install, skládal jméno balíčku rovnou do příkazu, který se posílá Statě. Hlášení o tom vyšlo 12. srpna 2026 pod označením GHSA-49m4-vp58-wgc9 a číslem CVE-2026-55071.

Konec řádku uprostřed jména balíčku
Ve verzi 1.18.2 vypadala obsluha instalace takhle:
def install(self, package: str) -> str:
install_command = f"ssc install {package}{self.REPLACE_MESSAGE}"
runner_result = self.controller.run(install_command)Hodnota package přišla od volajícího a nikdo ji nekontroloval. Výsledný řetězec putuje do Staty přes knihovnu pexpect, tedy tak, jako by ho někdo psal do jejího příkazového řádku. Použitá metoda sendline() pošle celý řetězec včetně případných konců řádku a Stata pak každý řádek vykoná zvlášť. Kdo tedy do jména balíčku dostal konec řádku, dostal tam i druhý příkaz.
Tím by to skončilo u statistiky, kdyby Stata neuměla i příkazy operačního systému. Umí je: shell, winexec a unixcmd spustí program mimo ni, s právy účtu, pod kterým server běží. Hlášení proto vede chybu jako vkládání kódu (CWE-94) a dává jí podle CVSS 3.1 známku 8,4 bodu z deseti, tedy vysokou závažnost. Ta známka počítá s místním přístupem (vektor AV:L), ne s útokem po síti. Volat ten nástroj ale může každý, kdo se k serveru dostane, včetně samotného modelu na druhém konci spojení, a pokud někdo vystaví přenos přes HTTP, dostane se k němu i klient odjinud.
Seznam zakázaných příkazů projekt měl
Zajímavější než chyba sama je, že se jí projekt bránit uměl. Soubor guard/blacklist.py vede výčet nebezpečných příkazů Staty a jsou v něm všechny tři jmenované, k tomu xshell, erase, rmdir, bloky python a mata i zkratky, které Stata uznává místo plných názvů. Kontrola nad tím výčtem se ale volala na jediném místě: před spuštěním do-souboru. Instalace balíčku svoje volání neměla, takže na ni seznam nedosáhl.
Nešlo o ochranu, kterou by si uživatel musel zapnout. Volba IS_GUARD má v nastavení výchozí hodnotu zapnuto, takže seznam běžel a jen se ho na téhle cestě nikdo nezeptal. Druhá věc, která to zhoršovala: nástroj byl zapsaný v profilu all a právě ten se použije, když se u příkazu neuvede --core ani --all. Chybná konfigurace tedy potřeba nebyla, stačilo server spustit.
Oprava zúžila jméno balíčku na písmena a číslice
Verze 1.19.0 z 9. června 2026 nespravila jen ten jeden řádek. Mění čtyři věci naráz:
- nástroj se přesunul do profilu
unsafe, takže se bez přepínače--unsafevůbec nezaregistruje; - jméno balíčku ze zdrojů ssc a net musí projít výrazem
^[A-Za-z0-9]+$, tedy jen písmena a číslice, žádná mezera ani konec řádku; - volání vyžaduje výslovné potvrzení a na vrstvě MCP se server zeptá uživatele sám;
- repozitář na GitHubu musí být na seznamu povolených, jinak instalace skončí chybou.
V popisu toho nástroje k tomu autor napsal, že obsah repozitářů z GitHubu nekontroluje nic a podívat se do nich musí uživatel. Je to poctivá věta, ale taky připomínka, že proti balíčku, který si člověk sám schválí, žádný výraz nepomůže.
Devět vydání ze sta čtyř je v pořádku
Zásah je široký. Evidence OSV vypisuje jako dotčených 95 vydání, tedy všechna od prvního po 1.18.2. Balíček stata-mcp má na PyPI 104 vydání se souborem ke stažení, takže nezasažených je devět: 1.19.0 až po dnešní 1.21.4 z 5. srpna 2026. Rozdíl obou čísel je náš odečet, obě vstupní čísla jsou ve zdrojích odkázaných výš.
Mezi opravou a hlášením uplynuly dva měsíce a pár dní. Poznámky k vydání 1.19.0 zamlčovací nejsou: přímo píšou, že nová kontrola jména balíčku má zabránit vkládání příkazů. Chybělo jim jediné, číslo. A podle čísla se řídí nástroje, které hlídají závislosti za člověka, takže projekt, který si stata-mcp přibalil a poznámky nečetl, se o chybě dozvěděl teprve 12. srpna.
Kontrola patří tam, kde se řetězec skládá
Rozestup mezi tím, co server slibuje, a tím, co doopravdy vynutí, je u serverů MCP téma, na které jsme narazili u serveru Dynatrace: i tam část ochran existovala jako popis, ne jako podmínka v kódu. Tady je poučení konkrétnější. Ochrana byla podmínkou v kódu, jen stála na jedné ze dvou cest, kudy vede řetězec do interpretu.
Server MCP má tu vlastnost, že mu nástrojů přibývá. Každý nový je další cesta k témuž interpretu, a kdo kontrolu pověsí na cestu místo na místo, kde se příkaz skládá, ji musí pověsit znovu pokaždé. Oprava v 1.19.0 to řeší i takhle: validace se volá uvnitř install(), tedy o patro níž, než kde se dřív kontrolovalo.
Zdroje
- GHSA-49m4-vp58-wgc9: MCP-for-Stata: Stata Command Injection via Unsanitized
packageinado_package_install, GitHub Advisory Database, 12. srpna 2026 - Týž záznam v databázi OSV se seznamem dotčených vydání
- Poznámky k vydání stata-mcp 1.19.0, 9. června 2026
- Repozitář SepineTam/mcp-for-stata, zdrojové kódy značek v1.18.2 a v1.19.0
- Balíček stata-mcp na PyPI