From c0b4f7f66ee3903d4c8b16448b4dc417813b5790 Mon Sep 17 00:00:00 2001 From: jesxion Date: Sat, 25 Apr 2026 17:34:30 +0800 Subject: [PATCH] fix: tighten project api validation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- server/src/routes/platform-projects.ts | 16 ++++++++++++++-- server/tests/platform-projects.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/server/src/routes/platform-projects.ts b/server/src/routes/platform-projects.ts index 0c8c19b..9ae8d17 100644 --- a/server/src/routes/platform-projects.ts +++ b/server/src/routes/platform-projects.ts @@ -10,8 +10,16 @@ import { const router = Router(); const projectIdSchema = z.string().transform((val, ctx) => { + // Strict integer validation: only digits, no leading zeros (except "0" itself) + if (!/^\d+$/.test(val) || (val.length > 1 && val[0] === '0')) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Project ID must be a positive integer', + }); + return z.NEVER; + } const parsed = parseInt(val, 10); - if (isNaN(parsed) || parsed <= 0) { + if (parsed <= 0) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Project ID must be a positive integer', @@ -22,7 +30,11 @@ const projectIdSchema = z.string().transform((val, ctx) => { }); const createProjectSchema = z.object({ - name: z.string().min(1, 'Project name is required'), + name: z.string() + .min(1, 'Project name is required') + .refine((val) => val.trim().length > 0, { + message: 'Project name cannot be only whitespace', + }), description: z.string().optional(), }); diff --git a/server/tests/platform-projects.test.ts b/server/tests/platform-projects.test.ts index 7ad95d6..dd278b9 100644 --- a/server/tests/platform-projects.test.ts +++ b/server/tests/platform-projects.test.ts @@ -166,4 +166,26 @@ describe('Platform Projects API', () => { expect(response.body).toHaveProperty('error'); expect(typeof response.body.error).toBe('string'); }); + + it('rejects whitespace-only project name', async () => { + const response = await request(app) + .post('/v1/platform/projects') + .set('Authorization', `Bearer ${platformToken}`) + .send({ name: ' ' }) + .expect(400); + + expect(response.body).toHaveProperty('error'); + expect(typeof response.body.error).toBe('string'); + expect(response.body.error).toContain('whitespace'); + }); + + it('rejects malformed project ID like 123abc', async () => { + const response = await request(app) + .get('/v1/platform/projects/123abc') + .set('Authorization', `Bearer ${platformToken}`) + .expect(400); + + expect(response.body).toHaveProperty('error'); + expect(typeof response.body.error).toBe('string'); + }); });