{"id":"GHSA-5324-3w58-qpp2","revision":null,"records":[{"revision":"10483de1d4ac40079835da4632aec02f","observedAt":"2026-10-27T15:44:06.157+00:00","statement":{"source":"cve-program","id":"CVE-2026-116575","aliases":[],"title":"Mem0: Cross-tenant conversation leak via unescaped delimiter collision in session-scope key (_build_session_scope)","description":"Mem0 provides persistent memory infrastructure for AI agents and applications. Prior to Python SDK 2.0.18 and TypeScript SDK 3.1.6, _build_session_scope() in mem0/memory/main.py and Memory.buildSessionScope() in the TypeScript SDK join caller-controlled user_id, agent_id, and run_id values using unescaped & and = delimiters. A caller can embed another scope component in a controlled identifier so that distinct tenant tuples produce the same session_scope key. Memory.add(), AsyncMemory.add(), and the REST POST /memories flow use that key to read and save recent raw messages, allowing cross-tenant conversation disclosure and eviction or interleaving of another tenant's history. This issue is fixed in Python SDK 2.0.18 and TypeScript SDK 3.1.6.","state":0,"publishedAt":"2026-10-26T20:15:01.875+00:00","modifiedAt":"2026-10-27T14:18:25.039+00:00","affected":[{"ecosystem":"Vendor product","name":"mem0","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"mem0ai\",\n                    \"product\": \"mem0\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 2.0.18\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N","score":6.3,"severity":"MEDIUM"}],"references":["https://github.com/mem0ai/mem0/security/advisories/GHSA-5324-3w58-qpp2","https://github.com/mem0ai/mem0/pull/6892","https://github.com/mem0ai/mem0/commit/71f2ebefa3494da21550fb525216818776cde67f","https://github.com/mem0ai/mem0/releases/tag/ts-v3.1.6","https://github.com/mem0ai/mem0/releases/tag/v2.0.18"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-116575","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-116575","identifiers":["CVE-2026-116575"]},"retained":false,"historicalAffected":[]},{"revision":"ec4adf343bd54d2999a159d6c41d50e9","observedAt":"2026-10-28T21:57:34.697+00:00","statement":{"source":"euvd","id":"EUVD-2026-110315","aliases":["CVE-2026-116575"],"title":"Mem0 provides persistent memory infrastructure for AI agents and applications. Prior to Python SDK 2.0.18 and TypeScript SDK 3.1.6, _build_session_scope() in mem0/memory/main.py and Memory.buildSessionScope() in the TypeScript SDK join call…","description":"Mem0 provides persistent memory infrastructure for AI agents and applications. Prior to Python SDK 2.0.18 and TypeScript SDK 3.1.6, _build_session_scope() in mem0/memory/main.py and Memory.buildSessionScope() in the TypeScript SDK join caller-controlled user_id, agent_id, and run_id values using unescaped & and = delimiters. A caller can embed another scope component in a controlled identifier so that distinct tenant tuples produce the same session_scope key. Memory.add(), AsyncMemory.add(), and the REST POST /memories flow use that key to read and save recent raw messages, allowing cross-tenant conversation disclosure and eviction or interleaving of another tenant's history. This issue is fixed in Python SDK 2.0.18 and TypeScript SDK 3.1.6.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N","score":6.3,"severity":null}],"references":["https://github.com/mem0ai/mem0/security/advisories/GHSA-5324-3w58-qpp2","https://github.com/mem0ai/mem0/pull/6892","https://github.com/mem0ai/mem0/commit/71f2ebefa3494da21550fb525216818776cde67f","https://github.com/mem0ai/mem0/releases/tag/ts-v3.1.6","https://github.com/mem0ai/mem0/releases/tag/v2.0.18"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-110315","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 26, 2026, 8:15:01 PM","dateUpdated":"Oct 27, 2026, 2:18:25 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-110315","identifiers":["EUVD-2026-110315","CVE-2026-116575"]},"retained":false,"historicalAffected":[]},{"revision":"c53dc777e4164e209f51dc4c3bd734f8","observedAt":"2026-10-28T21:57:06.789+00:00","statement":{"source":"nvd","id":"CVE-2026-116575","aliases":[],"title":"Mem0 provides persistent memory infrastructure for AI agents and applications. Prior to Python SDK 2.0.18 and TypeScript SDK 3.1.6, _build_session_scope() in mem0/memory/main.py and Memory.buildSessionScope() in the TypeScript SDK join call…","description":"Mem0 provides persistent memory infrastructure for AI agents and applications. Prior to Python SDK 2.0.18 and TypeScript SDK 3.1.6, _build_session_scope() in mem0/memory/main.py and Memory.buildSessionScope() in the TypeScript SDK join caller-controlled user_id, agent_id, and run_id values using unescaped & and = delimiters. A caller can embed another scope component in a controlled identifier so that distinct tenant tuples produce the same session_scope key. Memory.add(), AsyncMemory.add(), and the REST POST /memories flow use that key to read and save recent raw messages, allowing cross-tenant conversation disclosure and eviction or interleaving of another tenant's history. This issue is fixed in Python SDK 2.0.18 and TypeScript SDK 3.1.6.","state":0,"publishedAt":"2026-10-26T21:18:13.36+00:00","modifiedAt":"2026-10-27T16:04:22.707+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N","score":6.3,"severity":"MEDIUM"}],"references":["https://github.com/mem0ai/mem0/commit/71f2ebefa3494da21550fb525216818776cde67f","https://github.com/mem0ai/mem0/pull/6892","https://github.com/mem0ai/mem0/releases/tag/ts-v3.1.6","https://github.com/mem0ai/mem0/releases/tag/v2.0.18","https://github.com/mem0ai/mem0/security/advisories/GHSA-5324-3w58-qpp2"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-116575","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-200","CWE-863"],"productCriteria":[],"sourceDates":{"published":"2026-10-26T21:18:13.360","lastModified":"2026-10-27T16:04:22.707"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-116575","identifiers":["CVE-2026-116575"]},"retained":false,"historicalAffected":[]},{"revision":"b37352c162ff4fe58cbca94eec56d0cb","observedAt":"2026-10-28T21:55:29.474+00:00","statement":{"source":"osv","id":"GHSA-5324-3w58-qpp2","aliases":["CVE-2026-116575"],"title":"Mem0: Cross-tenant conversation leak via unescaped delimiter collision in session-scope key (_build_session_scope)","description":"Summary\n\n_build_session_scope() in mem0/memory/main.py builds the conversation-history scope key used to fetch/store the last N raw messages by naively joining caller-supplied user_id/agent_id/run_id values with unescaped & and = delimiters:\n\ndef _build_session_scope(filters):\n    parts = []\n    for key in sorted([\"user_id\", \"agent_id\", \"run_id\"]):\n        val = filters.get(key)\n        if val:\n            parts.append(f\"{key}={val}\")\n    return \"&\".join(parts)\n\n_validate_and_trim_entity_id() (the only validation applied to these values) rejects only empty/whitespace-only strings — it never forbids & or =. This scope string is then used as an exact-match key against a SQLite column (storage.py: WHERE session_scope = ?) to fetch the last 10 raw messages for a given scope (main.py, Phase 0 of _add_to_vector_store, both sync and async).\n\nImpact\n\nAny caller who controls only their own run_id (or agent_id) — a very common integration shape, since apps frequently pass a client-/session-supplied identifier straight through as run_id without namespacing — can craft a value containing &user_id=<victim> (or &agent_id=<victim>) that produces a scope string byte-for-byte identical to a different tenant's real scope string.\n\nConcretely: Tenant A calls add(msgs, user_id=\"alice\", run_id=\"proj-x\") → scope \"run_id=proj-x&user_id=alice\". An attacker who can only set run_id calls add(msgs, run_id=\"proj-x&user_id=alice\") → identical scope string. The attacker's next add() call:\n\n1. Reads Tenant A's raw prior conversation via get_last_messages(session_scope) and splices it verbatim into the attacker's own LLM fact-extraction prompt — a direct cross-tenant confidentiality leak (conversation content, potentially including PII, credentials, etc.).\n2. Writes into the same colliding row-set via save_messages(), whose eviction logic (DELETE FROM messages WHERE session_scope = ? AND id NOT IN (...)) can evict/interleave Tenant A's history — permanent cross-tenant data corruption, not just a read.\n\nReachable via the public Memory.add() / AsyncMemory.add() API, and by extension the REST server's POST /memories endpoint (which takes run_id/agent_id straight from the request body).\n\nReproduction\n\nfrom mem0.memory.main import _build_session_scope\n\n# Tenant A: real user_id + run_id\nscope_a = _build_session_scope({\"user_id\": \"alice\", \"run_id\": \"proj-x\"})\nprint(scope_a)  # \"run_id=proj-x&user_id=alice\"\n\n# Attacker: controls only run_id, embeds a fake user_id inside it\nscope_c = _build_session_scope({\"run_id\": \"proj-x&user_id=alice\"})\nprint(scope_c)  # \"run_id=proj-x&user_id=alice\"\n\nassert scope_a == scope_c  # byte-for-byte collision\n\nA full end-to-end proof (actual add() calls, no mocking of the vulnerable functions) confirms the attacker's subsequent add() call receives the victim's raw prior message content via get_last_messages.\n\nSuggested Fix\n\nReject & and = in _validate_and_trim_entity_id(), alongside the existing whitespace check — this is the single validation chokepoint already used by every entry point (add, search, get_all, delete_all), so the fix is centralized. Alternatively/additionally, encode each component unambiguously (percent-encode each id, or use a length-prefixed/JSON-encoded scope key) so no valid-character combination in one field can ever collide with a different partition.\n\nAlso affected: TypeScript SDK (npm mem0ai)\n\nThe same flaw exists in mem0-ts/src/oss/src/memory/index.ts,\nMemory.buildSessionScope(), which joins agent_id/run_id/user_id into the same\n`key=value&key=value` string with the delimiters unescaped:\n\n  private buildSessionScope(filters: SearchFilters): string {\n    const parts: string[] = [];\n    for (const key of [\"agent_id\", \"run_id\", \"user_id\"].sort()) {\n      const val = (filters as any)[key];\n      if (val) parts.push(`${key}=${val}`);\n    }\n    return parts.join(\"&\");\n  }\n\nThe scope string is used the same way, as an exact-match key into the messages\ntable via SQLiteManager.getLastMessages() and saveMessages(), so the identical\nforge, cross-tenant read and eviction all apply.\n\nVerified against the current releases as of 2026-07-26: pip mem0ai v2.0.14 and\nnpm mem0ai ts-v3.1.2 both still contain the unescaped join.\n\nReporter: Abhinav Singh (GitHub: OfficialAbhinavSingh)","state":0,"publishedAt":"2026-10-28T21:17:08+00:00","modifiedAt":"2026-10-28T21:30:05.8473569+00:00","affected":[{"ecosystem":"PyPI","name":"mem0ai","ranges":[{"type":"ECOSYSTEM","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"2.0.18"}]}],"versions":["0.0.1","0.0.10","0.0.11","0.0.12","0.0.13","0.0.14","0.0.15","0.0.16","0.0.17","0.0.18","0.0.19","0.0.2","0.0.20","0.0.20rc1","0.0.21","0.0.3","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9","0.1.0","0.1.1","0.1.10","0.1.100","0.1.101","0.1.102","0.1.103","0.1.104","0.1.106","0.1.107","0.1.107rc1","0.1.107rc2","0.1.108","0.1.109","0.1.11","0.1.110","0.1.111","0.1.112","0.1.113","0.1.114","0.1.115","0.1.116","0.1.117","0.1.118","0.1.12","0.1.13","0.1.14","0.1.15","0.1.16","0.1.17","0.1.18","0.1.19","0.1.2","0.1.20","0.1.20a0","0.1.21","0.1.22","0.1.23","0.1.24","0.1.25","0.1.26","0.1.27","0.1.28","0.1.29","0.1.3","0.1.30","0.1.31","0.1.32","0.1.33","0.1.34","0.1.35","0.1.36","0.1.37","0.1.38","0.1.39","0.1.4","0.1.40","0.1.41","0.1.42","0.1.43","0.1.44","0.1.45","0.1.45rc1","0.1.46","0.1.47","0.1.48","0.1.49","0.1.5","0.1.50","0.1.52","0.1.53","0.1.54","0.1.55","0.1.56","0.1.57","0.1.58","0.1.59","0.1.6","0.1.60","0.1.61","0.1.62","0.1.63","0.1.65","0.1.66","0.1.67","0.1.69","0.1.7","0.1.70","0.1.71","0.1.72","0.1.73","0.1.74","0.1.75","0.1.76","0.1.77","0.1.78","0.1.78a0","0.1.78a1","0.1.78a2","0.1.79","0.1.8","0.1.80","0.1.81","0.1.81a0","0.1.81a1","0.1.81a2","0.1.82","0.1.83","0.1.84","0.1.84a1","0.1.85","0.1.86","0.1.87","0.1.88","0.1.89","0.1.9","0.1.90","0.1.91","0.1.92","0.1.93","0.1.94","0.1.95","0.1.96","0.1.96rc1","0.1.97","0.1.98","0.1.99","0.1.99a0","0.1.99a1","0.1.99a2","0.1.99a3","1.0.0","1.0.0b0","1.0.1","1.0.10","1.0.11","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","2.0.0","2.0.0b0","2.0.0b1","2.0.0b2","2.0.1","2.0.10","2.0.11","2.0.12","2.0.13","2.0.14","2.0.15","2.0.16","2.0.17","2.0.2","2.0.3","2.0.4","2.0.5","2.0.6","2.0.7","2.0.8"],"rawVersionEvidence":null},{"ecosystem":"npm","name":"mem0ai","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"3.1.6"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/mem0ai/mem0/security/advisories/GHSA-5324-3w58-qpp2","https://nvd.nist.gov/vuln/detail/CVE-2026-116575","https://github.com/mem0ai/mem0/pull/6892","https://github.com/mem0ai/mem0/commit/71f2ebefa3494da21550fb525216818776cde67f","https://github.com/mem0ai/mem0","https://github.com/mem0ai/mem0/releases/tag/ts-v3.1.6","https://github.com/mem0ai/mem0/releases/tag/v2.0.18"],"sourceUrl":"https://github.com/advisories/GHSA-5324-3w58-qpp2","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-5324-3w58-qpp2","identifiers":["GHSA-5324-3w58-qpp2","CVE-2026-116575"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-30T16:47:08.0799506+00:00","complete":true,"records":1764,"lastAttemptAt":"2026-10-30T16:47:08.0799506+00:00","lastSuccessAt":"2026-10-30T16:47:08.2603913+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:47:08.2603923+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-7e2c4e7288f2c464ad8c61f3bb6017374714faedcc781283ac06a706ee1a820b-d7b222ecfb3b37ed75e1512e40fdcf28594abff2a8568f1274ffe87891d61caa","complete":true,"records":417228,"lastAttemptAt":"2026-10-30T16:45:32.5536592+00:00","lastSuccessAt":"2026-10-30T16:45:32.4100997+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:45:32.5536592+00:00","snapshotPublishedAt":"2026-10-30T16:25:31+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-95342c7cc17d4168bc976527a67e37f2","complete":false,"records":13527,"lastAttemptAt":"2026-10-30T16:52:10.1813764+00:00","lastSuccessAt":"2026-10-30T16:52:10.2382915+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:52:10.2382915+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-30T16:47:08.0799506+00:00","complete":true,"records":1094,"lastAttemptAt":"2026-10-30T16:47:08.0799506+00:00","lastSuccessAt":"2026-10-30T16:47:08.2616584+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:47:08.2616589+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-0f2f428ccf42491d9927e55626b8fc62","complete":false,"records":13545,"lastAttemptAt":"2026-10-30T16:47:33.5927086+00:00","lastSuccessAt":"2026-10-30T16:47:33.589+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:47:33.589+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13603,"expectedIdentifiers":13605,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-e091d9104aae1991994c868b5fad0248c9b288abafb7672fca8ae6500f97e080","complete":true,"records":9808,"lastAttemptAt":"2026-10-30T16:47:08.0351821+00:00","lastSuccessAt":"2026-10-30T15:47:48.5635219+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:47:08.0351821+00:00","snapshotPublishedAt":"2026-10-30T15:23:39+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-2de9ccc5290f060c7aa63f89c25dd76f655a64ba1aba7c05c97fa3bee4f09899","complete":true,"records":1967,"lastAttemptAt":"2026-10-30T16:45:32.7049631+00:00","lastSuccessAt":"2026-10-28T20:55:34.2982937+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:45:32.7049631+00:00","snapshotPublishedAt":"2026-10-28T20:03:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-f63fa91e750071ae88c9f82019ecd6c982fa4fcc1d3dc5ceb5d9c642fb6bf05c","complete":true,"records":3023,"lastAttemptAt":"2026-10-30T16:47:08.0492653+00:00","lastSuccessAt":"2026-10-30T11:49:36.4759887+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:47:08.0492653+00:00","snapshotPublishedAt":"2026-10-30T11:18:14+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-5a294989873fb9168d8ae38882a7a1451dd62315f67b71899ceec86dfb29d13b","complete":true,"records":230954,"lastAttemptAt":"2026-10-30T16:47:07.9043788+00:00","lastSuccessAt":"2026-10-30T16:47:06.5719002+00:00","error":null,"lastVerifiedAt":"2026-10-30T16:47:07.9043788+00:00","snapshotPublishedAt":"2026-10-30T16:33:30+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}