fix: bind channel thread parent/reply to the URL channel (#25766)

GET /api/v1/channels/{id}/messages/{message_id}/thread authorized only the URL channel, but get_messages_by_parent_id() appended the thread parent (loaded by id) without checking it belonged to that channel, so a caller could read a message from a channel they cannot access by passing its id as the thread root. Require the parent to be in the requested channel before returning it, and reject a posted parent_id/reply_to_id that does not belong to the channel.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Classic298
2026-06-29 10:46:50 +02:00
committed by GitHub
parent 7b1aa749eb
commit a66477b710
2 changed files with 9 additions and 1 deletions

View File

@@ -328,7 +328,8 @@ class MessageTable:
async with get_async_db_context(db) as db:
message = await db.get(Message, parent_id)
if not message:
# Thread parent must belong to the requested channel; never disclose a foreign-channel message.
if not message or message.channel_id != channel_id:
return []
result = await db.execute(

View File

@@ -1129,6 +1129,13 @@ async def new_message_handler(request: Request, id: str, form_data: MessageForm,
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())
# Thread parent / reply target must belong to this channel (no cross-channel binding).
for ref_id in (form_data.parent_id, form_data.reply_to_id):
if ref_id:
ref = await Messages.get_message_by_id(ref_id, include_thread_replies=False, db=db)
if not ref or ref.channel_id != channel.id:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT())
try:
message = await Messages.insert_new_message(form_data, channel.id, user.id, db=db)
if message: