flawopen.com/Teardowns/cve-2026-71433-langgraph-store-namespace-boundary
LangGraph CVE-2026-71433 : fuite de namespace
Les lectures ciblées des stores Postgres et SQLite de LangGraph pouvaient franchir les limites de namespace et exposer les données d’un tenant voisin. Mettez les deux paquets à niveau 3.1.1.
Vecteur CVSS
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:NImaginez qu’un bibliothécaire cherche des dossiers dont le nom commence par « équipe ». Si le catalogue compare seulement le début du texte, il peut renvoyer « équipe2 » avec « équipe ». LangGraph concaténait les étiquettes du namespace avec des points, puis utilisait un motif de préfixe sans exiger ce point comme frontière. Le correctif exige le namespace exact ou un véritable segment suivant, afin que le dossier d’un tenant ne soit plus confondu avec celui d’un autre.
Concepts Clés et Termes
Namespace- Un tuple d’étiquettes utilisé par le Store LangGraph pour regrouper des éléments. Une application peut s’en servir comme frontière entre utilisateurs ou tenants.
Filtrage par préfixe- Une demande de lecture d’un namespace et de ses descendants. La correspondance correcte respecte la frontière du segment :
fooinclutfoo.child, mais pas le namespace voisinfoobar. Motif SQL `LIKE`- Un motif textuel où
%correspond à une suite quelconque et_à un caractère. Il faut échapper ces caractères lorsque les étiquettes du namespace doivent rester littérales. CWE-863- Autorisation incorrecte : le Store a renvoyé des enregistrements hors du namespace que l’appelant était autorisé à lire.
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-863
Analyse de Cause Racine
Avant la version 3.1.1, les stores de checkpoint Postgres et SQLite aplatissaient les tuples de namespace avec des points et limitaient les recherches avec LIKE '<namespace>%'. LIKE ne comprend pas le séparateur du namespace : une recherche sur foo pouvait donc aussi correspondre au namespace voisin foobar. Les étiquettes valides % et _ pouvaient également servir de jokers. list_namespaces comportait des erreurs proches pour les préfixes et suffixes. Une lecture ciblée ordinaire suffisait ; aucune entrée malformée n’était nécessaire. Le problème concerne uniquement les chemins de lecture search et de liste des namespaces des stores Postgres/SQLite lorsque l’application s’appuie sur les namespaces pour séparer les tenants. get, put et delete utilisent l’égalité exacte et n’étaient pas concernés ; InMemoryStore compare les éléments du namespace et n’était pas concerné non plus. Le correctif exige une égalité exacte ou un point avant les descendants, échappe les métacaractères de LIKE et utilise des motifs par segment pour la liste des namespaces. Sous SQLite, la recherche évite aussi le comportement de LIKE qui ignore la casse ASCII. L’avis ne signale aucune exploitation connue. Les paquets concernés sont corrigés en 3.1.1.
Matrice d’exposition au CVE-2026-71433 selon le déploiement
| Déploiement et forme des namespaces | Comportement documenté | Action de triage |
|---|---|---|
| PostgresStore / AsyncPostgresStore ou SqliteStore / AsyncSqliteStore avant la version 3.1.1 ; l’isolation des tenants repose sur les namespaces. | Une recherche search ou list_namespaces limitée peut atteindre un namespace frère si les libellés partagent un préfixe textuel (foo / foobar, 1 / 12) ou contiennent % / _. Sous SQLite, la recherche assimilait aussi les différences de casse ASCII. | Mettez à niveau le paquet checkpoint concerné vers la version 3.1.1 ou ultérieure. Testez les préfixes frères, les caractères génériques, les suffixes et les différences de casse sur le moteur de production. |
| Store SQLite antérieur à 3.1.1 avec des libellés de namespace qui ne diffèrent que par la casse ASCII. | La recherche vulnérable des descendants utilisait LIKE, insensible à la casse, alors que les opérations exactes distinguaient la casse. | Mettez à niveau et ajoutez un test de non-régression sur la casse si les libellés peuvent varier ainsi. |
Libellés UUID de longueur fixe sans % ni _. | L’avis indique que ce format n’est pas concerné : des libellés de longueur fixe ne peuvent pas être préfixes les uns des autres. | Vérifiez que chaque segment respecte cette invariance. Si les libellés peuvent varier ou contenir des caractères génériques, considérez le déploiement comme concerné. |
| InMemoryStore. | Non concerné : les éléments du namespace sont comparés individuellement. | Aucune mise à niveau requise pour ce CVE ; conservez les contrôles de propriété des tenants à la frontière applicative. |
| Déploiement hébergé LangSmith. | La valeur par défaut LANGGRAPH_STORE_BACKEND=python utilise AsyncPostgresStore et est concernée avec un paquet vulnérable. Le backend distinct grpc a reçu une correction équivalente. | Vérifiez le backend configuré et la version déployée ; confirmez la version ou le déploiement du correctif au lieu de déduire l’exposition du seul nom du produit. |
Déroulement de l'Attaque Étape par Étape
Vérifiez le backend et les étiquettes
Le déploiement doit utiliser langgraph-checkpoint-postgres ou langgraph-checkpoint-sqlite avant 3.1.1 et s’appuyer sur le namespace pour séparer utilisateurs ou tenants. Des étiquettes partageant un préfixe ou contenant % et _ rendent cette frontière pertinente.
Une lecture ciblée ordinaire est lancée
Une requête authentifiée de l’application recherche un namespace tel que foo. L’appelant n’a pas besoin d’envoyer de payload spécial : le namespace ordinaire est l’entrée du comparateur défectueux.
Le backend aplatit et compare un motif
Le Store joint les étiquettes avec des points et utilise un préfixe LIKE terminé par %. Comme aucun séparateur n’est exigé, foo peut correspondre à foobar ; les caractères de motif des étiquettes peuvent aussi élargir la correspondance.
Des éléments d’un namespace voisin peuvent être renvoyés
Lorsque l’application utilise les namespaces comme frontières entre tenants, la recherche ou la liste peut renvoyer les éléments stockés d’un autre utilisateur. Il s’agit d’un problème de confidentialité en lecture, pas d’une injection SQL ni d’un accès en écriture.
Mettez à niveau et retestez l’isolation
Mettez les deux paquets concernés à niveau vers 3.1.1 ou une version ultérieure. Testez les préfixes voisins, les étiquettes littérales % et _, les suffixes et la sensibilité à la casse de SQLite avec des tenants de test séparés.
Code Source : Vulnérable vs Sécurisé
Le prédicat vulnérable considère toute chaîne commençant par le namespace aplati comme un descendant. Le correctif ajoute une comparaison exacte et exige un point littéral avant les segments descendants ; il échappe aussi la barre oblique inverse, % et _ dans le motif LIKE. Le commit upstream remplace également les motifs de préfixe/suffixe de list_namespaces par une correspondance par segments et ajuste la recherche SQLite. Il s’agit d’un extrait source, pas d’un module de remplacement.
Extrait source: 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)
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Mettez
langgraph-checkpoint-postgresetlanggraph-checkpoint-sqliteà niveau vers 3.1.1 ou une version ultérieure partout où ils sont installés. - ✓Ajoutez des régressions où l’étiquette d’un tenant est le préfixe d’une autre, comme
fooetfoobar, et vérifiez que chaque recherche ne renvoie que le namespace demandé et ses descendants. - ✓Testez les étiquettes contenant
%,_, des barres obliques inverses et des différences de casse uniquement avec le backend exact de production. - ✓Testez
list_namespacesavec préfixe, suffixe, joker et chemin vide ; exigez que*corresponde à un seul segment, comme documenté. - ✓Conservez les contrôles d’autorisation à la frontière de l’application ; des identifiants de namespace opaques ou à format fixe ne remplacent pas la vérification du propriétaire du tenant.