diff --git a/src/app/api/auth/verify-sms/route.js b/src/app/api/auth/verify-sms/route.js index abe732b..bb30c7f 100644 --- a/src/app/api/auth/verify-sms/route.js +++ b/src/app/api/auth/verify-sms/route.js @@ -60,6 +60,10 @@ export async function POST(request, { params } = {}) { return NextResponse.json({ error: 'Invalid request format' }, { status: 400 }); } + if (phoneNumber !== null && phoneNumber !== undefined && typeof phoneNumber !== 'string') { + return NextResponse.json({ error: 'Phone number must be a string' }, { status: 400 }); + } + logger.info('SMS verification request received', { phoneNumber: phoneNumber ? `${phoneNumber.substring(0, 3)}***${phoneNumber.substring(phoneNumber.length - 2)}` : null, hasCode: !!verificationCode, diff --git a/src/app/api/auth/verify-sms/route.test.js b/src/app/api/auth/verify-sms/route.test.js index 50c3cd2..1eace22 100644 --- a/src/app/api/auth/verify-sms/route.test.js +++ b/src/app/api/auth/verify-sms/route.test.js @@ -90,6 +90,21 @@ describe('verify-sms session phone binding', () => { expect(mocks.getUser).not.toHaveBeenCalled(); }); + it('rejects non-string phone numbers before session validation', async () => { + const { POST } = await import('./route.js'); + const res = await POST(sessionRequest({ + useSession: true, + phoneNumber: 15559999999, + username: 'alice' + })); + const body = await res.json(); + + expect(res.status).toBe(400); + expect(body.error).toBe('Phone number must be a string'); + expect(mocks.serverClient).not.toHaveBeenCalled(); + expect(mocks.getUser).not.toHaveBeenCalled(); + }); + it('rejects anonymous / phoneless sessions', async () => { mocks.getUser.mockResolvedValue({ data: { user: { id: 'anon', phone: null } },