Úložiště LangGraphu vracelo i záznamy z prostoru, jehož jméno začínalo stejně
Knihovna LangGraph ukládá paměť agentů do jmenných prostorů a aplikace jimi běžně oddělují data jednoho uživatele od druhého. Úložiště pro Postgres a SQLite je ale porovnávala operátorem LIKE, který nezná tečkový oddělovač, takže dotaz omezený na jeden prostor vracel i záznamy ze sousedního. Oprava vyšla 30. července 2026, popis chyby o týden později.

LangGraph je knihovna, ve které se staví agenti nad jazykovými modely: popíše se graf kroků a knihovna se stará o jejich pořadí a o paměť mezi nimi. Tu paměť drží takzvaný store, tedy úložiště, a pro ostrý provoz k němu vývojáři berou balíčky langgraph-checkpoint-postgres nebo langgraph-checkpoint-sqlite. Právě v nich byla chyba, kterou projekt popsal 6. srpna 2026 pod označením CVE-2026-71433.
Záznamy se v úložišti třídí do jmenných prostorů, což je uspořádaná dvojice, trojice či delší řada popisků: ("memories", "alice"). Do databáze se ale ukládá jako jediný řetězec spojený tečkami, tedy memories.alice, a čtení omezené na jeden prostor se dělalo porovnáním LIKE se začátkem toho řetězce. Operátor LIKE ale o tečce nic neví. Dotaz omezený na ("foo",) proto vracel i záznamy z ("foobar",) a z ("foo2",).
Kdy se z toho stane únik dat
Samo o sobě je to chyba v dotazu. Váhu jí dává to, k čemu se jmenné prostory běžně používají: aplikace jimi oddělují data jednoho uživatele nebo zákazníka od druhého. Kde je hranicí jmenný prostor, tam mohl obyčejný dotaz vrátit cizí záznamy. Nic vyrobeného na míru k tomu podle hlášení potřeba nebylo, stačil běžný požadavek.
Hlášení popisuje tři podoby téhož omylu:
- Sousední prostory. Čtení omezené na
("foo",)vrátilo i položky z("foobar",)a("foo2",). - Neošetřené zástupné znaky. Znaky
_a%jsou v popisku prostoru povolené (zakázaná je jen tečka), jenže do porovnávacího vzoru se dosazovaly tak, jak jsou. V jazyce SQL přitom zastupují jeden libovolný znak a libovolný počet znaků, takže("user_1",)sedělo i na("userX1",). - Shoda na konci. Dotaz
list_namespaces(suffix=("alice",))sedl i na sousedníusers.malice.
Vkládání cizího SQL to není a projekt na tom trvá: hodnoty se předávaly jako vázané parametry a do textu dotazu se nikdy nevkládaly. Vzorem pro LIKE byla sama ta hodnota, jen s neošetřenými zástupnými znaky. Proti třem starším dírám v témže úložišti je to podstatný rozdíl: CVE-2025-67644, CVE-2025-64104 i CVE-2025-8709 z podzimu a zimy 2025 byly vkládání SQL přes klíč filtru a všechny tři měly závažnost vysokou.
Koho se to netýká
Zasažená je aplikace, která splní tři podmínky najednou: používá PostgresStore nebo SqliteStore (i v asynchronní podobě), spoléhá na jmenný prostor jako na hranici mezi uživateli a má popisky, kde jeden může být začátkem druhého (1 a 12, alice a alice2) nebo obsahuje _ či %. Kdo prostory pojmenovává identifikátory pevné délky, typicky UUID, zasažený není: žádný takový popisek nemůže být začátkem jiného. InMemoryStore porovnává prostory po prvcích a chybu nikdy neměl.
Nedotčené zůstaly i operace get, put a delete, protože ty prostory porovnávají přes rovnítko. Vada byla jen na straně čtení a odpovídá tomu i hodnocení závažnosti: 5,3 bodu z deseti, důvěrnost vysoká, celistvost i dostupnost nulové. To číslo pochází od GitHubu a americká databáze NVD ho přebírá jako druhotné, vlastní hodnocení k němu nepřipojila.
Oprava mění chování hvězdičky
Ve verzi 3.1.1 obou balíčků musí prostor sedět buď přesně, nebo za shodným začátkem musí následovat tečka. Zástupné znaky v popiscích se ošetřují a list_namespaces porovnává po segmentech. V Postgresu k tomu slouží regulární výraz místo LIKE, v SQLite se sestupné hledání přesunulo na GLOB.
Ten přesun srovnal ještě jednu nesrovnalost. LIKE v SQLite nerozlišuje u znaků ASCII velikost písmen, takže hledání dosud našlo i prostory lišící se jen velkým a malým písmenem, zatímco zápisové operace je považovaly za dva různé. Teď se shodnou.
Povýšení ale není zadarmo. Hvězdička v cestě u list_namespaces nově zabírá přesně jeden segment, ne libovolný počet. Projekt to označuje za návrat k dokumentovanému chování, protože ("cache", "*", "v1") je podle dokumentace libovolná kategorie mezipaměti s verzí v1; víceúrovňová shoda byla vedlejším produktem téhož mechanismu, který chybu způsobil, a zachovat ji prý při opravě nešlo. Kdo potřebuje shodu v libovolné hloubce, má podle projektu zkombinovat obě podmínky, prefix i suffix; platí zároveň.
Co je vidět z čísel opravy
Změna prošla jako návrh 8478, sloučený 30. července 2026, a dotkla se šesti souborů. Přidala 554 řádků a ubrala 36. Z přidaných řádků je 379 v testech a 175 ve vlastním kódu; obojí je náš součet z čísel, která u jednotlivých souborů uvádí rozhraní GitHubu. Na jeden řádek opravy tak vyšly zhruba dva řádky testu.
Opravné verze obou balíčků vyšly 30. července 2026, popis chyby o týden později. Tentýž odstup mezi opravnou verzí a zveřejněným hlášením jsme popisovali u Open WebUI, kde šlo o osm dní.
Co s tím
Kdo LangGraph provozuje nad Postgresem nebo SQLite, má povýšit na 3.1.1 a projít si, jestli jeho popisky jmenných prostorů můžou být začátkem jiných. Projekt sám radí dvě věci: dávat prostorům identifikátory pevné délky, aby žádný nemohl být začátkem jiného, a popisky pocházející od uživatele ověřovat na vstupu, místo aby se spoléhalo jen na omezení dotazu.
Zvláštní odstavec věnuje hlášení hostovaným nasazením. Na rozdíl od dřívějších hlášení o tomhle úložišti se tohle týká i jich: nasazení LangSmith mají ve výchozím stavu nastavené LANGGRAPH_STORE_BACKEND=python, což je právě AsyncPostgresStore z opravovaného balíčku. Varianta grpc je jiná implementace a dostala odpovídající opravu zvlášť. Že by chybu někdo zneužil, projekt nezaznamenal a píše to v hlášení výslovně.
Zdroje: hlášení GHSA-47pj-3jcm-6whg projektu LangGraph, záznam CVE-2026-71433 v databázi NVD amerického úřadu NIST, návrh 8478 a poznámky k vydání obou opravených balíčků.