Get a Demo

Let's Patch It!

Book a short call with one our specialists, we'll walk you through how Endor Patches work, and ask you a few questions about your environment (like your primary programming languages and repository management). We'll also send you an email right after you fill out the form, feel free to reply with any questions you have in advance!

CVE

CVE-2026-55615

Langroid: Neo4jChatAgent executes LLM-generated Cypher without validation (prompt-to-Cypher injection; config-conditional RCE), mirroring the SQLChatAgent bug fixed in CVE-2026-25879
Back to all
CVE

CVE-2026-55615

Langroid: Neo4jChatAgent executes LLM-generated Cypher without validation (prompt-to-Cypher injection; config-conditional RCE), mirroring the SQLChatAgent bug fixed in CVE-2026-25879

Neo4jChatAgent passes LLM-generated Cypher queries straight to the Neo4j driver with no validation, no statement-type allowlist, and no opt-out gate. The query text is influenceable by prompt injection (direct user input or indirect content the agent reads back via RAG), so an attacker who can influence the prompt can read or destroy all graph data and, when APOC or dbms.security procedures are enabled on the server, achieve OS-command and filesystem access. This is the same defect class and threat model as the SQLChatAgent prompt-to-SQL-to-RCE issue fixed in version 0.63.0 (CVE-2026-25879); that fix did not extend to the neo4j module.

Technical detail

Untrusted-input to sink trace (reviewed on langroid HEAD b9df06f, v0.65.3):

  1. Tool schemas accept raw query text from the LLM. langroid/agent/special/neo4j/tools.py:4-9 (CypherRetrievalTool.cypherquery: str) and :15-21 (CypherCreationTool.cypherquery: str). These tools are enabled unconditionally in neo4jchatagent.py:412-419 (enable_message([GraphSchemaTool, CypherRetrievalTool, CypherCreationTool, DoneTool])).
  2. Read path. neo4jchatagent.py:300 cypherretrievaltool(msg) -> :325 query = msg.cypherquery -> :328 self.readquery(query) -> :223 session.run(query, parameters). The LLM-controlled string is the first positional argument to session.run; parameters is None. No validation occurs between :325 and :223.
  3. Write path. neo4jchatagent.py:338 cyphercreationtool(msg) -> :348 query = msg.cypherquery -> :351 self.writequery(query) -> :276 session.writetransaction(lambda tx: tx.run(query, parameters)). The only inspection of the string (writequery :251-264) is a query.upper() substring test for CREATE/MERGE/CONSTRAINT/INDEX whose sole effect is setting self.config.databasecreated = True; it blocks nothing. A query such as MATCH (n) DETACH DELETE n (the same statement the built-in removedatabase helper runs at :287-293) passes unrestricted.
  4. Guarded-sibling contrast proving the incomplete fix. The SQLChatAgent, patched for the parent CVE, validates every query before execution. langroid/agent/special/sql/sqlchatagent.py:256 defines allowdangerousoperations: bool = False (opt-in gate); :618 validatequery runs (a) a dangerous-pattern regex blocklist (DANGEROUSSQLPATTERNS, :628-641), (b) a sqlglot statement-type allowlist defaulting to SELECT-only (:643-686), and (c) an AST-level dangerous-function blocklist (:687-704). runquery calls rejection = self.validatequery(query) at :721 before executing. The neo4j readquery/writequery paths have no equivalent: a grep for validatequery/allowdangerous in neo4jchat_agent.py returns nothing. The defense exists for SQL and is simply absent for Cypher, which is the definition of an incomplete fix for the same prompt-to-query-language injection class.

Proof of concept (static, no third-party systems, no live Neo4j required)

Step 1 (presence vs absence of the guard, one command):

grep -n "_validate_query\|allow_dangerous" langroid/agent/special/sql/sql_chat_agent.py    # SQL: many hits (gate + validator + call site :721)
grep -n "_validate_query\|allow_dangerous" langroid/agent/special/neo4j/neo4j_chat_agent.py # neo4j: ZERO hits

Step 2 (sink trace is direct, no interposed check):

read_query:  msg.cypher_query (line 325) -> read_query(query) (328) -> session.run(query, parameters) (223)
write_query: msg.cypher_query (348) -> write_query(query) (351) -> tx.run(query, parameters) (276)

The only string inspection (writequery 251-264) is a .upper() substring test that only sets databasecreated=True and rejects nothing. The asymmetry (validator + opt-out gate enforced on every SQL query at sqlchatagent.py:721; nothing on either Cypher path) is the deterministic artifact: the same project, for the same injection class, guards one query language and not the other. A dynamic confirmation against an operator-owned disposable Neo4j (create a CSPRNG-marked node via cyphercreationtool, read it back via cypherretrievaltool, then DETACH DELETE it) reproduces the read/write/destroy primitive without any third-party system.

Impact

An attacker who can influence the agent prompt (directly, or indirectly via content the agent reads back through RAG) controls the executed Cypher. Floor (no extra config, not contingent): unauthorized read of all graph data via cypherretrievaltool and full write/destroy (including MATCH (n) DETACH DELETE n) via cyphercreationtool, plus the built-in LOAD CSV remote-fetch (SSRF) primitive. Ceiling (config-conditional, when APOC / dbms.security procedures are granted to the DB role, a common deployment): apoc.load. (SSRF + remote/local file read), apoc.cypher.runFile / apoc.import. / apoc.export. (filesystem), and CALL dbms. admin procedures, i.e. the Cypher analogue of the parent's COPY ... FROM PROGRAM RCE primitive. This mirrors the privileged-role contingency the parent advisory (CVE-2026-25879) accepted.

Suggested fix

Mirror the SQLChatAgent fix in the neo4j module: (1) add allowdangerousoperations: bool = False to Neo4jChatAgentConfig with a read-only default for cypherretrievaltool; (2) add a validatecypher(query, write) method that, unless allowdangerousoperations is True, blocks CALL apoc., CALL dbms., CALL db.* admin procedures, LOAD CSV, and any procedure/function touching filesystem/network/OS, and (for the read path) rejects write clauses (CREATE/MERGE/SET/DELETE/DETACH DELETE/REMOVE/DROP); (3) enforce it before session.run (readquery :223) and tx.run (writequery :276), returning the rejection to the LLM the way runquery does at sqlchatagent.py:721; (4) document, as the SQL config does, that LLM-generated Cypher is prompt-injectable and that allowdangerous_operations should only be set with a least-privilege Neo4j role. I am happy to send this as a PR if useful.

Relationship to the parent advisory

GHSA-mxfr-6hcw-j9rq / CVE-2026-25879 (Langroid SQLChatAgent prompt-to-SQL injection leading to RCE; Critical; fixed 0.63.0, SQLChatAgent only). This report is the same injection class in the still-unpatched sibling Neo4jChatAgent.

Severity note (honest, both ways)

Filed as High to reflect the non-contingent floor (unrestricted attacker-steered graph read/write/destroy + LOAD CSV SSRF, present regardless of APOC). The ceiling is Critical and RCE-equivalent when APOC / admin procedures are enabled on the DB role, at parity with the parent CVE-2026-25879 which was rated Critical under an equivalent privileged-role contingency. Please rate per your deployment assumptions.

Resolution (maintainer)

Fixed in 0.65.5. Both Neo4jChatAgent (Cypher) and the sibling ArangoChatAgent (AQL, raised in the follow-up) now mirror the SQLChatAgent controls: a new allowdangerousoperations config gate (default False), with the retrieval tool restricted to read-only queries and both tools rejecting code-execution / file / network primitives (LOAD CSVapoc.*dbms.*CALL db.* for Cypher; user-defined namespace::func calls for AQL) unless the operator opts in. Validation is enforced at the tool handlers, so internal schema/maintenance calls are unaffected. Upgrade to 0.65.5 and run the agents against a least-privilege database role.

Package Versions Affected

Package Version
patch Availability
No items found.

Automatically patch vulnerabilities without upgrading

Fix Without Upgrading
Detect compatible fix
Apply safe remediation
Fix with a single pull request

CVSS Version

Severity
Base Score
CVSS Version
Score Vector
C
H
U
9.2
-
4.0
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
C
H
U
0
-
C
H
U
-

Related Resources

No items found.

References

https://github.com/langroid/langroid/security/advisories/GHSA-2pq5-3q89-j7cc, https://github.com/langroid/langroid

Severity

0

CVSS Score
0
10

Basic Information

Base CVSS
0
EPSS Probability
0.00461%
EPSS Percentile
0.37111%
Introduced Version
0
Fix Available
0.65.5

Fix Critical Vulnerabilities Instantly

Secure your app without upgrading.
Fix Without Upgrading