diff --git a/src/app/api/referrals/route.test.ts b/src/app/api/referrals/route.test.ts index 3af21063..267f2640 100644 --- a/src/app/api/referrals/route.test.ts +++ b/src/app/api/referrals/route.test.ts @@ -254,4 +254,43 @@ describe("POST /api/referrals", () => { const body = await res.json(); expect(body.error).toContain("No valid email"); }); + + // --- Regression tests for #141: reject non-string email entries --- + + it("should return 400 for non-string email entries (e.g. number in array)", async () => { + mockGetAuthContext.mockResolvedValue({ + user: { id: "user1" }, + supabase: mockSupabase, + }); + + // Mixed array: valid string + number + const res = await POST(makePostRequest({ emails: ["friend@test.com", 42] })); + expect(res.status).toBe(400); + const body = await res.json(); + expect(body.error).toBe("All email entries must be strings"); + }); + + it("should return 400 for null in email array", async () => { + mockGetAuthContext.mockResolvedValue({ + user: { id: "user1" }, + supabase: mockSupabase, + }); + + const res = await POST(makePostRequest({ emails: [null] })); + expect(res.status).toBe(400); + const body = await res.json(); + expect(body.error).toBe("All email entries must be strings"); + }); + + it("should return 400 for object in email array", async () => { + mockGetAuthContext.mockResolvedValue({ + user: { id: "user1" }, + supabase: mockSupabase, + }); + + const res = await POST(makePostRequest({ emails: [{ email: "test@test.com" }] })); + expect(res.status).toBe(400); + const body = await res.json(); + expect(body.error).toBe("All email entries must be strings"); + }); }); diff --git a/src/app/api/referrals/route.ts b/src/app/api/referrals/route.ts index 50099975..0e577e9c 100644 --- a/src/app/api/referrals/route.ts +++ b/src/app/api/referrals/route.ts @@ -62,6 +62,13 @@ export async function POST(request: NextRequest) { ); } + if (!emails.every((email: unknown) => typeof email === "string")) { + return NextResponse.json( + { error: "All email entries must be strings" }, + { status: 400 } + ); + } + if (emails.length > 20) { return NextResponse.json( { error: "Maximum 20 invites at a time" },