diff --git a/src/app/api/webhooks/[id]/route.js b/src/app/api/webhooks/[id]/route.js index d66a7e25..56213e1e 100644 --- a/src/app/api/webhooks/[id]/route.js +++ b/src/app/api/webhooks/[id]/route.js @@ -5,11 +5,16 @@ import { withAuth } from '@/lib/api/middleware/auth.js'; export const DELETE = withAuth(async (event) => { const { supabase, user } = event.locals; const { id } = (await event.context?.params) || {}; + const webhookId = typeof id === 'string' ? id.trim() : ''; + + if (!webhookId) { + return NextResponse.json({ error: 'Webhook id is required' }, { status: 400 }); + } const { error } = await supabase .from('webhooks') .delete() - .eq('id', id) + .eq('id', webhookId) .eq('user_id', user.id); if (error) { diff --git a/src/app/api/webhooks/[id]/route.test.js b/src/app/api/webhooks/[id]/route.test.js index b8699493..952ae087 100644 --- a/src/app/api/webhooks/[id]/route.test.js +++ b/src/app/api/webhooks/[id]/route.test.js @@ -22,6 +22,19 @@ vi.mock('@/lib/api/middleware/auth.js', () => ({ })); describe('DELETE /api/webhooks/[id]', () => { + it('rejects blank webhook ids before deleting', async () => { + const { DELETE } = await import('./route.js'); + const response = await DELETE(new Request('https://qrypt.chat/api/webhooks/%20'), { + params: Promise.resolve({ id: ' ' }) + }); + const body = await response.json(); + + expect(response.status).toBe(400); + expect(body).toEqual({ error: 'Webhook id is required' }); + expect(mocks.deleteMock).not.toHaveBeenCalled(); + expect(mocks.eqMock).not.toHaveBeenCalled(); + }); + it('reads the webhook id from route context params', async () => { mocks.eqMock.mockReturnThis(); mocks.deleteMock.mockReturnValue({ eq: mocks.eqMock });