L'agent Neo4j de Langroid exécute du Cypher généré sans contrôle — le jumeau du bug SQL
L'agent base de données graphe de Langroid transmet le Cypher généré par le modèle directement à Neo4j, sans validation. Une injection de prompt peut effacer le graphe ou, avec APOC activé, atteindre l'hôte — exactement le défaut déjà corrigé pour l'agent SQL, laissé béant dans le module Neo4j.
De quoi s’agit-il ?
Langroid est un framework Python open source pour construire des applications LLM multi-agents. L’un de ses agents utilitaires, Neo4jChatAgent, permet à un utilisateur d’interroger une base de données graphe en langage naturel : le modèle traduit la demande en Cypher — le langage de requête de Neo4j — l’exécute, puis explique le résultat. Le 6 juillet 2026, un avis de sécurité (GitLab GLAD, GitHub GHSA-2pq5-3q89-j7cc) a révélé que cet agent transmet le Cypher généré par le modèle directement au pilote Neo4j, sans validation, sans liste blanche de types d’instructions et sans possibilité de désactivation. La faille est cotée 9.8 (critique) et classée comme faiblesse d’injection (CWE-74).
Le détail qui justifie cette couverture n’est pas le bug en lui-même, mais sa filiation. Il s’agit de la même classe de défaut, et du même modèle de menace, que la faille prompt-vers-SQL de l’agent SQLChatAgent de Langroid, corrigée dès la version 0.63.0. Ce correctif ne s’est jamais étendu au module Neo4j : le même trou est donc resté ouvert dans un agent voisin pendant six semaines de plus. Toutes les versions antérieures à 0.65.5 sont concernées ; 0.65.5 apporte le correctif.
Comment ça marche
Le mécanisme est un pipeline texte-vers-requête auquel il manque une frontière de confiance. La question de l’utilisateur — ou, pire, le contenu que l’agent relit via de la recherche documentaire — oriente le Cypher émis par le modèle, et ce Cypher s’exécute tel quel.
# Schéma vulnérable (avant 0.65.5), simplifié :
cypher = llm.generate(user_question) # texte influençable par l'attaquant
session.run(cypher) # aucune barrière lecture seule, aucune liste blanche
Parce que le texte de la requête est influençable par injection de prompt — soit directe (un utilisateur hostile qui dialogue avec l’agent), soit indirecte (un document, une page web ou un nœud empoisonné que l’agent ingère via RAG) — un attaquant qui atteint le prompt contrôle la requête. Deux paliers d’escalade en découlent :
- Palier données. Cypher n’est pas en lecture seule. Des instructions injectées peuvent contraindre le modèle à émettre des écritures ou des suppressions : l’attaquant peut lire chaque nœud du graphe ou le détruire en bloc (un
DETACH DELETEsur tous les nœuds). Les charges utiles sont omises ici —[REDACTED]— mais la classe est simple : n’importe quelle instruction mutante que le pilote accepte. - Palier hôte. Neo4j embarque des bibliothèques de procédures optionnelles. Lorsque APOC ou les procédures
dbms.securitysont activées sur le serveur, Cypher peut atteindre le système d’exploitation et le système de fichiers. À ce stade, une injection prompt-vers-Cypher devient un accès commande et fichier — une exécution de code à distance conditionnée par la configuration du serveur, et non par une faille supplémentaire de Langroid.
Le parallèle avec la variante SQL est exact. Là, des primitives propres au dialecte comme COPY ... FROM PROGRAM transformaient une zone de chat en exécution de code sur l’hôte de la base ; ici, les procédures APOC font le même travail pour un magasin graphe. Langage de requête différent, forme identique.
Pourquoi c’est important
Les agents « texte-vers-requête » sont désormais une brique courante — SQL, Cypher, SPARQL, filtres MongoDB, DSL Elasticsearch. Chaque backend constitue sa propre surface d’injection, et chacun fait confiance à une chaîne écrite par un LLM sous l’influence d’entrées non fiables. Le cas Langroid illustre proprement deux points que les défenseurs doivent intégrer.
Premièrement, corriger un langage de requête ne corrige pas les autres. L’agent SQL a été durci en juin ; l’agent Neo4j, structurellement identique, est resté vulnérable parce que la remédiation a été écrite comme un correctif ponctuel plutôt que comme une politique appliquée à tous les agents émetteurs de requêtes. Si votre code possède une surface texte-vers-requête, il en possède très probablement plusieurs.
Deuxièmement, l’entrée initiale n’a pas besoin d’être un utilisateur malveillant. La voie indirecte — un nœud du graphe, un document récupéré ou une page scrappée portant des instructions que l’agent relit ensuite — signifie que la surface d’attaque inclut tout contenu que l’agent ingère. C’est le vecteur le plus difficile à raisonner et le plus facile à négliger.
Défenses
Le correctif amont et le motif général se renforcent mutuellement :
- Passez à Langroid 0.65.5 ou ultérieur, qui comble cette faille précise. Puis auditez chaque agent émetteur de requêtes de votre pile pour le même défaut, au lieu de supposer qu’un module corrigé vous met à l’abri.
- Traitez les requêtes générées par le LLM comme une sortie non fiable. Configurez par défaut la session de base de données de l’agent en lecture seule, et imposez une liste blanche de types d’instructions qui rejette écritures, changements de schéma et appels de procédures, sauf besoin explicite.
- Fonctionnez avec un compte de base de données à moindre privilège. Attribuez à l’agent un rôle Neo4j limité aux données dont il a besoin. Désactivez APOC et les procédures
dbms.securitysauf réelle nécessité ; si elles sont requises, restreignez-les avecdbms.security.procedures.allowlistau strict minimum. Cela supprime le palier hôte même si le palier données est atteint. - Passez les actions mutantes par une validation humaine. Une requête destructrice doit exiger une confirmation explicite, pas se déclencher automatiquement depuis un tour de conversation.
- Isolez le magasin graphe. Segmentez-le sur le réseau et assurez-vous que l’hôte de Neo4j n’expose ni identifiants ambiants ni surface de commande qu’une requête échappée pourrait détourner.
- Journalisez les requêtes générées. Enregistrer le Cypher exact émis par le modèle transforme un incident opaque en incident auditable et fait remonter tôt les tentatives d’injection.
Statut
| Élément | Référence | Date | Notes |
|---|---|---|---|
| Injection Cypher de l’agent Neo4j | GHSA-2pq5-3q89-j7cc / CVE-2026-55615 | 2026-07-06 | CVSS 9.8 ; concerne langroid < 0.65.5 ; corrigé en 0.65.5 |
| RCE prompt-vers-SQL de l’agent SQL (miroir) | CVE-2026-25879 | 2026-06-01 | Même classe ; corrigé en 0.63.0 ; le correctif ne couvrait pas Neo4j |
| Escalade palier hôte | Procédures APOC / dbms.security | — | RCE conditionnée par la config serveur, pas une faille distincte |
La bonne lecture n’est pas « un agent graphe a eu un bug ». C’est qu’une classe d’injection connue et déjà corrigée est réapparue non corrigée dans un agent voisin — le rappel que la remédiation texte-vers-requête est une politique à appliquer partout, pas un correctif à livrer une seule fois.