From 925d1a459329ec9ce397bf5b05bdb6843ad87e5a Mon Sep 17 00:00:00 2001 From: Leander Furumo Date: Sat, 31 Oct 2026 01:42:13 +0100 Subject: [PATCH] Validate the guess request body Co-Authored-By: Claude Opus 5.5 --- src/app/api/guess/route.ts | 34 +++++++++++++++++++++++++++++--- src/app/components/GuessForm.tsx | 1 + 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/app/api/guess/route.ts b/src/app/api/guess/route.ts index a5a664a..b55f9d3 100644 --- a/src/app/api/guess/route.ts +++ b/src/app/api/guess/route.ts @@ -3,15 +3,43 @@ import { findDiscordMember } from "@/lib/members"; import { getServerSession } from "next-auth/next"; import { NextResponse } from "next/server"; +const MAX_INPUT_LENGTH = 100; +const MAX_GUESSED_IDS = 10_000; + +function parseGuess(body: unknown): { input: string; guessedIds: string[] } | null { + if (typeof body !== "object" || body === null) { + return null; + } + + const { input, guessedIds } = body as Record; + + if (typeof input !== "string" || !input.trim() || input.length > MAX_INPUT_LENGTH) { + return null; + } + if ( + !Array.isArray(guessedIds) || + guessedIds.length > MAX_GUESSED_IDS || + !guessedIds.every((id) => typeof id === "string") + ) { + return null; + } + + return { input, guessedIds }; +} + export async function POST(request: Request) { const session = await getServerSession(authOptions); if (!session) { - return new Response("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const guess = parseGuess(await request.json().catch(() => null)); + if (!guess) { + return NextResponse.json({ error: "Invalid request body" }, { status: 400 }); } - const { input, guessedIds } = await request.json(); try { - return NextResponse.json(await findDiscordMember(input, guessedIds)); + return NextResponse.json(await findDiscordMember(guess.input, guess.guessedIds)); } catch (error) { console.error("Error finding Discord member:", error); return NextResponse.json({ error: "Failed to look up member" }, { status: 503 }); diff --git a/src/app/components/GuessForm.tsx b/src/app/components/GuessForm.tsx index ac8430b..f5515af 100644 --- a/src/app/components/GuessForm.tsx +++ b/src/app/components/GuessForm.tsx @@ -35,6 +35,7 @@ const GuessForm: React.FC = ({ input, onInputChange, onSubmit, d type="text" value={input} onChange={onInputChange} + maxLength={100} className={`focus:border-accent-primary h-12 w-60 rounded border px-2 py-1 focus:outline-none md:w-75 ${ disabled || loading ? "cursor-not-allowed border-gray-500 bg-gray-400 opacity-50"