FlawOpen

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

● CVE-2026-71433 · CVSS 5.3 · Media
Investigación · FlawOpen

LangGraph CVE-2026-71433: fuga de namespaces

Las lecturas con ámbito en Postgres y SQLite de LangGraph podían cruzar límites de namespace y exponer datos de tenants vecinos. Actualiza ambos paquetes a 3.1.1.

Vector CVSSCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N
💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina que pides al bibliotecario una carpeta cuyo nombre empieza por «equipo». Si el catálogo busca solo el texto inicial, podría devolver «equipo2» junto con «equipo». LangGraph unía las etiquetas del namespace con puntos y usaba un patrón de prefijo que no exigía ese punto como límite. La corrección exige el namespace exacto o un segmento siguiente real, para que la carpeta de un tenant no se confunda con la de otro.

Conceptos Clave y Términos

Namespace
Una tupla de etiquetas que LangGraph Store usa para agrupar elementos. Una aplicación puede usarla como límite entre usuarios o tenants.
Ámbito por prefijo
Una solicitud para leer un namespace y sus descendientes. La coincidencia correcta respeta el límite del segmento: foo incluye foo.child, pero no el namespace hermano foobar.
Patrón SQL `LIKE`
Un patrón de texto donde % coincide con cualquier secuencia y _ con un carácter. Hay que escapar estos caracteres si las etiquetas del namespace deben tratarse literalmente.
CWE-863
Autorización incorrecta: el Store devolvió registros fuera del namespace que el llamante tenía permiso para leer.

Mapeos de marcos

  • Referencia CWE para la debilidad descrita en esta guía: CWE-863

Análisis de Causa Raíz

Antes de 3.1.1, los Stores de checkpoint para Postgres y SQLite aplanaban las tuplas de namespace con puntos y limitaban las búsquedas con LIKE '<namespace>%'. LIKE no entiende el separador del namespace, así que una consulta para foo también podía coincidir con el namespace hermano foobar; las etiquetas válidas % y _ podían actuar como comodines. list_namespaces tenía errores relacionados de prefijo y sufijo. Bastaba una lectura normal con ámbito: no se necesitaba una entrada malformada. El problema se limita a las rutas de lectura search y listado de namespaces de los Stores Postgres/SQLite cuando la aplicación depende del namespace para separar tenants. get, put y delete usan igualdad exacta y no estaban afectados; InMemoryStore compara los elementos del namespace y tampoco estaba afectado. La corrección exige igualdad exacta o un punto antes de los descendientes, escapa metacaracteres de LIKE y usa patrones por segmento al listar namespaces. En SQLite también evita el comportamiento de LIKE que ignora diferencias ASCII entre mayúsculas y minúsculas. El aviso no informa de explotación conocida. Los paquetes afectados se corrigieron en 3.1.1.

Matriz de exposición de CVE-2026-71433 por despliegue

Despliegue y forma del namespaceComportamiento documentadoAcción de triaje
PostgresStore / AsyncPostgresStore o SqliteStore / AsyncSqliteStore anterior a 3.1.1; el aislamiento entre tenants depende de namespaces.Una búsqueda search o list_namespaces con ámbito puede alcanzar un namespace hermano si las etiquetas comparten un prefijo textual (foo / foobar, 1 / 12) o contienen % / _. En SQLite, la búsqueda también igualaba diferencias solo de mayúsculas ASCII.Actualiza el paquete checkpoint correspondiente a 3.1.1 o posterior. Prueba prefijos hermanos, caracteres comodín, coincidencias de sufijo y diferencias de mayúsculas/minúsculas en el backend de producción.
Store SQLite anterior a 3.1.1 con etiquetas de namespace que solo difieren en mayúsculas ASCII.La búsqueda vulnerable de descendientes usaba LIKE sin distinguir mayúsculas, mientras que las operaciones exactas sí las distinguían.Actualiza y añade una prueba de regresión de mayúsculas si las etiquetas pueden diferir así.
Etiquetas UUID de longitud fija sin % ni _.El aviso indica que este esquema no está afectado: las etiquetas de longitud fija no pueden ser prefijos entre sí.Confirma que todos los segmentos cumplen esa invariante. Si las etiquetas pueden variar o contener comodines, considera el despliegue afectado.
InMemoryStore.No está afectado: compara individualmente los elementos del namespace.No hace falta actualizar por este CVE; conserva las comprobaciones de propiedad del tenant en el límite de la aplicación.
Despliegue hospedado de LangSmith.El valor predeterminado LANGGRAPH_STORE_BACKEND=python usa AsyncPostgresStore y entra en el alcance con un paquete afectado. El backend independiente grpc recibió una corrección equivalente.Comprueba el backend configurado y la versión desplegada; confirma la versión o la actualización hospedada en vez de deducir la exposición por el nombre del producto.

Flujo de Ataque Paso a Paso

Paso 1

Comprueba el backend y las etiquetas

La instalación debe usar langgraph-checkpoint-postgres o langgraph-checkpoint-sqlite anterior a 3.1.1 y depender del namespace para separar usuarios o tenants. Los prefijos coincidentes o las etiquetas % y _ hacen relevante este límite.

Paso 2

Se realiza una lectura normal con ámbito

Una solicitud autenticada de la aplicación busca un namespace como foo. No hace falta enviar un payload especial: el namespace normal es la entrada del comparador defectuoso.

Paso 3

El backend aplana y compara patrones

El Store une las etiquetas con puntos y usa un prefijo LIKE terminado en %. Como no exige el separador, foo puede coincidir con foobar; los caracteres de patrón en las etiquetas también pueden ampliar la coincidencia.

Paso 4

Pueden devolverse elementos de un namespace vecino

Si la aplicación trata los namespaces como límites entre tenants, la búsqueda o el listado puede devolver elementos guardados de otro usuario. Es un problema de confidencialidad de lectura, no SQL injection ni acceso de escritura.

Paso 5

Actualiza y vuelve a probar el aislamiento

Actualiza ambos paquetes aplicables a 3.1.1 o posterior. Prueba prefijos de namespaces vecinos, etiquetas literales % y _, coincidencias de sufijo y el comportamiento de mayúsculas/minúsculas de SQLite con tenants de prueba separados.

Código Fuente: Vulnerable vs. Seguro

El predicado vulnerable considera descendiente cualquier cadena que comienza con el namespace aplanado. La corrección añade una comparación exacta y exige un punto literal antes de los segmentos descendientes; también escapa la barra invertida, % y _ en el patrón LIKE. El commit upstream también sustituye los patrones de prefijo/sufijo de list_namespaces por coincidencias conscientes de segmentos y ajusta la búsqueda SQLite. Es un fragmento de código, no un módulo completo.

Fragmento de origen: libs/checkpoint-postgres/langgraph/store/postgres/base.py

IMPLEMENTACIÓN VULNERABLE
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
PARCHE SEGURO Y ROBUSTO
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)

Lista de Verificación de Seguridad para Ingeniería

Fuentes