Langroid's Neo4j agent runs LLM-written Cypher unchecked — the SQL bug's twin
Langroid's graph-database agent hands model-generated Cypher straight to Neo4j with no validation. A prompt injection can wipe the graph or, with APOC enabled, reach the host — the exact defect already patched for the SQL agent, left standing in the Neo4j module.
What is this?
Langroid is an open-source Python framework for building multi-agent LLM applications. One of its helper agents, Neo4jChatAgent, lets a user ask a graph database questions in plain language: the model translates the request into Cypher — Neo4j’s query language — and runs it, then explains the result. On July 6, 2026, a security advisory (GitLab GLAD, GitHub GHSA-2pq5-3q89-j7cc) disclosed that this agent passes the model-generated Cypher straight to the Neo4j driver with no validation, no allowlist of statement types, and no opt-out gate. It is rated 9.8 (critical) and classed as an injection weakness (CWE-74).
The detail that makes this worth covering is not the bug on its own but its lineage. It is the same defect class, and the same threat model, as the prompt-to-SQL issue in Langroid’s SQLChatAgent that was fixed back in version 0.63.0. That fix never extended to the Neo4j module, so the identical hole sat open in a sibling agent for another six weeks. All versions before 0.65.5 are affected; 0.65.5 is the fix.
How it works
The mechanism is a text-to-query pipeline with a missing trust boundary. The user’s question — or, worse, content the agent reads back through retrieval — steers what Cypher the model emits, and that Cypher executes verbatim.
# Vulnerable pattern (pre-0.65.5), simplified:
cypher = llm.generate(user_question) # attacker-influenceable text
session.run(cypher) # no read-only gate, no statement allowlist
Because the query text is influenceable by prompt injection — either direct (a hostile user typing to the agent) or indirect (a poisoned document, web page, or node the agent ingests via RAG) — an attacker who can reach the prompt controls the query. Two escalation tiers follow:
- Data tier. Cypher is not read-only. Injected instructions can coerce the model into emitting write or delete statements, so an attacker can read every node in the graph or destroy it wholesale (a
DETACH DELETEover all matches). The payloads are omitted here —[REDACTED]— but the class is straightforward: any mutating statement the driver accepts. - Host tier. Neo4j ships optional procedure libraries. When APOC or
dbms.securityprocedures are enabled on the server, Cypher can call out to the operating system and filesystem. At that point a prompt-to-Cypher injection becomes OS-command and file access — remote code execution conditioned on server configuration, not on any additional flaw in Langroid.
The through-line with the SQL variant is exact. There, dialect primitives like COPY ... FROM PROGRAM turned a chat box into code execution on the database host; here, APOC procedures do the same job for a graph store. Different query language, identical shape.
Why it matters
“Text-to-query” agents are now a common building block — SQL, Cypher, SPARQL, MongoDB find-filters, Elasticsearch DSL. Each backend is its own injection surface, and each one trusts a string an LLM wrote under the influence of untrusted input. The Langroid case is a clean demonstration of two things defenders should internalize.
First, fixing one query language does not fix the others. The SQL agent was hardened in June; the Neo4j agent, structurally identical, stayed vulnerable because the remediation was written as a point fix rather than a policy applied across every query-emitting agent. If your codebase has one text-to-query surface, it very likely has several.
Second, the first-mile input does not have to be a malicious user. The indirect path — a graph node, a retrieved document, or a scraped page carrying instructions the agent later reads — means the attack surface includes any content the agent ingests. That is the harder vector to reason about and the easier one to overlook.
Defenses
The upstream fix and the general pattern reinforce each other:
- Upgrade to Langroid 0.65.5 or later, which closes this specific gap. Then audit every query-emitting agent in your stack for the same defect, rather than trusting that one patched module means you are clear.
- Treat LLM-generated queries as untrusted output. Default the agent’s database session to read-only, and enforce a statement-type allowlist that rejects writes, schema changes, and procedure calls unless a specific use case requires them.
- Run with a least-privilege database account. Give the agent a Neo4j role scoped to the data it needs. Disable APOC and
dbms.securityprocedures unless genuinely required; if they are, constrain them withdbms.security.procedures.allowlistto the minimum set. This removes the host tier even if the data tier is reached. - Gate mutating actions behind human review. A destructive query should require explicit confirmation, not fire automatically from a chat turn.
- Isolate the graph store. Network-segment it, and ensure the host running Neo4j has no ambient credentials or command surface an escaped query could abuse.
- Log the generated queries. Recording the exact Cypher the model emitted turns an opaque incident into an auditable one and surfaces injection attempts early.
Status
| Item | Reference | Date | Notes |
|---|---|---|---|
| Neo4j agent Cypher injection | GHSA-2pq5-3q89-j7cc / CVE-2026-55615 | 2026-07-06 | CVSS 9.8; affects langroid < 0.65.5; fixed in 0.65.5 |
| SQL agent prompt-to-SQL RCE (mirror) | CVE-2026-25879 | 2026-06-01 | Same class; fixed in 0.63.0; fix did not cover Neo4j |
| Host-tier escalation | APOC / dbms.security procedures | — | RCE conditioned on server config, not a separate flaw |
The right reading is not “a graph agent had a bug.” It is that a known, already-patched injection class reappeared unpatched in a sibling agent — the reminder that text-to-query remediation is a policy to apply everywhere, not a fix to ship once.