Every CVE Search/ Get API key

CVE-2026-114510

Flexible GraphRAG: Cypher/Graph Injection via broken string-escaping in FalkorDB adapter (_escape()) - full graph wipe from ingested-document content

7.1 HighFix available

Flexible GraphRAG is a Python framework for building graph and retrieval-augmented generation applications across multiple graph, vector, and search backends. From 0.6.0 until 0.8.2, the _escape() helper in flexible-graphrag/langchain/graph/pg_store_adapters/falkordb_adapter.py escapes quotes before backslashes and then interpolates the result into Cypher assembled by FalkorDBAdapter.add_graph_documents, allowing an inserted escape to be doubled and a quote to terminate the string early. When GRAPH_BACKEND=langchain and GRAPH_DB=falkordb are configured, attacker-influenced ingested document content can cause the extraction model to produce malicious entity or relationship values that reach these queries, allowing injected Cypher clauses to modify or delete graph data and, when the calling code surfaces query results to the user or LLM, disclose graph data. Dynamic node and relationship type identifiers are also inserted into backtick-quoted query positions without safe parameterization. The LlamaIndex FalkorDB store is not affected. This issue is fixed in version 0.8.2.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
Low
User interaction
Required

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.36%Up 0.1 points since Oct 23, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.1

Flexible GraphRAG: Cypher/Graph Injection via broken string-escaping in FalkorDB adapter (_escape()) - full graph wipe from ingested-document content

Flexible GraphRAG is a Python framework for building graph and retrieval-augmented generation applications across multiple graph, vector, and search backends. From 0.6.0 until 0.8.2, the _escape() helper in flexible-graphrag/langchain/graph/pg_store_adapters/falkordb_adapter.py escapes quotes before backslashes and then interpolates the result into Cypher assembled by FalkorDBAdapter.add_graph_documents, allowing an inserted escape to be doubled and a quote to terminate the string early. When GRAPH_BACKEND=langchain and GRAPH_DB=falkordb are configured, attacker-influenced ingested document content can cause the extraction model to produce malicious entity or relationship values that reach these queries, allowing injected Cypher clauses to modify or delete graph data and, when the calling code surfaces query results to the user or LLM, disclose graph data. Dynamic node and relationship type identifiers are also inserted into backtick-quoted query positions without safe parameterization. The LlamaIndex FalkorDB store is not affected. This issue is fixed in version 0.8.2.

Record
CVE-2026-114510
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-116, CWE-943
Updated
2026-11-02
EUVDEUVD-2026-108207 7.1

Flexible GraphRAG is a Python framework for building graph and retrieval-augmented generation applications across multiple graph, vector, and search backends. From 0.6.0 until 0.8.2, the _escape() helper in flexible-graphrag/langchain/graph/pg_store_adapters/falkordb_adapter.py escapes quotes before backslashes and then interpolates the result into Cypher assembled by FalkorDBAdapter.add_graph_documents, allowing an inserted escape to be doubled and a quote to terminate the string early. When GRAPH_BACKEND=langchain and GRAPH_DB=falkordb are configured, attacker-influenced ingested document content can cause the extraction model to produce malicious entity or relationship values that reach these queries, allowing injected Cypher clauses to modify or delete graph data and, when the calling code surfaces query results to the user or LLM, disclose graph data. Dynamic node and relationship type identifiers are also inserted into backtick-quoted query positions without safe parameterization. The LlamaIndex FalkorDB store is not affected. This issue is fixed in version 0.8.2.

Record
EUVD-2026-108207
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-vqrg-773h-4wxc 7.1

Flexible GraphRAG: Cypher/Graph Injection via broken string-escaping in FalkorDB adapter (_escape()) - full graph wipe from ingested-document content

Description def _escape(v: str) -> str: return str(v).replace("'", "\'").replace("\", "\\") The escaping order is backwards, exactly like the classic HTML double-escape bug: step 1 inserts a literal backslash in front of every ', then step 2 re-matches that just-inserted backslash and doubles it. For an input ending in a single quote, the two steps combined produce \' (two backslashes followed by the quote) in the output — which, under the standard backslash-escape convention used by Cypher/openCypher single-quoted string literals, is parsed as one escaped backslash followed by an unescaped, string-terminating quote. The quote is not neutralized; the string breaks open.

This escaped value is interpolated directly into Cypher query strings built by string formatting (not parameters) at 5 call sites in the same method:

self.lc_graph.query( f"MERGE (n:{node.type} {{id: '{_escape(node.id)}'}}) " f"{props}" f"RETURN distinct 'done' AS result" ) ... self.lc_graph.query( f"MATCH (a:{rel.source.type} {{id: '{_escape(rel.source.id)}'}}), " f"(b:{rel.target.type} {{id: '{_escape(rel.target.id)}'}}) " f"MERGE (a)-[r:{rel_type}]->(b) " f"{props}" f"RETURN distinct 'done' AS result" ) node.type, rel.source.type, rel.target.type and rel_type are also interpolated into backtick-quoted identifiers with no escaping at all.

Where the attacker-controlled data comes from: node/rel objects are LLM-extracted entities and relationships derived from ingested document content (the whole point of this GraphRAG pipeline). Document content is therefore untrusted input relative to this database layer — a classic indirect-prompt-injection-to-query-injection chain: craft a document whose text causes the extraction LLM to produce an entity id containing the payload below, ingest it, and the broken escape function turns it into live Cypher.

Impact Full Cypher clause injection into lc_graph.query() calls that run with no query parameterization and no read-only restriction visible in this adapter. Demonstrated impact: appending a second clause (MATCH (m) DETACH DELETE m) to the query wipes every node and relationship in the graph in a single call. The same primitive can be adapted to exfiltrate data (e.g. RETURN other nodes' properties back through a field the caller reads) or tamper with existing records, depending on what the calling code surfaces to the end user/LLM.

Proof of Concept (executed, full query reconstruction) Only the two-line _escape() function and the f-string template were executed — no import of the flexible-graphrag package itself.

raw attacker-controlled node.id: "pwn' }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //" output of buggy _escape(): "pwn\' }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //" full query string actually sent to lc_graph.query(): MERGE (n:Entity {id: 'pwn\' }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //'}) RETURN distinct 'done' AS result Parse trace (standard backslash-escape convention for single-quoted Cypher strings):

Opening ' starts the id string literal. pwn is consumed literally. \ (two backslash chars) is consumed as one escaped-backslash sequence — the string now holds one literal backslash. The very next character is an unescaped ' — this closes the string literal early. id value ends up as "pwn" (with a trailing backslash). The remaining text }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //... is now parsed as live Cypher syntax, not string content. }) closes the property map and the node pattern — completing a valid MERGE clause. MATCH (m) DETACH DELETE m RETURN count(m) AS pwn is a second, fully valid Cypher clause chained immediately after the MERGE (Cypher allows sequential clauses with no separator). // starts a line comment, consuming the rest of the original template ('}) RETURN distinct 'done' AS result) so it never executes. Effective query the database actually receives:

MERGE (n:Entity…

Record
GHSA-vqrg-773h-4wxc
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-116, CWE-943
Updated
2026-11-02
NVD 7.1

Flexible GraphRAG is a Python framework for building graph and retrieval-augmented generation applications across multiple graph, vector, and search backends. From 0.6.0 until 0.8.2, the _escape() helper in flexible-graphrag/langchain/graph/pg_store_adapters/falkordb_adapter.py escapes quotes before backslashes and then interpolates the result into Cypher assembled by FalkorDBAdapter.add_graph_documents, allowing an inserted escape to be doubled and a quote to terminate the string early. When GRAPH_BACKEND=langchain and GRAPH_DB=falkordb are configured, attacker-influenced ingested document content can cause the extraction model to produce malicious entity or relationship values that reach these queries, allowing injected Cypher clauses to modify or delete graph data and, when the calling code surfaces query results to the user or LLM, disclose graph data. Dynamic node and relationship type identifiers are also inserted into backtick-quoted query positions without safe parameterization. The LlamaIndex FalkorDB store is not affected. This issue is fixed in version 0.8.2.

Record
CVE-2026-114510
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-116, CWE-943
Updated
2026-11-02
OSV 7.1

Flexible GraphRAG: Cypher/Graph Injection via broken string-escaping in FalkorDB adapter (_escape()) - full graph wipe from ingested-document content

Flexible GraphRAG is a Python framework for building graph and retrieval-augmented generation applications across multiple graph, vector, and search backends. From 0.6.0 until 0.8.2, the _escape() helper in flexible-graphrag/langchain/graph/pg_store_adapters/falkordb_adapter.py escapes quotes before backslashes and then interpolates the result into Cypher assembled by FalkorDBAdapter.add_graph_documents, allowing an inserted escape to be doubled and a quote to terminate the string early. When GRAPH_BACKEND=langchain and GRAPH_DB=falkordb are configured, attacker-influenced ingested document content can cause the extraction model to produce malicious entity or relationship values that reach these queries, allowing injected Cypher clauses to modify or delete graph data and, when the calling code surfaces query results to the user or LLM, disclose graph data. Dynamic node and relationship type identifiers are also inserted into backtick-quoted query positions without safe parameterization. The LlamaIndex FalkorDB store is not affected. This issue is fixed in version 0.8.2.

Record
CVE-2026-114510
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-116, CWE-943
Updated
2026-11-02
OSVGHSA-vqrg-773h-4wxc 7.1

Flexible GraphRAG: Cypher/Graph Injection via broken string-escaping in FalkorDB adapter (_escape()) - full graph wipe from ingested-document content

Description def _escape(v: str) -> str: return str(v).replace("'", "\'").replace("\", "\\") The escaping order is backwards, exactly like the classic HTML double-escape bug: step 1 inserts a literal backslash in front of every ', then step 2 re-matches that just-inserted backslash and doubles it. For an input ending in a single quote, the two steps combined produce \' (two backslashes followed by the quote) in the output — which, under the standard backslash-escape convention used by Cypher/openCypher single-quoted string literals, is parsed as one escaped backslash followed by an unescaped, string-terminating quote. The quote is not neutralized; the string breaks open.

This escaped value is interpolated directly into Cypher query strings built by string formatting (not parameters) at 5 call sites in the same method:

self.lc_graph.query( f"MERGE (n:{node.type} {{id: '{_escape(node.id)}'}}) " f"{props}" f"RETURN distinct 'done' AS result" ) ... self.lc_graph.query( f"MATCH (a:{rel.source.type} {{id: '{_escape(rel.source.id)}'}}), " f"(b:{rel.target.type} {{id: '{_escape(rel.target.id)}'}}) " f"MERGE (a)-[r:{rel_type}]->(b) " f"{props}" f"RETURN distinct 'done' AS result" ) node.type, rel.source.type, rel.target.type and rel_type are also interpolated into backtick-quoted identifiers with no escaping at all.

Where the attacker-controlled data comes from: node/rel objects are LLM-extracted entities and relationships derived from ingested document content (the whole point of this GraphRAG pipeline). Document content is therefore untrusted input relative to this database layer — a classic indirect-prompt-injection-to-query-injection chain: craft a document whose text causes the extraction LLM to produce an entity id containing the payload below, ingest it, and the broken escape function turns it into live Cypher.

Impact Full Cypher clause injection into lc_graph.query() calls that run with no query parameterization and no read-only restriction visible in this adapter. Demonstrated impact: appending a second clause (MATCH (m) DETACH DELETE m) to the query wipes every node and relationship in the graph in a single call. The same primitive can be adapted to exfiltrate data (e.g. RETURN other nodes' properties back through a field the caller reads) or tamper with existing records, depending on what the calling code surfaces to the end user/LLM.

Proof of Concept (executed, full query reconstruction) Only the two-line _escape() function and the f-string template were executed — no import of the flexible-graphrag package itself.

raw attacker-controlled node.id: "pwn' }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //" output of buggy _escape(): "pwn\' }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //" full query string actually sent to lc_graph.query(): MERGE (n:Entity {id: 'pwn\' }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //'}) RETURN distinct 'done' AS result Parse trace (standard backslash-escape convention for single-quoted Cypher strings):

Opening ' starts the id string literal. pwn is consumed literally. \ (two backslash chars) is consumed as one escaped-backslash sequence — the string now holds one literal backslash. The very next character is an unescaped ' — this closes the string literal early. id value ends up as "pwn" (with a trailing backslash). The remaining text }) MATCH (m) DETACH DELETE m RETURN count(m) AS pwn //... is now parsed as live Cypher syntax, not string content. }) closes the property map and the node pattern — completing a valid MERGE clause. MATCH (m) DETACH DELETE m RETURN count(m) AS pwn is a second, fully valid Cypher clause chained immediately after the MERGE (Cypher allows sequential clauses with no separator). // starts a line comment, consuming the rest of the original template ('}) RETURN distinct 'done' AS result) so it never executes. Effective query the database actually receives:

MERGE (n:Entity…

Record
GHSA-vqrg-773h-4wxc
CVSS 3.1
7.1 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-116, CWE-943
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
flexible-graphrag
PyPI
0.6.0 up to 0.8.2
0.8.2
flexible-graphrag
PyPI
0.6.0 up to 0.8.2, plus listed versions
0.6.10.6.20.6.30.7.00.7.10.7.20.8.00.8.1
0.8.2

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-114510 is a high vulnerability (CVSS 7.1) in Stevereiner Flexible-graphrag, published October 22, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 30th percentile. Fixed in flexible-graphrag 0.8.2.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open