FlawOpen

flawopen.com/Teardowns/cve-2026-71433-langgraph-store-namespace-boundary

● CVE-2026-71433 · CVSS 5.3 · Средний
Исследования · FlawOpen

LangGraph CVE-2026-71433: утечка namespace

Чтение с ограничением по namespace в Postgres и SQLite LangGraph могло пересекать границы и раскрывать данные соседнего tenant. Обновите оба пакета до 3.1.1.

Вектор CVSSCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N
💡 Простыми словами (ELI5)

Представьте, что библиотекаря попросили найти папки, названия которых начинаются на «команда». Если каталог проверяет только начало строки, он может вернуть и «команда2». LangGraph соединял части namespace точками, но искал по префиксу без обязательной точки-разделителя. Исправление требует точного совпадения либо настоящего следующего сегмента, поэтому папки разных tenant больше не смешиваются.

Ключевые понятия и термины

Namespace
Кортеж меток, с помощью которого LangGraph Store группирует записи. Приложение может использовать его как границу между пользователями или tenant.
Область по префиксу
Запрос на чтение namespace и его дочерних областей. Корректное совпадение учитывает границы сегментов: foo включает foo.child, но не соседний namespace foobar.
Шаблон 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 и установленную версию; подтвердите версию или обновление размещённой среды, не делая вывод по одному названию продукта.

Пошаговый сценарий атаки

Шаг 1

Проверьте backend и метки

Развертывание должно использовать langgraph-checkpoint-postgres или langgraph-checkpoint-sqlite версии ниже 3.1.1 и полагаться на namespace для разделения пользователей или tenant. Общие префиксы, а также метки % и _ делают границу уязвимой.

Шаг 2

Выполняется обычное чтение с областью

Аутентифицированный запрос приложения ищет namespace, например foo. Специальная полезная нагрузка не нужна: обычный namespace поступает в ошибочный механизм сравнения.

Шаг 3

Backend объединяет части и применяет шаблон

Store соединяет метки точками и использует префикс LIKE, оканчивающийся на %. Без обязательного разделителя foo может совпасть с foobar; метасимволы в метках тоже расширяют совпадение.

Шаг 4

Могут вернуться записи соседнего namespace

Если приложение считает namespace границей tenant, поиск или перечисление может вернуть сохранённые записи другого пользователя. Это утечка конфиденциальности при чтении, а не SQL injection и не доступ на запись.

Шаг 5

Обновите пакеты и повторите тесты изоляции

Обновите оба применимых пакета до 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)

Чек-лист по защите системы для инженеров

Источники