diff --git a/src/api_ingests.ts b/src/api_ingests.ts index 1f50159..8a263e2 100644 --- a/src/api_ingests.ts +++ b/src/api_ingests.ts @@ -135,6 +135,9 @@ const apiIngests: FastifyPluginCallback = ( { schema: { description: 'Retrieves an ingest.', + params: Type.Object({ + ingestId: Type.String({ minLength: 1, pattern: '^[0-9]+$' }) + }), response: { 200: Ingest, 500: Type.String() @@ -175,6 +178,9 @@ const apiIngests: FastifyPluginCallback = ( schema: { description: 'Modify an existing Ingest. By changing the label, the deviceOutput or the deviceInput, the ingest is updated and the new ingest is returned.', + params: Type.Object({ + ingestId: Type.String({ minLength: 1, pattern: '^[0-9]+$' }) + }), body: PatchIngest, response: { 200: PatchIngestResponse, @@ -247,6 +253,9 @@ const apiIngests: FastifyPluginCallback = ( { schema: { description: 'Deletes a Ingest.', + params: Type.Object({ + ingestId: Type.String({ minLength: 1, pattern: '^[0-9]+$' }) + }), response: { 200: Type.String(), 500: Type.String() diff --git a/src/api_validation.test.ts b/src/api_validation.test.ts index d0120de..95fb43f 100644 --- a/src/api_validation.test.ts +++ b/src/api_validation.test.ts @@ -447,4 +447,53 @@ describe('Input Validation', () => { expect(response.statusCode).toBe(410); }); }); + + // ── Ingest :ingestId param validation (regression for #257) ──── + // Routes are 501-gated by a preHandler, but Fastify runs schema + // validation before preHandler, so a bad ingestId is rejected with + // 400 while a valid numeric id falls through to the 501 stub. + + describe('Ingest :ingestId param validation', () => { + test.each([ + ['non-numeric', 'abc'], + ['empty-ish', ' '], + ['float', '1.5'], + ['negative', '-1'], + ['special characters', 'id!@#'] + ])( + 'GET /ingest/:ingestId rejects %s ingestId with 400', + async (_label, badId) => { + const response = await server.inject({ + method: 'GET', + url: `/api/v1/ingest/${encodeURIComponent(badId)}` + }); + expect(response.statusCode).toBe(400); + } + ); + + test('PATCH /ingest/:ingestId rejects non-numeric ingestId with 400', async () => { + const response = await server.inject({ + method: 'PATCH', + url: '/api/v1/ingest/abc', + body: { label: 'valid-label' } + }); + expect(response.statusCode).toBe(400); + }); + + test('DELETE /ingest/:ingestId rejects non-numeric ingestId with 400', async () => { + const response = await server.inject({ + method: 'DELETE', + url: '/api/v1/ingest/abc' + }); + expect(response.statusCode).toBe(400); + }); + + test('GET /ingest/:ingestId with a valid numeric id passes validation (501, not 400)', async () => { + const response = await server.inject({ + method: 'GET', + url: '/api/v1/ingest/123' + }); + expect(response.statusCode).toBe(501); + }); + }); });