From 05a303cd3506b004392cdb0e28d877826d9b0128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Fri, 10 Apr 2026 21:52:46 +0300 Subject: [PATCH] feat(projects): add validation for name and description in PATCH request to ensure they are non-empty strings within specified length limits --- app/api/projects/[projectId]/route.ts | 17 +++++++++++++++++ .../videos/[videoId]/versions/route.ts | 9 +++++++++ app/api/workspaces/[workspaceId]/route.ts | 17 +++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/app/api/projects/[projectId]/route.ts b/app/api/projects/[projectId]/route.ts index edd6aa4..129b9c2 100644 --- a/app/api/projects/[projectId]/route.ts +++ b/app/api/projects/[projectId]/route.ts @@ -112,6 +112,23 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) { const body = await request.json(); const { name, description, visibility } = body; + if (name !== undefined) { + if (typeof name !== 'string' || name.trim().length === 0) { + return apiErrors.badRequest('Name must be a non-empty string'); + } + if (name.trim().length > 100) { + return apiErrors.badRequest('Name must be 100 characters or fewer'); + } + } + if (description !== undefined && description !== null) { + if (typeof description !== 'string') { + return apiErrors.badRequest('Description must be a string'); + } + if (description.trim().length > 1000) { + return apiErrors.badRequest('Description must be 1000 characters or fewer'); + } + } + const VALID_VISIBILITY = ['PRIVATE', 'INVITE', 'PUBLIC'] as const; if (visibility !== undefined && !VALID_VISIBILITY.includes(visibility)) { return apiErrors.badRequest('Invalid visibility value'); diff --git a/app/api/projects/[projectId]/videos/[videoId]/versions/route.ts b/app/api/projects/[projectId]/videos/[videoId]/versions/route.ts index eaa2b93..cbde43b 100644 --- a/app/api/projects/[projectId]/videos/[videoId]/versions/route.ts +++ b/app/api/projects/[projectId]/videos/[videoId]/versions/route.ts @@ -94,6 +94,15 @@ export async function POST(request: NextRequest, { params }: RouteParams) { return apiErrors.badRequest('Video URL is required'); } + if (versionLabel !== undefined && versionLabel !== null) { + if (typeof versionLabel !== 'string') { + return apiErrors.badRequest('Version label must be a string'); + } + if (versionLabel.trim().length > 100) { + return apiErrors.badRequest('Version label must be 100 characters or fewer'); + } + } + // Validate URLs use safe schemes (http/https only) const videoUrlError = validateUrl(videoUrl, 'Video URL'); if (videoUrlError) { diff --git a/app/api/workspaces/[workspaceId]/route.ts b/app/api/workspaces/[workspaceId]/route.ts index 04311bb..a821e28 100644 --- a/app/api/workspaces/[workspaceId]/route.ts +++ b/app/api/workspaces/[workspaceId]/route.ts @@ -108,6 +108,23 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) { const body = await request.json(); const { name, description } = body; + if (name !== undefined) { + if (typeof name !== 'string' || name.trim().length === 0) { + return apiErrors.badRequest('Name must be a non-empty string'); + } + if (name.trim().length > 100) { + return apiErrors.badRequest('Name must be 100 characters or fewer'); + } + } + if (description !== undefined && description !== null) { + if (typeof description !== 'string') { + return apiErrors.badRequest('Description must be a string'); + } + if (description.trim().length > 1000) { + return apiErrors.badRequest('Description must be 1000 characters or fewer'); + } + } + const updateData: Record = {}; if (name !== undefined) updateData.name = name.trim(); if (description !== undefined) updateData.description = description?.trim() || null;