{"id":"CVE-2026-44504","aliases":["GHSA-m98r-6667-4wq7","PYSEC-2026-2328"],"title":"Aegra has cross-user run injection in /threads/{thread_id}/runs (IDOR)","summary":"Aegra has cross-user run injection in /threads/{thread_id}/runs (IDOR)","severity":"high","vendor":"aegra-api","product":"aegra-api","ecosystem":"pip","affected":["aegra-api >= 0.9.0, < 0.9.7"],"patched":["aegra-api 0.9.7"],"published":"2026-05-07","updated":"2026-07-13","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-m98r-6667-4wq7","references":[{"url":"https://github.com/aegra/aegra/security/advisories/GHSA-m98r-6667-4wq7"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44504"},{"url":"https://github.com/aegra/aegra/issues/336"},{"url":"https://github.com/aegra/aegra/pull/337"},{"url":"https://github.com/aegra/aegra/commit/e1b2042254fd49072ca281bc35b3f2a3bed74b31"},{"url":"https://github.com/aegra/aegra"},{"url":"https://github.com/aegra/aegra/releases/tag/v0.9.7"}],"tags":["osv","pip"],"epss":0.00351,"epssPercentile":0.25928,"ingestedAt":"2026-07-13T18:58:00.496Z","slug":"CVE-2026-44504","body":"## Overview\n\n## 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\n\n## Affected packages\n\n- `aegra-api >= 0.9.0, < 0.9.7`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `aegra-api 0.9.7`","depth":"twilight","depthScore":41,"depthScoreParts":{"impact":41.3,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}