flawopen.com/Teardowns/cve-2026-71433-langgraph-store-namespace-boundary
LangGraph CVE-2026-71433: утечка namespace
Чтение с ограничением по namespace в Postgres и SQLite LangGraph могло пересекать границы и раскрывать данные соседнего tenant. Обновите оба пакета до 3.1.1.
Вектор CVSS
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:NПредставьте, что библиотекаря попросили найти папки, названия которых начинаются на «команда». Если каталог проверяет только начало строки, он может вернуть и «команда2». LangGraph соединял части namespace точками, но искал по префиксу без обязательной точки-разделителя. Исправление требует точного совпадения либо настоящего следующего сегмента, поэтому папки разных tenant больше не смешиваются.
Ключевые понятия и термины
Namespace- Кортеж меток, с помощью которого LangGraph Store группирует записи. Приложение может использовать его как границу между пользователями или tenant.
Область по префиксу- Запрос на чтение namespace и его дочерних областей. Корректное совпадение учитывает границы сегментов:
fooвключаетfoo.child, но не соседний namespacefoobar. Шаблон SQL `LIKE`- Текстовый шаблон, где
%означает любую последовательность, а_— один символ. Эти знаки нужно экранировать, если метки namespace должны трактоваться буквально. CWE-863- Неправильная авторизация: Store вернул записи за пределами namespace, который вызывающему разрешено читать.
Связи со стандартами
- Ссылка CWE для слабого места, описанного в этом руководстве: CWE-863
Анализ первопричины
До версии 3.1.1 Postgres- и SQLite-store контрольных точек соединяли кортежи namespace точками и ограничивали поиск выражением LIKE '<namespace>%'. LIKE не учитывает разделитель namespace, поэтому запрос для foo мог также совпасть с соседним namespace foobar; допустимые метки % и _ могли стать шаблонами. В list_namespaces были похожие ошибки для префиксов и суффиксов. Достаточно было обычного чтения с указанной областью; некорректный ввод не требовался. Проблема затрагивала только пути чтения search и перечисления namespace в Postgres/SQLite store, если приложение использовало namespace для разделения tenant. get, put и delete применяют точное равенство и не затронуты; InMemoryStore сравнивает элементы namespace и также не затронут. Исправление требует точного равенства либо точки перед дочерними сегментами, экранирует метасимволы LIKE и использует сегментные шаблоны при перечислении namespace. В SQLite поиск также избегает нечувствительного к ASCII-регистру поведения LIKE. В advisory нет сведений об эксплуатации в реальных атаках. Исправленные версии пакетов — 3.1.1.
Матрица достижимости CVE-2026-71433 для разных конфигураций
| Конфигурация и формат namespace | Документированное поведение | Действие при проверке |
|---|---|---|
| PostgresStore / AsyncPostgresStore или SqliteStore / AsyncSqliteStore версии ниже 3.1.1; изоляция арендаторов зависит от namespace. | Запрос search или list_namespaces с областью действия может затронуть соседний namespace, если метки имеют общий текстовый префикс (foo / foobar, 1 / 12) или содержат % / _. В SQLite поиск также приравнивал различия только в регистре ASCII. | Обновите соответствующий пакет checkpoint до версии 3.1.1 или новее. Проверьте соседние префиксы, подстановочные символы, суффиксы и различия регистра на рабочем backend. |
| SQLite Store версии ниже 3.1.1 с метками namespace, отличающимися только регистром ASCII. | Уязвимый поиск потомков использовал LIKE, нечувствительный к регистру, тогда как точные операции учитывали регистр. | Обновите пакет и добавьте регрессионную проверку регистра, если метки могут так различаться. |
UUID-метки фиксированной длины без % и _. | В advisory указано, что такая схема не затронута: метки фиксированной длины не могут быть префиксами друг друга. | Убедитесь, что каждый сегмент соблюдает это условие. Если метки могут иметь разную длину или содержать подстановочные символы, считайте конфигурацию затронутой. |
| InMemoryStore. | Не затронут: элементы namespace сравниваются по отдельности. | Для этой CVE обновление не требуется; сохраняйте проверки принадлежности арендатора на уровне приложения. |
| Размещённая среда LangSmith. | По умолчанию LANGGRAPH_STORE_BACKEND=python использует AsyncPostgresStore и затронут при уязвимой версии пакета. Отдельный backend grpc получил эквивалентное исправление. | Проверьте настроенный backend и установленную версию; подтвердите версию или обновление размещённой среды, не делая вывод по одному названию продукта. |
Пошаговый сценарий атаки
Проверьте backend и метки
Развертывание должно использовать langgraph-checkpoint-postgres или langgraph-checkpoint-sqlite версии ниже 3.1.1 и полагаться на namespace для разделения пользователей или tenant. Общие префиксы, а также метки % и _ делают границу уязвимой.
Выполняется обычное чтение с областью
Аутентифицированный запрос приложения ищет namespace, например foo. Специальная полезная нагрузка не нужна: обычный namespace поступает в ошибочный механизм сравнения.
Backend объединяет части и применяет шаблон
Store соединяет метки точками и использует префикс LIKE, оканчивающийся на %. Без обязательного разделителя foo может совпасть с foobar; метасимволы в метках тоже расширяют совпадение.
Могут вернуться записи соседнего namespace
Если приложение считает namespace границей tenant, поиск или перечисление может вернуть сохранённые записи другого пользователя. Это утечка конфиденциальности при чтении, а не SQL injection и не доступ на запись.
Обновите пакеты и повторите тесты изоляции
Обновите оба применимых пакета до 3.1.1 или новее. Проверьте соседние префиксы, буквальные метки % и _, суффиксы и чувствительность SQLite к регистру на отдельных тестовых tenant.
Исходный код: Уязвимый vs Защищённый вариант
Уязвимое условие считает дочерней любую строку с началом, совпадающим со сведённым namespace. Исправление добавляет точное равенство и требует буквальную точку перед дочерними сегментами; в шаблоне LIKE отдельно экранируются обратная косая черта, % и _. Upstream-коммит также заменяет шаблоны префикса/суффикса list_namespaces на сегментное сравнение и корректирует поиск SQLite. Это фрагмент исходного кода, а не замена модуля.
Фрагмент исходного кода: libs/checkpoint-postgres/langgraph/store/postgres/base.py
def _prepare_batch_search_queries(op):
ns_condition = "TRUE"
ns_param = None
if op.namespace_prefix:
ns_condition = "store.prefix LIKE %s"
ns_param = (f"{_namespace_to_text(op.namespace_prefix)}%",)
return ns_condition, ns_param
def _escape_like_literal(text: str) -> str:
return text.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
def _namespace_prefix_condition(namespace_prefix: tuple[str, ...]) -> tuple[str, tuple]:
path = _namespace_to_text(namespace_prefix)
condition = r"(store.prefix = %s OR store.prefix LIKE %s ESCAPE '\\')"
return condition, (path, f"{_escape_like_literal(path)}.%")
if op.namespace_prefix:
ns_condition, ns_param = _namespace_prefix_condition(op.namespace_prefix)
Чек-лист по защите системы для инженеров
- ✓Обновите
langgraph-checkpoint-postgresиlanggraph-checkpoint-sqliteдо версии 3.1.1 или новее во всех установках. - ✓Добавьте регрессионные проверки, где метка одного tenant является префиксом другой, например
fooиfoobar; каждый поиск должен возвращать только запрошенный namespace и его дочерние области. - ✓Проверьте метки с
%,_, обратной косой чертой и различием только регистра на том же backend БД, что используется в production. - ✓Проверьте
list_namespacesс префиксом, суффиксом, wildcard и пустым путём;*должен соответствовать ровно одному сегменту, как указано в документации. - ✓Оставьте проверки авторизации на границе приложения; непрозрачные или фиксированного формата ID namespace не заменяют проверку владельца tenant.