---
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.26033
ingestedAt: '2026-07-13T18:58:00.496Z'
---

## Overview

## Impact

Aegra 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:

- 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`
- Read User B's full checkpoint state via the resulting run's `output` field
- Inject arbitrary messages into User B's conversation history (persisted in B's checkpoint)
- Hide their activity from User B's `GET /threads/{thread_id}/runs` listing because the run carries A's `user_id`

The streaming variant is worse — the first SSE `event: values` frame returns the entire prior `messages` array immediately on connection, no graph execution needed.

Thread IDs are UUIDs but leak through frontend URLs, server logs, observability traces, and shared links. Guessing is not required.

## Patches

Fixed 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.

## Workarounds

If 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.

Example mitigation handler:

```python
from langgraph_sdk import Auth

auth = Auth()

@auth.on("threads", "create_run")
async def enforce_thread_owner(ctx: Auth.types.AuthContext, value: dict):
    # Look up the thread, raise 404 if not owned by ctx.user.identity.
    # Implementation depends on your data layer.
    ...
```

## Root cause

Aegra'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.

## Affected endpoints

- `POST /threads/{thread_id}/runs`
- `POST /threads/{thread_id}/runs/stream`
- `POST /threads/{thread_id}/runs/wait`

Stateless 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.

## Credits

- @JoJoTheBizarre — discovered and reported the vulnerability with a precise reproducer (#336)
- @victorjmarin and @jawhardjebbi — wrote the fix and added test coverage at unit, integration, and manual-auth e2e levels (#337)

## Resources

- Issue: https://github.com/aegra/aegra/issues/336
- Fix PR: https://github.com/aegra/aegra/pull/337
- Release: https://github.com/aegra/aegra/releases/tag/v0.9.7

## Affected packages

- `aegra-api >= 0.9.0, < 0.9.7`

## Remediation

Upgrade to a patched release:

- `aegra-api 0.9.7`
