From a994c061ac7b508ee3226f5c2fe634ad5802c090 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 25 Jun 2026 17:11:08 +0100 Subject: [PATCH] fix: prevent cross-tenant comment deletion and edit --- packages/api/src/routers/card.ts | 47 ++++++++++++------- .../db/src/repository/cardComment.repo.ts | 1 + 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/api/src/routers/card.ts b/packages/api/src/routers/card.ts index 8b134a98..4855e007 100644 --- a/packages/api/src/routers/card.ts +++ b/packages/api/src/routers/card.ts @@ -8,20 +8,24 @@ import * as checklistRepo from "@kan/db/repository/checklist.repo"; import * as labelRepo from "@kan/db/repository/label.repo"; import * as listRepo from "@kan/db/repository/list.repo"; import * as workspaceRepo from "@kan/db/repository/workspace.repo"; +import { generateAttachmentUrl, generateAvatarUrl } from "@kan/shared/utils"; -import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc"; import { - cardCreateResponseSchema, - cardUpdateResponseSchema, - cardDetailSchema, - commentResponseSchema, - commentDeleteResponseSchema, activityItemSchema, + cardCreateResponseSchema, + cardDetailSchema, + cardUpdateResponseSchema, + commentDeleteResponseSchema, + commentResponseSchema, } from "../schemas"; +import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc"; import { mergeActivities } from "../utils/activities"; import { sendMentionEmails } from "../utils/notifications"; -import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions"; -import { generateAttachmentUrl, generateAvatarUrl } from "@kan/shared/utils"; +import { + assertCanDelete, + assertCanEdit, + assertPermission, +} from "../utils/permissions"; import { createCardWebhookPayload, sendWebhooksForWorkspace, @@ -246,7 +250,12 @@ export const cardRouter = createTRPCRouter({ code: "NOT_FOUND", }); - await assertPermission(ctx.db, userId, card.workspaceId, "comment:create"); + await assertPermission( + ctx.db, + userId, + card.workspaceId, + "comment:create", + ); const newComment = await cardCommentRepo.create(ctx.db, { comment: input.comment, @@ -324,7 +333,7 @@ export const cardRouter = createTRPCRouter({ input.commentPublicId, ); - if (!existingComment) + if (!existingComment || existingComment.cardId !== card.id) throw new TRPCError({ message: `Comment with public ID ${input.commentPublicId} not found`, code: "NOT_FOUND", @@ -412,7 +421,7 @@ export const cardRouter = createTRPCRouter({ input.commentPublicId, ); - if (!existingComment) + if (!existingComment || existingComment.cardId !== card.id) throw new TRPCError({ message: `Comment with public ID ${input.commentPublicId} not found`, code: "NOT_FOUND", @@ -901,10 +910,7 @@ export const cardRouter = createTRPCRouter({ | undefined; if (input.listPublicId) { - newList = await listRepo.getByPublicId( - ctx.db, - input.listPublicId, - ); + newList = await listRepo.getByPublicId(ctx.db, input.listPublicId); if (!newList) throw new TRPCError({ @@ -1048,12 +1054,14 @@ export const cardRouter = createTRPCRouter({ ) { webhookChanges.dueDate = { from: previousDueDate, to: input.dueDate }; } - const movedToNewList = Boolean(newListId && existingCard.listId !== newListId); + const movedToNewList = Boolean( + newListId && existingCard.listId !== newListId, + ); const currentWebhookListPublicId = movedToNewList ? input.listPublicId! : existingCard.list.publicId; const currentWebhookListName = movedToNewList - ? newList?.name ?? card.listName + ? (newList?.name ?? card.listName) : existingCard.list.name; if (movedToNewList) { @@ -1291,7 +1299,10 @@ export const cardRouter = createTRPCRouter({ if (input.copyLabels && sourceCard.labels?.length) { const labelPublicIds = sourceCard.labels.map((l) => l.publicId); - const labels = await labelRepo.getAllByPublicIds(ctx.db, labelPublicIds); + const labels = await labelRepo.getAllByPublicIds( + ctx.db, + labelPublicIds, + ); if (labels.length) { const labelsInsert = labels.map((label) => ({ cardId: newCard.id, diff --git a/packages/db/src/repository/cardComment.repo.ts b/packages/db/src/repository/cardComment.repo.ts index 31d4e5c9..526c2482 100644 --- a/packages/db/src/repository/cardComment.repo.ts +++ b/packages/db/src/repository/cardComment.repo.ts @@ -45,6 +45,7 @@ export const getByPublicId = (db: dbClient, publicId: string) => { publicId: true, comment: true, createdBy: true, + cardId: true, }, where: eq(comments.publicId, publicId), });