CVE-2026-114510
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.
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
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 High
CVSS: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 High
CVSS: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 High
CVSS: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 High
CVSS: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 High
CVSS: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 High
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
flexible-graphragPyPI |
0.6.0 up to 0.8.2 | 0.8.2 |
flexible-graphragPyPI |
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
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE 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
github.com/stevereiner/flexible-graphrag/security/advisories/GHSA-vqrg-773h-4wxc
github.com/stevereiner/flexible-graphrag/commit/9ace8d47e198ce3f15dbf22853e396ab4e9884fa
github.com/stevereiner/flexible-graphrag/releases/tag/v0.8.2
nvd.nist.gov/vuln/detail/CVE-2026-114510
github.com/advisories/GHSA-vqrg-773h-4wxc
github.com/CVEProject/cvelistV5/tree/main/cves/2026/114xxx/CVE-2026-114510.json
github.com/stevereiner/flexible-graphrag
Left out here: the rest of 2 source descriptions. The JSON record has them.