diff options
| -rw-r--r-- | docs/dev/roadmap.md | 2 | ||||
| -rw-r--r-- | src/server/repositories/sync.ts | 9 |
2 files changed, 6 insertions, 5 deletions
diff --git a/docs/dev/roadmap.md b/docs/dev/roadmap.md index d877d78..ed45ea6 100644 --- a/docs/dev/roadmap.md +++ b/docs/dev/roadmap.md @@ -197,7 +197,7 @@ Smaller features first to enable early MVP validation. - [x] Configure CORS middleware ### Medium Priority -- [ ] Fix card update authorization in sync push (verify existing card ownership) +- [x] Fix card update authorization in sync push (verify existing card ownership) - [ ] Unify password length requirement (add-user.ts: 8 chars → 15 chars) ### Low Priority diff --git a/src/server/repositories/sync.ts b/src/server/repositories/sync.ts index a1b6648..3e9f8ed 100644 --- a/src/server/repositories/sync.ts +++ b/src/server/repositories/sync.ts @@ -171,18 +171,18 @@ export const syncRepository: SyncRepository = { for (const cardData of data.cards) { const clientUpdatedAt = new Date(cardData.updatedAt); - // Verify deck belongs to user + // Verify target deck belongs to user const deckCheck = await db .select({ id: decks.id }) .from(decks) .where(and(eq(decks.id, cardData.deckId), eq(decks.userId, userId))); if (deckCheck.length === 0) { - // Deck doesn't belong to user, skip + // Target deck doesn't belong to user, skip continue; } - // Check if card exists + // Check if card exists AND belongs to user (via deck ownership) const existing = await db .select({ id: cards.id, @@ -190,7 +190,8 @@ export const syncRepository: SyncRepository = { syncVersion: cards.syncVersion, }) .from(cards) - .where(eq(cards.id, cardData.id)); + .innerJoin(decks, eq(cards.deckId, decks.id)) + .where(and(eq(cards.id, cardData.id), eq(decks.userId, userId))); if (existing.length === 0) { // New card - insert |
