{"id":"CVE-2026-44504","aliases":["EUVD-2026-30322","GHSA-m98r-6667-4wq7","PYSEC-2026-2328"],"status":"published","title":"Aegra: Cross-user run injection in /threads/{thread_id}/runs (IDOR)","description":"Aegra is a drop-in replacement for LangSmith Deployments. Prior to 0.9.7, with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated attacker, given another user's thread_id, can execute graph runs against the user's thread, read the user's full checkpoint state, and inject arbitrary messages into the user's conversation history. This vulnerability is fixed in 0.9.7.","severity":{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00285,"percentile":0.21291,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-285","CWE-639"],"published":"2026-05-07T01:49:51Z","modified":"2026-10-28T02:31:01Z","fixes":[{"ecosystem":"PyPI","package":"aegra-api","fixed_in":["0.9.7"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"aegra-api","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.9.7"}]}]},{"source":"osv","ecosystem":"PyPI","package":"aegra-api","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.9.7"}]}],"versions":["0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5","0.9.6"]},{"source":"osv","ecosystem":"PyPI","package":"aegra-api","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.9.7"}]}],"versions":["0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5","0.9.6"]}],"products":[{"vendor":"aegra","product":"aegra"}],"references":[{"url":"https://github.com/aegra/aegra/security/advisories/GHSA-m98r-6667-4wq7","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/aegra/aegra/issues/336","tags":["web"]},{"url":"https://github.com/aegra/aegra/pull/337","tags":["web"]},{"url":"https://github.com/aegra/aegra/commit/e1b2042254fd49072ca281bc35b3f2a3bed74b31","tags":["web"]},{"url":"https://github.com/aegra/aegra/releases/tag/v0.9.7","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44504","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-m98r-6667-4wq7","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44504.json","tags":["advisory"]},{"url":"https://github.com/aegra/aegra","tags":["package"]},{"url":"https://pypi.org/project/aegra-api","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-44504","title":"Aegra: Cross-user run injection in /threads/{thread_id}/runs (IDOR)","description":"Aegra is a drop-in replacement for LangSmith Deployments. Prior to 0.9.7, with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated attacker, given another user's thread_id, can execute graph runs against the user's thread, read the user's full checkpoint state, and inject arbitrary messages into the user's conversation history. This vulnerability is fixed in 0.9.7.","status":"published","published":"2026-05-14T15:52:30.547Z","modified":"2026-05-16T00:38:24.939Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high","assessor":"cna"}],"cwes":["CWE-285","CWE-639"],"updated_at":"2026-11-02T13:43:00.288193Z"}],"euvd":[{"id":"EUVD-2026-30322","description":"Aegra is a drop-in replacement for LangSmith Deployments. Prior to 0.9.7, with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated attacker, given another user's thread_id, can execute graph runs against the user's thread, read the user's full checkpoint state, and inject arbitrary messages into the user's conversation history. This vulnerability is fixed in 0.9.7.","status":"published","published":"2026-05-14T15:52:30Z","modified":"2026-05-16T00:38:24Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T19:04:27.676923Z"}],"ghsa":[{"id":"GHSA-m98r-6667-4wq7","title":"Aegra has cross-user run injection in /threads/{thread_id}/runs (IDOR)","description":"## Impact\n\nAegra deployments running 0.9.0 through 0.9.6 with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated user (User A), given another user's `thread_id` (User B), can:\n\n- Execute graph runs against User B's thread via `POST /threads/{thread_id}/runs`, `POST /threads/{thread_id}/runs/stream`, or `POST /threads/{thread_id}/runs/wait`\n- Read User B's full checkpoint state via the resulting run's `output` field\n- Inject arbitrary messages into User B's conversation history (persisted in B's checkpoint)\n- Hide their activity from User B's `GET /threads/{thread_id}/runs` listing because the run carries A's `user_id`\n\nThe streaming variant is worse — the first SSE `event: values` frame returns the entire prior `messages` array immediately on connection, no graph execution needed.\n\nThread IDs are UUIDs but leak through frontend URLs, server logs, observability traces, and shared links. Guessing is not required.\n\n## Patches\n\nFixed in **0.9.7**. The three affected endpoints now perform an SQL-level `user_id == authenticated_user.identity` check before calling `_prepare_run`. When the thread exists but is owned by another user, the response is `404 Thread not found` (matching the read-side pattern) to avoid leaking thread existence.\n\n## Workarounds\n\nIf upgrade is not immediately possible, register an `@auth.on(\"threads\", \"create_run\")` handler that explicitly verifies thread ownership against the authenticated identity before allowing the operation. Without a handler, no built-in authorization runs on these write paths.\n\nExample mitigation handler:\n\n```python\nfrom langgraph_sdk import Auth\n\nauth = Auth()\n\n@auth.on(\"threads\", \"create_run\")\nasync def enforce_thread_owner(ctx: Auth.types.AuthContext, value: dict):\n    # Look up the thread, raise 404 if not owned by ctx.user.identity.\n    # Implementation depends on your data layer.\n    ...\n```\n\n## Root cause\n\nAegra's authorization model delegates per-resource policy to user-defined `@auth.on` handlers. When no handler is registered, `handle_event(...)` returns `None` and the request proceeds (default-allow). Read endpoints in `api/threads.py` add a defense-in-depth `user_id` filter at the SQL layer, but the run-creation endpoints in `api/runs.py` skipped that filter. Result: out-of-the-box deployments without custom auth handlers were vulnerable.\n\n## Affected endpoints\n\n- `POST /threads/{thread_id}/runs`\n- `POST /threads/{thread_id}/runs/stream`\n- `POST /threads/{thread_id}/runs/wait`\n\nStateless variants (`POST /runs`, `POST /runs/wait`, `POST /runs/stream`) are NOT affected — they generate a fresh `thread_id` server-side and never accept a caller-supplied one.\n\n## Credits\n\n- @JoJoTheBizarre — discovered and reported the vulnerability with a precise reproducer (#336)\n- @victorjmarin and @jawhardjebbi — wrote the fix and added test coverage at unit, integration, and manual-auth e2e levels (#337)\n\n## Resources\n\n- Issue: https://github.com/aegra/aegra/issues/336\n- Fix PR: https://github.com/aegra/aegra/pull/337\n- Release: https://github.com/aegra/aegra/releases/tag/v0.9.7","status":"published","published":"2026-05-07T01:49:51Z","modified":"2026-05-14T20:53:22Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-285","CWE-639"],"updated_at":"2026-11-02T14:40:10.269075Z"}],"nvd":[{"id":"CVE-2026-44504","description":"Aegra is a drop-in replacement for LangSmith Deployments. Prior to 0.9.7, with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated attacker, given another user's thread_id, can execute graph runs against the user's thread, read the user's full checkpoint state, and inject arbitrary messages into the user's conversation history. This vulnerability is fixed in 0.9.7.","status":"published","published":"2026-05-14T16:16:24.38Z","modified":"2026-06-17T10:50:44.373Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"}],"cwes":["CWE-285","CWE-639"],"updated_at":"2026-11-02T14:12:19.020465Z"}],"osv":[{"id":"CVE-2026-44504","title":"Aegra: Cross-user run injection in /threads/{thread_id}/runs (IDOR)","description":"Aegra is a drop-in replacement for LangSmith Deployments. Prior to 0.9.7, with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated attacker, given another user's thread_id, can execute graph runs against the user's thread, read the user's full checkpoint state, and inject arbitrary messages into the user's conversation history. This vulnerability is fixed in 0.9.7.","status":"published","published":"2026-05-14T15:52:30.547Z","modified":"2026-10-28T02:31:01.518233863Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-285","CWE-639"],"updated_at":"2026-11-02T15:45:04.820169Z"},{"id":"GHSA-m98r-6667-4wq7","title":"Aegra has cross-user run injection in /threads/{thread_id}/runs (IDOR)","description":"## Impact\n\nAegra deployments running 0.9.0 through 0.9.6 with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated user (User A), given another user's `thread_id` (User B), can:\n\n- Execute graph runs against User B's thread via `POST /threads/{thread_id}/runs`, `POST /threads/{thread_id}/runs/stream`, or `POST /threads/{thread_id}/runs/wait`\n- Read User B's full checkpoint state via the resulting run's `output` field\n- Inject arbitrary messages into User B's conversation history (persisted in B's checkpoint)\n- Hide their activity from User B's `GET /threads/{thread_id}/runs` listing because the run carries A's `user_id`\n\nThe streaming variant is worse — the first SSE `event: values` frame returns the entire prior `messages` array immediately on connection, no graph execution needed.\n\nThread IDs are UUIDs but leak through frontend URLs, server logs, observability traces, and shared links. Guessing is not required.\n\n## Patches\n\nFixed in **0.9.7**. The three affected endpoints now perform an SQL-level `user_id == authenticated_user.identity` check before calling `_prepare_run`. When the thread exists but is owned by another user, the response is `404 Thread not found` (matching the read-side pattern) to avoid leaking thread existence.\n\n## Workarounds\n\nIf upgrade is not immediately possible, register an `@auth.on(\"threads\", \"create_run\")` handler that explicitly verifies thread ownership against the authenticated identity before allowing the operation. Without a handler, no built-in authorization runs on these write paths.\n\nExample mitigation handler:\n\n```python\nfrom langgraph_sdk import Auth\n\nauth = Auth()\n\n@auth.on(\"threads\", \"create_run\")\nasync def enforce_thread_owner(ctx: Auth.types.AuthContext, value: dict):\n    # Look up the thread, raise 404 if not owned by ctx.user.identity.\n    # Implementation depends on your data layer.\n    ...\n```\n\n## Root cause\n\nAegra's authorization model delegates per-resource policy to user-defined `@auth.on` handlers. When no handler is registered, `handle_event(...)` returns `None` and the request proceeds (default-allow). Read endpoints in `api/threads.py` add a defense-in-depth `user_id` filter at the SQL layer, but the run-creation endpoints in `api/runs.py` skipped that filter. Result: out-of-the-box deployments without custom auth handlers were vulnerable.\n\n## Affected endpoints\n\n- `POST /threads/{thread_id}/runs`\n- `POST /threads/{thread_id}/runs/stream`\n- `POST /threads/{thread_id}/runs/wait`\n\nStateless variants (`POST /runs`, `POST /runs/wait`, `POST /runs/stream`) are NOT affected — they generate a fresh `thread_id` server-side and never accept a caller-supplied one.\n\n## Credits\n\n- @JoJoTheBizarre — discovered and reported the vulnerability with a precise reproducer (#336)\n- @victorjmarin and @jawhardjebbi — wrote the fix and added test coverage at unit, integration, and manual-auth e2e levels (#337)\n\n## Resources\n\n- Issue: https://github.com/aegra/aegra/issues/336\n- Fix PR: https://github.com/aegra/aegra/pull/337\n- Release: https://github.com/aegra/aegra/releases/tag/v0.9.7","status":"published","published":"2026-05-07T01:49:51Z","modified":"2026-07-13T16:42:39.928198607Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-285","CWE-639"],"updated_at":"2026-11-02T15:46:51.463834Z"},{"id":"PYSEC-2026-2328","title":"Aegra has cross-user run injection in /threads/{thread_id}/runs (IDOR)","description":"## Impact\n\nAegra deployments running 0.9.0 through 0.9.6 with multiple authenticated users on a shared instance are vulnerable to a cross-tenant IDOR. Any authenticated user (User A), given another user's `thread_id` (User B), can:\n\n- Execute graph runs against User B's thread via `POST /threads/{thread_id}/runs`, `POST /threads/{thread_id}/runs/stream`, or `POST /threads/{thread_id}/runs/wait`\n- Read User B's full checkpoint state via the resulting run's `output` field\n- Inject arbitrary messages into User B's conversation history (persisted in B's checkpoint)\n- Hide their activity from User B's `GET /threads/{thread_id}/runs` listing because the run carries A's `user_id`\n\nThe streaming variant is worse — the first SSE `event: values` frame returns the entire prior `messages` array immediately on connection, no graph execution needed.\n\nThread IDs are UUIDs but leak through frontend URLs, server logs, observability traces, and shared links. Guessing is not required.\n\n## Patches\n\nFixed in **0.9.7**. The three affected endpoints now perform an SQL-level `user_id == authenticated_user.identity` check before calling `_prepare_run`. When the thread exists but is owned by another user, the response is `404 Thread not found` (matching the read-side pattern) to avoid leaking thread existence.\n\n## Workarounds\n\nIf upgrade is not immediately possible, register an `@auth.on(\"threads\", \"create_run\")` handler that explicitly verifies thread ownership against the authenticated identity before allowing the operation. Without a handler, no built-in authorization runs on these write paths.\n\nExample mitigation handler:\n\n```python\nfrom langgraph_sdk import Auth\n\nauth = Auth()\n\n@auth.on(\"threads\", \"create_run\")\nasync def enforce_thread_owner(ctx: Auth.types.AuthContext, value: dict):\n    # Look up the thread, raise 404 if not owned by ctx.user.identity.\n    # Implementation depends on your data layer.\n    ...\n```\n\n## Root cause\n\nAegra's authorization model delegates per-resource policy to user-defined `@auth.on` handlers. When no handler is registered, `handle_event(...)` returns `None` and the request proceeds (default-allow). Read endpoints in `api/threads.py` add a defense-in-depth `user_id` filter at the SQL layer, but the run-creation endpoints in `api/runs.py` skipped that filter. Result: out-of-the-box deployments without custom auth handlers were vulnerable.\n\n## Affected endpoints\n\n- `POST /threads/{thread_id}/runs`\n- `POST /threads/{thread_id}/runs/stream`\n- `POST /threads/{thread_id}/runs/wait`\n\nStateless variants (`POST /runs`, `POST /runs/wait`, `POST /runs/stream`) are NOT affected — they generate a fresh `thread_id` server-side and never accept a caller-supplied one.\n\n## Credits\n\n- @JoJoTheBizarre — discovered and reported the vulnerability with a precise reproducer (#336)\n- @victorjmarin and @jawhardjebbi — wrote the fix and added test coverage at unit, integration, and manual-auth e2e levels (#337)\n\n## Resources\n\n- Issue: https://github.com/aegra/aegra/issues/336\n- Fix PR: https://github.com/aegra/aegra/pull/337\n- Release: https://github.com/aegra/aegra/releases/tag/v0.9.7","status":"published","published":"2026-07-13T15:15:40.572514Z","modified":"2026-07-13T16:31:30.415106896Z","cvss":[{"version":"4.0","score":8.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T15:52:55.537808Z"}]}}
