Oct 29, 2026·9 min read·3 visits
Mem0 Python SDK (< 2.0.18) and TypeScript SDK (< 3.1.6) fail to escape '=' and '&' delimiters when constructing deterministic session scope keys from entity identifiers. Attackers controlling one identifier (such as run_id) can inject key-value pairs to collide with another tenant's session scope, enabling cross-tenant conversation history retrieval and buffer manipulation.
CVE-2026-116575 (GHSA-5324-3w58-qpp2) is an unescaped delimiter injection and canonicalization flaw in the Mem0 persistent memory platform for AI agents and applications. Prior to Python SDK version 2.0.18 and TypeScript SDK version 3.1.6, the session scope key construction functions join caller-controlled user_id, agent_id, and run_id identifiers using raw '=' and '&' delimiters without sanitization. An attacker who controls one of these identifiers in a multi-tenant deployment can inject delimiter sequences to force a string collision with a target tenant's session scope key, resulting in unauthorized disclosure, poisoning, or eviction of the target tenant's recent conversation buffer.
Mem0 is an infrastructure platform that provides persistent memory management for Large Language Model (LLM) agents and multi-tenant AI applications. To maintain coherent context across user interactions, Mem0 utilizes a two-tier storage architecture consisting of long-term vector databases (such as Qdrant or PGVector) and a short-term relational conversation buffer (typically backed by SQLite). Whenever an application adds new memories via the SDK or REST API, Mem0 queries the relational store to fetch the most recent raw conversation messages associated with the active session.
To isolate tenant sessions within this relational buffer, Mem0 constructs a deterministic lookup key known as the session_scope. This string key is synthesized by combining up to three entity scoping parameters passed in the request filter dictionary: agent_id, run_id, and user_id. The resulting string serves as the primary partitioning index for reading historical messages via get_last_messages() and appending incoming conversation records via save_messages().
In versions prior to Python SDK 2.0.18 and TypeScript SDK 3.1.6, the functions responsible for generating this key—_build_session_scope() in Python and Memory.buildSessionScope() in TypeScript—concatenate the scoping parameters using unescaped = and & characters. Because the input validation routine fails to restrict or escape these structural delimiters, the key generation logic is vulnerable to parameter injection.
Tracked as CVE-2026-116575 (and GHSA-5324-3w58-qpp2), this flaw is classified under CWE-863 (Incorrect Authorization) and CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor). In multi-tenant deployments where a client controls at least one identifier (such as supplying a custom run_id or agent_id), an attacker can embed delimiter sequences to forge a session_scope identical to that of another tenant. This collision breaches tenant isolation, allowing unauthorized reading, interleaving, and eviction of victim conversation histories.
The root cause of CVE-2026-116575 is an improper output canonicalization flaw during composite key serialization. When constructing a single string representation from a tuple of attributes (agent_id, run_id, user_id), the serialization algorithm must ensure bijectivity—meaning two distinct input dictionaries must never map to the same output string. Mem0 violated this requirement by using query-string formatting (key=value joined by &) without escaping occurrences of the delimiter characters within the attribute values.
During the execution of Memory.add(), AsyncMemory.add(), or the POST /memories REST endpoint, Mem0 first passes the caller-supplied entity identifiers through _validate_and_trim_entity_id(). This validation helper strips leading and trailing whitespace and verifies that the string is non-empty, but it performs no character-set filtering or encoding. Immediately afterward, _build_session_scope() iterates through the sorted list of keys—specifically ["agent_id", "run_id", "user_id"]—and formats each present key-value pair as f"{key}={val}", joining the resulting array with "&".
Because the keys are evaluated in strict lexicographical order (agent_id first, run_id second, user_id third), an attacker controlling an earlier key in the sequence can inject subsequent keys into the serialized output. Specifically, if an attacker controls agent_id, they can inject fake run_id and user_id components. If the attacker controls run_id while agent_id is omitted, they can inject a fake user_id component.
When the relational database adapter executes get_last_messages(session_scope, limit=10), it uses a parameterized SQL query (WHERE session_scope = ?). While parameterization prevents SQL injection, the database engine evaluates equality against the already-collided session_scope string. Consequently, the storage layer cannot distinguish between a legitimate multi-attribute filter tuple and a single-attribute filter containing injected delimiters.
The vulnerability exists identically across both the Python and TypeScript implementations of the Mem0 SDK. In the Python SDK (mem0/memory/main.py), _build_session_scope() previously formatted dictionary values directly into the scope string without transformation. In the TypeScript SDK (mem0-ts/src/oss/src/memory/index.ts), the buildSessionScope() private method performed the exact same unescaped string interpolation.
Commit 71f2ebefa3494da21550fb525216818776cde67f (introduced in Pull Request #6892) remediates the vulnerability by introducing dedicated percent-encoding helper functions: _escape_scope_value() in Python and escapeScopeValue() in TypeScript.
# File: mem0/memory/main.py
def _escape_scope_value(val: Any) -> str:
"""Escape the structural delimiters of the session scope key."""
# Escape '%' first to prevent double-encoding or bypasses via literal '%26' / '%3D'
return str(val).replace("%", "%25").replace("&", "%26").replace("=", "%3D")
def _build_session_scope(filters):
"""Build deterministic session scope string from entity IDs."""
parts = []
for key in sorted(["user_id", "agent_id", "run_id"]):
val = filters.get(key)
if val:
# Vulnerable: parts.append(f"{key}={val}")
# Patched: Escape value delimiters prior to key=value concatenation
parts.append(f"{key}={_escape_scope_value(val)}")
return "&".join(parts)The TypeScript patch applies the same transformation using global regular expression replacements within the Memory class:
// File: mem0-ts/src/oss/src/memory/index.ts
private escapeScopeValue(val: unknown): string {
// Replace '%' prior to '&' and '=' to preserve canonical uniqueness
return String(val)
.replace(/%/g, "%25")
.replace(/&/g, "%26")
.replace(/=/g, "%3D");
}
private buildSessionScope(filters: SearchFilters): string {
const parts: string[] = [];
for (const key of ["agent_id", "run_id", "user_id"].sort()) {
const val = (filters as any)[key];
// Vulnerable: if (val) parts.push(`${key}=${val}`);
if (val) parts.push(`${key}=${this.escapeScopeValue(val)}`);
}
return parts.join("&");
}> [!NOTE]
> The order of replacement operations in _escape_scope_value() is critical for fix completeness. By replacing literal % characters with %25 before encoding & (%26) and = (%3D), the fix prevents second-order canonicalization collisions where an attacker supplies pre-encoded strings such as proj-x%26user_id%3Dalice to match an escaped output. Furthermore, because the key names themselves (agent_id, run_id, user_id) are hardcoded constants rather than user-controlled strings, only the values require escaping to guarantee bijective serialization.
Exploiting CVE-2026-116575 requires an architectural configuration where an unprivileged caller or tenant can control at least one entity identifier (agent_id or run_id) while the target session uses a predictable or known identifier subsequent in alphabetical order (run_id or user_id). In addition, if an application binds user_id from an authenticated session token for most endpoints, an attacker must locate a code path or API endpoint where user_id can be omitted (set to None) while run_id or agent_id is accepted from the request body.
Consider a target victim whose session interacts with Mem0 using run_id="proj-production" and user_id="target_user". When the victim's application invokes Memory.add(), Mem0 computes the session_scope as run_id=proj-production&user_id=target_user and stores the victim's raw messages in SQLite under that key.
# Proof of Concept: CVE-2026-116575 Delimiter Collision Verification
def vulnerable_build_session_scope(filters):
parts = []
for key in sorted(["user_id", "agent_id", "run_id"]):
val = filters.get(key)
if val:
parts.append(f"{key}={val}")
return "&".join(parts)
# 1. Legitimate user session filter tuple
victim_filters = {
"run_id": "proj-production",
"user_id": "target_user"
}
# 2. Malicious attacker session controlling only run_id (with user_id omitted)
attacker_filters = {
"run_id": "proj-production&user_id=target_user"
}
victim_scope = vulnerable_build_session_scope(victim_filters)
attacker_scope = vulnerable_build_session_scope(attacker_filters)
print(f"Victim Scope: {victim_scope}")
print(f"Attacker Scope: {attacker_scope}")
assert victim_scope == attacker_scopeOnce the attacker submits a memory addition call with filters={"run_id": "proj-production&user_id=target_user"}, three distinct exploitation primitives become available:
Memory.add(), Mem0 calls db.get_last_messages(session_scope, 10) and prepends those historical messages into the LLM prompt used to extract memories. By crafting an extraction prompt or observing the resulting memory entries returned by Memory.add(), the attacker can exfiltrate confidential information, Personally Identifiable Information (PII), or system prompts from the victim's recent conversation buffer.Memory.add() calls db.save_messages() using the collided session_scope. The attacker's messages are written directly into the victim's conversation buffer. The next time the victim triggers Memory.add(), the attacker's injected messages will be loaded into the victim's LLM context, enabling indirect prompt injection and manipulation of the victim's persistent memories.session_scope to push the victim's legitimate conversation history out of the active context window.CVE-2026-116575 carries a CVSS v3.1 base score of 6.3 (Medium) with the vector string CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N. The Attack Vector is scored as Local (AV:L) because the flaw resides within an SDK library interface rather than a standalone network daemon, though it can be exposed over the network if an application wraps Mem0 in a REST API (such as the bundled POST /memories flow). Attack Complexity is rated High (AC:H) because successful exploitation requires specific multi-tenant parameter handling where the caller can omit higher-order identifiers (or control lower-order ones) and possesses knowledge of the target tenant's identifier values.
Both Confidentiality and Integrity impacts are rated High (C:H/I:H). In AI agent architectures, the short-term message buffer contains raw, unfiltered user transcripts prior to summarization or PII scrubbing. Unauthorized read access to this buffer exposes sensitive user queries and proprietary agent instructions. High integrity impact stems from the ability to inject arbitrary messages into another user's session buffer, corrupting both immediate LLM reasoning and downstream long-term vector memory extraction.
Availability impact is rated None (A:N). Although an attacker can evict recent messages from the sliding window buffer in SQLite, this degrades contextual accuracy rather than causing service crashes or resource exhaustion.
From a threat intelligence perspective, the Exploit Prediction Scoring System (EPSS) assigns CVE-2026-116575 a score of 0.00108 (0.11% probability of exploitation within 30 days, placing it in the 1.19th percentile). The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no weaponized exploits have been observed in the wild beyond verification unit tests included in the patch.
To remediate CVE-2026-116575, organizations and developers utilizing the Mem0 platform must upgrade their SDK dependencies to the patched releases that enforce delimiter percent-encoding.
For Python environments using mem0ai, upgrade to version 2.0.18 or later:
pip install --upgrade "mem0ai>=2.0.18"For Node.js and TypeScript environments using mem0ai (mem0-ts), upgrade to version 3.1.6 or later:
npm install "mem0ai@>=3.1.6"The patch maintains full backward compatibility for existing deployments whose legitimate entity identifiers do not contain %, &, or = characters, as their computed session_scope keys remain unchanged. For legitimate entity IDs that previously contained literal %, &, or = characters, upgrading will cause those specific sessions to map to a newly escaped session_scope key, effectively starting a fresh 10-message sliding buffer without impacting long-term vector store memories.
In environments where an immediate library upgrade is not feasible, engineering teams should implement strict input validation at the application or API gateway layer. Specifically, validate that user_id, agent_id, and run_id match a strict allowlist pattern (such as alphanumeric characters, hyphens, and underscores: ^[a-zA-Z0-9_-]+$) and reject any requests containing &, =, or %. Additionally, multi-tenant applications should strictly bind user_id from verified authentication context on every Mem0 method invocation rather than permitting callers to omit it.
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
mem0ai (Python SDK) Mem0 | < 2.0.18 | 2.0.18 |
mem0ai / mem0-ts (TypeScript SDK) Mem0 | < 3.1.6 | 3.1.6 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-116575 (GHSA-5324-3w58-qpp2) |
| CWE Classification | CWE-863 (Incorrect Authorization) / CWE-200 (Information Exposure) |
| CVSS v3.1 Score | 6.3 Medium (CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N) |
| EPSS Score | 0.11% (1.19th Percentile) |
| Affected Functions | _build_session_scope() (Python) / Memory.buildSessionScope() (TypeScript) |
| Security Impact | Cross-tenant conversation disclosure, message buffer poisoning, and history eviction |
| Patched Versions | mem0ai >= 2.0.18 (Python) / mem0ai >= 3.1.6 (TypeScript) |
| CISA KEV Status | Not Listed |
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check, allowing attackers to bypass intended access restrictions through key canonicalization collisions.
A session-binding vulnerability in Python Social Auth (social-core) versions prior to 6.2.0 allows an attacker to perform authorization code injection against the OpenID Connect authentication backend. By storing generated OIDC nonces in a global database table without binding them to the initiating browser session, the library accepts any valid, unexpired nonce returned in an ID token regardless of which session originated the authentication transaction. When combined with the default disablement of Proof Key for Code Exchange (PKCE), an attacker who intercepts an unused authorization code can inject it into their own authentication flow to achieve full account takeover.
CVE-2026-111497, tracked in threat intelligence under the aliases Node2Shell, Broccoli2Shell, and HTTP2Shell, is a critical out-of-bounds heap write and denial-of-service vulnerability in the reference Google Brotli decompression library (c/dec/decode.c). The flaw originates from an unchecked return value and error-code conflation in DecodeBlockTypeAndLength() when processing malformed streams with single-block-type declarations (max_block_type <= 1). On the fast decoding path, the ignored error return causes the 32-bit unsigned block_length counter to underflow from 0 to UINT32_MAX (0xFFFFFFFF), corrupting internal decoder tables and enabling arbitrary heap memory overwrites via memmove16(). On the streaming path, the error return is misinterpreted as an incomplete bit-stream, trapping the worker process in an infinite 100% CPU loop. Because Google Brotli is embedded across major runtimes and infrastructure components—including Node.js, Envoy Proxy, Microsoft .NET, Istio, and Python—the vulnerability exposes a broad attack surface via standard HTTP Content-Encoding: br request and response bodies.
Seroval versions prior to 1.6.8 contain an insecure deserialization vulnerability (CWE-502) in the `fromJSON` parser. When reconstructing `AsyncIteratorFactoryInstance` or `IteratorFactoryInstance` nodes, the deserializer verifies the source node type using its identifier in the AST lookup table rather than verifying the runtime identity of the deserialized object. Because unmarked node identifiers omitted from the `m` reference array can be reused in vanilla deserialization mode, an attacker can substitute an arbitrary plain object or plugin-produced value as the stream source. This causes the runtime to invoke `.on(listener)` on an attacker-controlled target.
A Denial of Service (DoS) vulnerability exists in the Seroval JavaScript serialization library affecting versions 1.4.1 through 1.6.7. The deserialization routines fromJSON and fromCrossJSON fail to verify that node element lists are actual JavaScript Array primitives before reading their length property. An unauthenticated remote attacker can supply a compact, malformed JSON payload containing an array-like object with an arbitrary length value, causing unbounded memory allocation and synchronous loop iterations that block the event loop or crash the process via heap exhaustion.
Seroval, a JavaScript serialization library used for transferring complex structures across network boundaries, contains an input validation vulnerability in versions prior to 1.6.8. The `fromJSON` and `fromCrossJSON` deserializers fail to validate array element lists and the termination boundaries (`done` and `throw` indices) of sequence nodes. By supplying out-of-bounds indices or forged array-like structures, an unauthenticated remote attacker can construct an iterator with an unreachable exit condition or trigger excessive loop iterations, halting the single-threaded JavaScript event loop and causing a denial of service.
CVE-2026-116582 is an improper output escaping vulnerability in the Seroval JavaScript serialization library that leads to cross-site scripting (XSS). When Seroval serializes RegExp objects into executable JavaScript literals for embedding in HTML script elements, it fails to neutralize the less-than character ('<'). If an application constructs a regular expression from untrusted input and embeds the serialized output inside an inline script block during server-side rendering (SSR), an attacker can inject a closing script tag to break out of the JavaScript context and execute arbitrary client-side code.