{"id":"CVE-2026-59215","aliases":["EUVD-2026-42630","GHSA-73x5-h92w-xc2j","PYSEC-2026-3591"],"status":"published","title":"Open WebUI: Private channel messages can be disclosed through cross-channel thread parent_id binding","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. Prior to 0.10.0, channel thread parent and reply handling did not bind parent_id to the channel in the URL, allowing an authenticated user to reference a message from another private or DM channel and disclose thread context across channels. This issue is fixed in version 0.10.0.","severity":{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00323,"percentile":0.25672,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-639"],"published":"2026-07-09T16:57:24Z","modified":"2026-10-28T02:30:32Z","fixes":[{"ecosystem":"PyPI","package":"open-webui","fixed_in":["0.10.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.10.0"}]}]},{"source":"osv","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.10.0"}]}],"versions":["0.1.124","0.1.125","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.3.0","0.3.1","0.3.10","0.3.12","0.3.13","0.3.14","0.3.15","0.3.16","0.3.17","0.3.17.dev2","0.3.17.dev3","0.3.17.dev4","0.3.17.dev5","0.3.18","0.3.19","0.3.2","0.3.20","0.3.21","0.3.22","0.3.23","0.3.24","0.3.25","0.3.26","0.3.27","0.3.27.dev1","0.3.27.dev2","0.3.27.dev3","0.3.28","0.3.29","0.3.3","0.3.30","0.3.30.dev1","0.3.30.dev2","0.3.31","0.3.31.dev1","0.3.32","0.3.33","0.3.33.dev1","0.3.34","0.3.35","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.0.dev1","0.4.0.dev2","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.6.dev1","0.4.7","0.4.8","0.5.0","0.5.0.dev1","0.5.0.dev2","0.5.1","0.5.10","0.5.11","0.5.12","0.5.13","0.5.14","0.5.15","0.5.16","0.5.17","0.5.18","0.5.19","0.5.2","0.5.20","0.5.3","0.5.3.dev1","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.12","0.6.13","0.6.14","0.6.15","0.6.16","0.6.18","0.6.19","0.6.2","0.6.20","0.6.21","0.6.22","0.6.23","0.6.24","0.6.25","0.6.26","0.6.26.dev1","0.6.27","0.6.28","0.6.29","0.6.3","0.6.30","0.6.31","0.6.32","0.6.33","0.6.34","0.6.35","0.6.36","0.6.37","0.6.38","0.6.39","0.6.4","0.6.40","0.6.41","0.6.42","0.6.43","0.6.5","0.6.6","0.6.6.dev1","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.8.0","0.8.1","0.8.10","0.8.11","0.8.12","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.8.7","0.8.8","0.8.9","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":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.10.0"}]}],"versions":["0.1.124","0.1.125","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.3.0","0.3.1","0.3.10","0.3.12","0.3.13","0.3.14","0.3.15","0.3.16","0.3.17","0.3.17.dev2","0.3.17.dev3","0.3.17.dev4","0.3.17.dev5","0.3.18","0.3.19","0.3.2","0.3.20","0.3.21","0.3.22","0.3.23","0.3.24","0.3.25","0.3.26","0.3.27","0.3.27.dev1","0.3.27.dev2","0.3.27.dev3","0.3.28","0.3.29","0.3.3","0.3.30","0.3.30.dev1","0.3.30.dev2","0.3.31","0.3.31.dev1","0.3.32","0.3.33","0.3.33.dev1","0.3.34","0.3.35","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.0.dev1","0.4.0.dev2","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.6.dev1","0.4.7","0.4.8","0.5.0","0.5.0.dev1","0.5.0.dev2","0.5.1","0.5.10","0.5.11","0.5.12","0.5.13","0.5.14","0.5.15","0.5.16","0.5.17","0.5.18","0.5.19","0.5.2","0.5.20","0.5.3","0.5.3.dev1","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.12","0.6.13","0.6.14","0.6.15","0.6.16","0.6.18","0.6.19","0.6.2","0.6.20","0.6.21","0.6.22","0.6.23","0.6.24","0.6.25","0.6.26","0.6.26.dev1","0.6.27","0.6.28","0.6.29","0.6.3","0.6.30","0.6.31","0.6.32","0.6.33","0.6.34","0.6.35","0.6.36","0.6.37","0.6.38","0.6.39","0.6.4","0.6.40","0.6.41","0.6.42","0.6.43","0.6.5","0.6.6","0.6.6.dev1","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.8.0","0.8.1","0.8.10","0.8.11","0.8.12","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.8.7","0.8.8","0.8.9","0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5","0.9.6"]}],"products":[{"vendor":"open-webui","product":"open-webui"},{"vendor":"openwebui","product":"open_webui"}],"references":[{"url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-73x5-h92w-xc2j","tags":["x_refsource_CONFIRM","exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/open-webui/open-webui/pull/25766","tags":["x_refsource_MISC","Issue Tracking","Patch","fix","web"]},{"url":"https://github.com/open-webui/open-webui/commit/a66477b7104c5d141ce7bffaea424b43e7666ef1","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/open-webui/open-webui/releases/tag/v0.10.0","tags":["x_refsource_MISC","Product","Release Notes","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-59215","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-73x5-h92w-xc2j","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59215.json","tags":["advisory"]},{"url":"https://github.com/open-webui/open-webui","tags":["package"]},{"url":"https://pypi.org/project/open-webui","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-59215","title":"Open WebUI: Private channel messages can be disclosed through cross-channel thread parent_id binding","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. Prior to 0.10.0, channel thread parent and reply handling did not bind parent_id to the channel in the URL, allowing an authenticated user to reference a message from another private or DM channel and disclose thread context across channels. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T16:57:24.325Z","modified":"2026-07-09T18:40:04.871Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low","assessor":"cna"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T14:19:58.560602Z"}],"euvd":[{"id":"EUVD-2026-42630","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. Prior to 0.10.0, channel thread parent and reply handling did not bind parent_id to the channel in the URL, allowing an authenticated user to reference a message from another private or DM channel and disclose thread context across channels. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T16:57:24Z","modified":"2026-07-09T18:40:04Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"updated_at":"2026-11-02T18:20:03.742277Z"}],"ghsa":[{"id":"GHSA-73x5-h92w-xc2j","title":"Open WebUI: Private channel messages can be disclosed through cross-channel thread parent_id binding","description":"## Summary\n\nA normal authenticated user can read the content of a message in a private channel they do not belong to. `GET /api/v1/channels/{id}/messages/{message_id}/thread` authorizes the caller against the URL channel, but the underlying thread lookup loads the thread *parent* by id and returns it without verifying the parent belongs to that channel. By requesting a thread in a channel they can access while supplying a victim channel's message id as the thread root, the attacker receives the victim message — content, channel id, and author.\n\n## Affected component\n\n- `backend/open_webui/models/messages.py` — `get_messages_by_parent_id()`\n- `backend/open_webui/routers/channels.py` — `get_channel_thread_messages()` (read), `new_message_handler()` (parent/reply binding on write)\n\n## Root cause\n\n`get_messages_by_parent_id(channel_id, parent_id)` filters the thread *replies* by `channel_id`, but loads the thread *parent* by id alone and appends it without requiring `parent.channel_id == channel_id`:\n\n```python\nmessage = await db.get(Message, parent_id)     # loaded by id only — no channel binding\nif not message:\n    return []\n# replies are filtered by channel_id ...\nif len(all_messages) \u003c limit:\n    all_messages.append(message)               # parent appended unconditionally\n```\n\n`get_channel_thread_messages()` authorizes only the URL channel, then calls `get_messages_by_parent_id(id, message_id)` with the caller-supplied `message_id`. The reply insert path (`new_message_handler` → `insert_new_message`) also stored a caller-supplied `parent_id` without binding it to the channel.\n\n## Impact\n\nA non-member can disclose the content (plus channel id and author metadata) of a private-channel message whose id they know or obtain. Direct reads of the victim channel/message/thread return 403; the disclosure is via the thread parent of a channel the attacker can access. Read-only, one message per known id.\n\n## Proof of Concept\n\n(reporter) Validated on v0.9.6: `GET /channels/{attacker_channel}/messages/{victim_message_id}/thread` returned the victim's private message — content, victim channel id, and author — although direct reads of the victim channel returned 403.\n\n## Fix\n\nBind the thread parent to the requested channel: `get_messages_by_parent_id` returns `[]` unless the parent exists and `parent.channel_id == channel_id`. Defence-in-depth on the write path: `new_message_handler` rejects a supplied `parent_id`/`reply_to_id` whose message does not belong to the URL channel.\n\n## Affected / Patched\n\n- Affected: `\u003c 0.10.0` (last affected release 0.9.6)\n- Patched: v0.10.0 (PR #25766). `get_messages_by_parent_id` binds the thread parent to the requested channel (returns `[]` unless `parent.channel_id == channel_id`), and `new_message_handler` rejects a caller-supplied `parent_id`/`reply_to_id` whose message does not belong to the channel.","status":"published","published":"2026-07-24T17:04:31Z","modified":"2026-07-24T17:04:32Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T14:40:52.46233Z"}],"nvd":[{"id":"CVE-2026-59215","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. Prior to 0.10.0, channel thread parent and reply handling did not bind parent_id to the channel in the URL, allowing an authenticated user to reference a message from another private or DM channel and disclose thread context across channels. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T17:17:02.34Z","modified":"2026-07-13T12:27:12.263Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low","assessor":"secondary"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T14:12:28.4135Z"}],"osv":[{"id":"CVE-2026-59215","title":"Open WebUI: Private channel messages can be disclosed through cross-channel thread parent_id binding","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. Prior to 0.10.0, channel thread parent and reply handling did not bind parent_id to the channel in the URL, allowing an authenticated user to reference a message from another private or DM channel and disclose thread context across channels. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T16:57:24.325Z","modified":"2026-10-28T02:30:32.823983629Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T15:45:12.496548Z"},{"id":"GHSA-73x5-h92w-xc2j","title":"Open WebUI: Private channel messages can be disclosed through cross-channel thread parent_id binding","description":"## Summary\n\nA normal authenticated user can read the content of a message in a private channel they do not belong to. `GET /api/v1/channels/{id}/messages/{message_id}/thread` authorizes the caller against the URL channel, but the underlying thread lookup loads the thread *parent* by id and returns it without verifying the parent belongs to that channel. By requesting a thread in a channel they can access while supplying a victim channel's message id as the thread root, the attacker receives the victim message — content, channel id, and author.\n\n## Affected component\n\n- `backend/open_webui/models/messages.py` — `get_messages_by_parent_id()`\n- `backend/open_webui/routers/channels.py` — `get_channel_thread_messages()` (read), `new_message_handler()` (parent/reply binding on write)\n\n## Root cause\n\n`get_messages_by_parent_id(channel_id, parent_id)` filters the thread *replies* by `channel_id`, but loads the thread *parent* by id alone and appends it without requiring `parent.channel_id == channel_id`:\n\n```python\nmessage = await db.get(Message, parent_id)     # loaded by id only — no channel binding\nif not message:\n    return []\n# replies are filtered by channel_id ...\nif len(all_messages) \u003c limit:\n    all_messages.append(message)               # parent appended unconditionally\n```\n\n`get_channel_thread_messages()` authorizes only the URL channel, then calls `get_messages_by_parent_id(id, message_id)` with the caller-supplied `message_id`. The reply insert path (`new_message_handler` → `insert_new_message`) also stored a caller-supplied `parent_id` without binding it to the channel.\n\n## Impact\n\nA non-member can disclose the content (plus channel id and author metadata) of a private-channel message whose id they know or obtain. Direct reads of the victim channel/message/thread return 403; the disclosure is via the thread parent of a channel the attacker can access. Read-only, one message per known id.\n\n## Proof of Concept\n\n(reporter) Validated on v0.9.6: `GET /channels/{attacker_channel}/messages/{victim_message_id}/thread` returned the victim's private message — content, victim channel id, and author — although direct reads of the victim channel returned 403.\n\n## Fix\n\nBind the thread parent to the requested channel: `get_messages_by_parent_id` returns `[]` unless the parent exists and `parent.channel_id == channel_id`. Defence-in-depth on the write path: `new_message_handler` rejects a supplied `parent_id`/`reply_to_id` whose message does not belong to the URL channel.\n\n## Affected / Patched\n\n- Affected: `\u003c 0.10.0` (last affected release 0.9.6)\n- Patched: v0.10.0 (PR #25766). `get_messages_by_parent_id` binds the thread parent to the requested channel (returns `[]` unless `parent.channel_id == channel_id`), and `new_message_handler` rejects a caller-supplied `parent_id`/`reply_to_id` whose message does not belong to the channel.","status":"published","published":"2026-07-24T17:04:31Z","modified":"2026-08-04T14:40:59.584276302Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T15:46:40.308826Z"},{"id":"PYSEC-2026-3591","title":"Open WebUI: Private channel messages can be disclosed through cross-channel thread parent_id binding","description":"## Summary\n\nA normal authenticated user can read the content of a message in a private channel they do not belong to. `GET /api/v1/channels/{id}/messages/{message_id}/thread` authorizes the caller against the URL channel, but the underlying thread lookup loads the thread *parent* by id and returns it without verifying the parent belongs to that channel. By requesting a thread in a channel they can access while supplying a victim channel's message id as the thread root, the attacker receives the victim message — content, channel id, and author.\n\n## Affected component\n\n- `backend/open_webui/models/messages.py` — `get_messages_by_parent_id()`\n- `backend/open_webui/routers/channels.py` — `get_channel_thread_messages()` (read), `new_message_handler()` (parent/reply binding on write)\n\n## Root cause\n\n`get_messages_by_parent_id(channel_id, parent_id)` filters the thread *replies* by `channel_id`, but loads the thread *parent* by id alone and appends it without requiring `parent.channel_id == channel_id`:\n\n```python\nmessage = await db.get(Message, parent_id)     # loaded by id only — no channel binding\nif not message:\n    return []\n# replies are filtered by channel_id ...\nif len(all_messages) \u003c limit:\n    all_messages.append(message)               # parent appended unconditionally\n```\n\n`get_channel_thread_messages()` authorizes only the URL channel, then calls `get_messages_by_parent_id(id, message_id)` with the caller-supplied `message_id`. The reply insert path (`new_message_handler` → `insert_new_message`) also stored a caller-supplied `parent_id` without binding it to the channel.\n\n## Impact\n\nA non-member can disclose the content (plus channel id and author metadata) of a private-channel message whose id they know or obtain. Direct reads of the victim channel/message/thread return 403; the disclosure is via the thread parent of a channel the attacker can access. Read-only, one message per known id.\n\n## Proof of Concept\n\n(reporter) Validated on v0.9.6: `GET /channels/{attacker_channel}/messages/{victim_message_id}/thread` returned the victim's private message — content, victim channel id, and author — although direct reads of the victim channel returned 403.\n\n## Fix\n\nBind the thread parent to the requested channel: `get_messages_by_parent_id` returns `[]` unless the parent exists and `parent.channel_id == channel_id`. Defence-in-depth on the write path: `new_message_handler` rejects a supplied `parent_id`/`reply_to_id` whose message does not belong to the URL channel.\n\n## Affected / Patched\n\n- Affected: `\u003c 0.10.0` (last affected release 0.9.6)\n- Patched: v0.10.0 (PR #25766). `get_messages_by_parent_id` binds the thread parent to the requested channel (returns `[]` unless `parent.channel_id == channel_id`), and `new_message_handler` rejects a caller-supplied `parent_id`/`reply_to_id` whose message does not belong to the channel.","status":"published","published":"2026-08-04T11:34:43.007012Z","modified":"2026-08-04T14:30:28.512935214Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"updated_at":"2026-11-02T15:52:56.847083Z"}]}}
