From d2b0a7995620d434c927f35038821a1b0516c82e Mon Sep 17 00:00:00 2001 From: jsdavid278-cyber Date: Sat, 13 Jun 2026 18:18:17 -0600 Subject: [PATCH] Handle malformed notification settings JSON --- .../api/notification-settings/route.test.ts | 39 +++++++++++++++++++ src/app/api/notification-settings/route.ts | 17 +++++++- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/app/api/notification-settings/route.test.ts diff --git a/src/app/api/notification-settings/route.test.ts b/src/app/api/notification-settings/route.test.ts new file mode 100644 index 00000000..f89c67dc --- /dev/null +++ b/src/app/api/notification-settings/route.test.ts @@ -0,0 +1,39 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { NextRequest } from "next/server"; +import { PUT } from "./route"; + +const mocks = vi.hoisted(() => ({ + mockGetAuthContext: vi.fn(), + mockFrom: vi.fn(), +})); + +vi.mock("@/lib/auth/get-user", () => ({ + getAuthContext: mocks.mockGetAuthContext, +})); + +function makePutRequest(body: BodyInit) { + return new NextRequest("http://localhost/api/notification-settings", { + method: "PUT", + body, + headers: { "Content-Type": "application/json" }, + }); +} + +describe("PUT /api/notification-settings", () => { + beforeEach(() => { + vi.clearAllMocks(); + mocks.mockGetAuthContext.mockResolvedValue({ + user: { id: "user-1" }, + supabase: { from: mocks.mockFrom }, + }); + }); + + it("returns 400 for malformed JSON before touching settings", async () => { + const response = await PUT(makePutRequest("{not valid json")); + const body = await response.json(); + + expect(response.status).toBe(400); + expect(body.error).toBe("Invalid JSON body"); + expect(mocks.mockFrom).not.toHaveBeenCalled(); + }); +}); diff --git a/src/app/api/notification-settings/route.ts b/src/app/api/notification-settings/route.ts index 346f32aa..45d5882f 100644 --- a/src/app/api/notification-settings/route.ts +++ b/src/app/api/notification-settings/route.ts @@ -14,6 +14,18 @@ const SETTING_KEYS = [ "email_upvote_milestone", ] as const; +async function readJsonObject(request: NextRequest) { + try { + const body = await request.json(); + if (!body || typeof body !== "object" || Array.isArray(body)) { + return null; + } + return body as Record; + } catch { + return null; + } +} + // GET /api/notification-settings export async function GET(request: NextRequest) { try { @@ -60,7 +72,10 @@ export async function PUT(request: NextRequest) { } const { user, supabase } = auth; - const body = await request.json(); + const body = await readJsonObject(request); + if (!body) { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + } // Only allow known boolean keys const updateData: Record = {};