Skip to content

bug: updateCard applies title and linkIds updates in two separate, non-atomic operations — network/DB failure between them leaves card in a permanently inconsistent state #437

@hariom888

Description

@hariom888

Description:

apps/backend/src/services/cardService.ts updateCard() handles a combined { title, linkIds } request as two sequential, independent write operations:

if (body.title) {
  await app.prisma.card.update({ where: { id }, data: { title: body.title } })
}

if (body.linkIds) {
  await app.prisma.$transaction(async (tx) => {
    await tx.cardLink.deleteMany({ where: { cardId: id } })
    if (linkIds.length > 0) {
      await tx.cardLink.createMany({ ... })
    }
  })
}

The title update is committed to the database before the linkIds transaction begins. If the process crashes, the DB connection drops, or the linkIds transaction rolls back (e.g., a FK violation, a timeout, or the ownership check failing after the title has already been committed), the card will have its new title but retain the old set of links. The caller receives a 500 error but the title mutation is permanent — there is no rollback and no compensating write.

Additionally, the linkIds block itself first calls deleteMany then createMany inside the transaction, but the ownership check occurs before entering the transaction using a plain Prisma query outside tx. A TOCTOU window exists between that check and the createMany — another concurrent request could delete a platformLink between validation and use.

Affected file: apps/backend/src/services/cardService.ts

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions