fix: scope every channel chat-completion message_id to the channel, not just the first

#24725 added a channel-access + message-scoping gate to the chat_completion
`channel:` branch, but it validated only list(message_ids.values())[0]. A
multimodel request (message_ids with more than one entry) could pass a
permitted-channel id as the first value and a victim-channel message id as a
later value; the fan-out then drove _make_channel_emitter to overwrite the
victim message in a channel the caller cannot access.

Validate every message_ids value against the channel, and make
_make_channel_emitter fail closed so it only updates a message whose
channel_id matches the channel in chat_id.

Co-authored-by: DavidCarliez <271374756+DavidCarliez@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Classic298 2026-06-02 19:44:40 +02:00
parent 1a97751e37
commit f8782dd46a
2 changed files with 10 additions and 4 deletions

View file

@ -1834,8 +1834,10 @@ async def chat_completion(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.DEFAULT(),
)
target_message_id = list(message_ids.values())[0] if message_ids else None
if target_message_id:
# Every message_ids value must match the channel; first-only let a multimodel id slip past.
for target_message_id in (message_ids or {}).values():
if not target_message_id:
continue
target_message = await Messages.get_message_by_id(target_message_id)
if target_message and target_message.channel_id != channel.id:
raise HTTPException(

View file

@ -841,11 +841,15 @@ async def _make_channel_emitter(request_info):
async def _emit_channel_update(content: str, done: bool = False):
from open_webui.models.messages import MessageForm, Messages
# Fail closed: only update a message that actually belongs to this channel.
msg = await Messages.get_message_by_id(message_id)
if not msg or msg.channel_id != channel_id:
return
update_form = MessageForm(content=content)
if done:
# Merge done flag into existing meta (preserve model_id etc.)
msg = await Messages.get_message_by_id(message_id)
existing_meta = (msg.meta or {}) if msg else {}
existing_meta = msg.meta or {}
update_form = MessageForm(
content=content,
meta={**existing_meta, 'done': True},