diff --git a/src/app/api/crypto/public-keys/route.js b/src/app/api/crypto/public-keys/route.js index 618c7276..af47a3b5 100644 --- a/src/app/api/crypto/public-keys/route.js +++ b/src/app/api/crypto/public-keys/route.js @@ -14,6 +14,10 @@ function getServiceRoleClient() { return supabaseServiceRole; } +function normalizeUserId(userId) { + return typeof userId === 'string' ? userId.trim() : ''; +} + // Create regular client for JWT validation const supabaseClient = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY); @@ -105,7 +109,7 @@ export async function GET(request) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } - const userId = url.searchParams.get('user_id'); + const userId = normalizeUserId(url.searchParams.get('user_id')); if (!userId) { return NextResponse.json({ error: 'Missing user_id parameter' }, { status: 400 }); } diff --git a/src/app/api/crypto/public-keys/route.test.js b/src/app/api/crypto/public-keys/route.test.js index 277d25b7..59b88398 100644 --- a/src/app/api/crypto/public-keys/route.test.js +++ b/src/app/api/crypto/public-keys/route.test.js @@ -82,4 +82,21 @@ describe('public key cookie authentication', () => { expect(body).toEqual({ public_key: 'public-key', user_id: 'target-user-id' }); expect(mocks.authGetUser).toHaveBeenCalledWith('access-token'); }); + + it('rejects blank user ids before looking up a target public key', async () => { + const { GET } = await import('./route.js'); + const response = await GET( + new Request('https://qrypt.chat/api/crypto/public-keys?user_id=%20%20%20', { + headers: { + cookie: `sb-xydzwxwsbgmznthiiscl-auth-token=${cookieValue('access-token')}` + } + }) + ); + const body = await response.json(); + + expect(response.status).toBe(400); + expect(body).toEqual({ error: 'Missing user_id parameter' }); + expect(mocks.serviceFrom).toHaveBeenCalledTimes(1); + expect(mocks.rpc).not.toHaveBeenCalled(); + }); });