From 8ddf80431f5b0efcf20f5c2bc601d2c3bc4e046f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 09:50:58 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20Fix=20Mass?= =?UTF-8?q?=20Assignment=20vulnerability=20in=20calendar=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the `saveNote` server action to explicitly list columns for `insert` and `update` operations. This prevents malicious users from overwriting protected fields like `userId` by providing unexpected keys in the JSON payload. - Removed object spreads (`...noteData`) in database calls. - Ensured `userId` is strictly taken from the authenticated session. - Added explicit mapping for allowed fields: `chatId`, `date`, `content`, `locationTags`, `userTags`, and `mapFeatureId`. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ lib/actions/calendar.ts | 20 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..a75814c0 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-05-22 - [Mass Assignment in Server Actions] +**Vulnerability:** Use of object spreads (...data) in Drizzle .set() and .values() methods within Next.js Server Actions. +**Learning:** Server Actions can receive any JSON payload from the client. Using spreads directly on these inputs can allow attackers to overwrite protected fields like 'userId' or 'id', leading to data corruption or privilege escalation. +**Prevention:** Always explicitly map allowed fields from client-provided objects when performing database operations in Server Actions. diff --git a/lib/actions/calendar.ts b/lib/actions/calendar.ts index d2e4dcf9..0b57e79b 100644 --- a/lib/actions/calendar.ts +++ b/lib/actions/calendar.ts @@ -73,7 +73,15 @@ export async function saveNote(noteData: NewCalendarNote | CalendarNote): Promis try { const [updatedNote] = await db .update(calendarNotes) - .set({ ...noteData, updatedAt: new Date() }) + .set({ + chatId: noteData.chatId, + date: noteData.date, + content: noteData.content, + locationTags: noteData.locationTags, + userTags: noteData.userTags, + mapFeatureId: noteData.mapFeatureId, + updatedAt: new Date() + }) .where(and(eq(calendarNotes.id, noteData.id), eq(calendarNotes.userId, userId))) .returning(); return updatedNote; @@ -86,7 +94,15 @@ export async function saveNote(noteData: NewCalendarNote | CalendarNote): Promis try { const [newNote] = await db .insert(calendarNotes) - .values({ ...noteData, userId }) + .values({ + userId: userId, + chatId: noteData.chatId, + date: noteData.date, + content: noteData.content, + locationTags: noteData.locationTags, + userTags: noteData.userTags, + mapFeatureId: noteData.mapFeatureId, + }) .returning(); if (newNote && newNote.chatId) {