{"id":"PYSEC-2026-2622","summary":"mcp-memory-service: OAuth read-only clients can write and delete memories through MCP tools/call","details":"## Summary\n\nThe HTTP MCP JSON-RPC endpoint at `/mcp` requires only OAuth `read` scope for all requests, then dispatches `tools/call` directly to handlers that include mutating tools. A read-only OAuth client can call `store_memory` and `delete_memory` through MCP even though the corresponding REST endpoints require `write` scope.\n\n## Technical Details\n\n`src/mcp_memory_service/web/api/mcp.py` declares `mcp_endpoint` with `user: AuthenticationResult = Depends(require_read_access)`. For `tools/call`, it extracts the requested tool name and arguments, then calls `handle_tool_call(storage, tool_name, arguments)` without passing the authenticated user or checking a per-tool required scope.\n\nThe MCP tool registry includes both read tools and write tools. In the same handler file, `store_memory` creates a `Memory` object and calls `storage.store(...)`, while `delete_memory` calls `storage.delete(content_hash)`. These operations are reachable with only the `read` scope.\n\nThe REST endpoint demonstrates the intended boundary: `POST /api/memories` uses `Depends(require_write_access)` and rejects a read-only token with 403 `insufficient_scope`.\n\n## Reproduction\n\n1. Enable OAuth and disable anonymous access.\n2. Generate a valid OAuth JWT with only `scope: read`.\n3. Confirm the REST write endpoint rejects it:\n\n```http\nPOST /api/memories\nAuthorization: Bearer \u003cread-only-token\u003e\nContent-Type: application/json\n\n{\"content\":\"rest denied control\"}\n```\n\nExpected and observed: HTTP 403 with `Required scope 'write' not granted`.\n\n4. Send the same read-only token to the MCP endpoint:\n\n```http\nPOST /mcp\nAuthorization: Bearer \u003cread-only-token\u003e\nContent-Type: application/json\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"store_memory\",\n    \"arguments\": {\n      \"content\": \"mcp read scope stored this\",\n      \"tags\": [\"poc\"]\n    }\n  }\n}\n```\n\nObserved: HTTP 200 JSON-RPC success and the storage `store` sink is reached.\n\n5. A read-only token can also call `delete_memory` through MCP if it knows a content hash:\n\n```http\nPOST /mcp\nAuthorization: Bearer \u003cread-only-token\u003e\nContent-Type: application/json\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 2,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"delete_memory\",\n    \"arguments\": {\"content_hash\": \"\u003cknown_hash\u003e\"}\n  }\n}\n```\n\nObserved: HTTP 200 JSON-RPC success and the storage `delete` sink is reached.\n\n## Impact\n\nA client intended to be read-only can inject or delete memories through the MCP API. This can corrupt the memory database, influence future agent context, and destroy stored user memories without the OAuth `write` scope required by the REST API.\n\n## Affected Versions\n\nConfirmed present on current main commit `c99a922477df41f75a44db11182ae48a57311910` and latest release tag `v10.65.0` (`4eb4a62665589f9dd9f8c393afa32de434b4098a`).\n\n## Suggested Fix\n\nEnforce authorization per MCP tool at `tools/call` time. Require `write` for `store_memory` and `delete_memory`, keep `read` only for read-only tools, and add regression tests proving direct `tools/call` to mutating tools is rejected before the handler reaches storage when the caller has only `read` scope.","aliases":["CVE-2026-49291","GHSA-2r68-g678-7qr3"],"modified":"2026-07-13T16:32:13.084434734Z","published":"2026-07-13T15:46:25.132643Z","references":[{"type":"WEB","url":"https://github.com/doobidoo/mcp-memory-service/security/advisories/GHSA-2r68-g678-7qr3"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-49291"},{"type":"PACKAGE","url":"https://github.com/doobidoo/mcp-memory-service"},{"type":"WEB","url":"https://pypi.org/project/mcp-memory-service/10.65.3"},{"type":"WEB","url":"https://web.archive.org/web/20260508112116/https://github.com/doobidoo/mcp-memory-service"},{"type":"PACKAGE","url":"https://pypi.org/project/mcp-memory-service"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-2r68-g678-7qr3"}],"affected":[{"package":{"name":"mcp-memory-service","ecosystem":"PyPI","purl":"pkg:pypi/mcp-memory-service"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"10.65.3"}]}],"versions":["10.0.0","10.0.1","10.0.2","10.0.3","10.1.0","10.1.1","10.1.2","10.10.0","10.10.1","10.10.2","10.10.3","10.10.4","10.10.5","10.10.6","10.11.0","10.11.1","10.11.2","10.12.0","10.12.1","10.13.0","10.13.1","10.13.2","10.14.0","10.15.0","10.15.1","10.16.0","10.16.1","10.17.0","10.17.10","10.17.12","10.17.13","10.17.14","10.17.15","10.17.16","10.17.2","10.17.3","10.17.4","10.17.5","10.17.6","10.17.7","10.17.8","10.17.9","10.18.0","10.18.1","10.18.2","10.18.3","10.19.0","10.2.0","10.2.1","10.20.0","10.20.1","10.20.2","10.20.4","10.20.5","10.20.6","10.21.0","10.21.1","10.22.0","10.23.0","10.24.0","10.25.0","10.25.1","10.25.2","10.25.3","10.26.0","10.26.1","10.26.2","10.26.3","10.26.4","10.26.5","10.26.7","10.26.8","10.26.9","10.27.0","10.28.0","10.28.1","10.28.2","10.28.3","10.28.4","10.28.5","10.29.0","10.29.1","10.3.0","10.30.0","10.31.0","10.31.1","10.31.2","10.32.0","10.33.0","10.34.0","10.35.0","10.36.0","10.36.1","10.36.2","10.36.3","10.36.4","10.36.5","10.36.6","10.36.7","10.36.8","10.37.0","10.38.0","10.38.1","10.38.2","10.38.3","10.38.4","10.39.0","10.39.1","10.4.0","10.4.1","10.4.2","10.4.3","10.4.4","10.4.5","10.4.6","10.40.0","10.40.1","10.40.2","10.40.3","10.40.4","10.41.0","10.42.0","10.42.1","10.43.0","10.44.0","10.45.0","10.45.1","10.46.0","10.47.0","10.47.1","10.47.2","10.48.0","10.49.0","10.49.1","10.49.2","10.49.3","10.49.4","10.5.0","10.5.1","10.50.0","10.51.0","10.51.1","10.51.2","10.51.3","10.52.0","10.53.0","10.54.0","10.55.0","10.55.1","10.55.2","10.56.0","10.56.1","10.56.2","10.56.3","10.57.0","10.57.1","10.57.2","10.57.3","10.58.0","10.59.0","10.59.1","10.59.2","10.6.0","10.6.1","10.60.0","10.60.1","10.60.2","10.61.0","10.62.0","10.63.0","10.64.0","10.64.1","10.64.2","10.65.0","10.65.1","10.7.0","10.7.1","10.7.2","10.8.0","10.9.0","8.24.0","8.25.1","8.25.2","8.26.0","8.27.0","8.27.1","8.27.2","8.28.0","8.28.1","8.29.0","8.30.0","8.31.0","8.32.0","8.33.0","8.34.0","8.35.0","8.36.0","8.36.1","8.37.0","8.38.0","8.38.1","8.39.0","8.39.1","8.40.0","8.41.0","8.41.1","8.41.2","8.42.0","8.42.1","8.43.0","8.44.0","8.45.0","8.45.1","8.45.2","8.45.3","8.46.0","8.46.1","8.46.2","8.46.3","8.47.0","8.47.1","8.48.0","8.48.1","8.48.2","8.48.3","8.48.4","8.49.0","8.50.0","8.50.1","8.51.0","8.52.0","8.52.1","8.52.2","8.53.0","8.54.0","8.54.1","8.54.2","8.54.3","8.54.4","8.55.0","8.57.1","8.58.0","8.59.0","8.60.0","8.61.0","8.61.1","8.61.2","8.62.0","8.62.1","8.62.10","8.62.11","8.62.12","8.62.13","8.62.2","8.62.3","8.62.4","8.62.5","8.62.6","8.62.7","8.62.8","8.62.9","8.63.0","8.63.1","8.64.0","8.65.0","8.66.0","8.67.0","8.68.0","8.68.1","8.68.2","8.69.0","8.70.0","8.71.0","8.72.0","8.73.0","8.74.0","8.75.0","8.75.1","8.76.0","9.0.0","9.0.1","9.0.2","9.0.3","9.0.4","9.0.5","9.0.6","9.2.0","9.2.1","9.3.1"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/mcp-memory-service/PYSEC-2026-2622.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H"}]}