diff --git a/backend/openapi/components/schemas/meet-room-options.yaml b/backend/openapi/components/schemas/meet-room-options.yaml index bf7603a..10a38ee 100644 --- a/backend/openapi/components/schemas/meet-room-options.yaml +++ b/backend/openapi/components/schemas/meet-room-options.yaml @@ -8,7 +8,7 @@ properties: After this date, the room will be automatically deleted and no new participants can join. - If this value is set and the date has already passed, the room is marked for deletion but will only be removed after the last + If this value is set and the date has already passed, the room is marked for deletion but will only be removed after the last participant leaves ("graceful deletion"). If this value is not defined, the room will exist indefinitely until manually deleted. @@ -17,6 +17,7 @@ properties: example: 'room' description: > A prefix to be used for the room ID. The room ID will be generated by concatenating this prefix with an unique identifier. + The maximum length of the room ID is 50 characters. # maxParticipants: # type: integer # example: 10 diff --git a/backend/src/middlewares/request-validators/room-validator.middleware.ts b/backend/src/middlewares/request-validators/room-validator.middleware.ts index 2860593..dc5d643 100644 --- a/backend/src/middlewares/request-validators/room-validator.middleware.ts +++ b/backend/src/middlewares/request-validators/room-validator.middleware.ts @@ -18,7 +18,7 @@ import INTERNAL_CONFIG from '../../config/internal-config.js'; * @param val The string to sanitize * @returns A sanitized string safe for use as an identifier */ -const sanitizeId = (val: string): string => { +const sanitizeRoomId = (val: string): string => { let transformed = val .trim() // Remove leading/trailing spaces .replace(/\s+/g, '') // Remove all spaces @@ -34,11 +34,12 @@ const sanitizeId = (val: string): string => { return transformed; }; -const nonEmptySanitizedString = (fieldName: string) => +export const nonEmptySanitizedRoomId = (fieldName: string) => z .string() .min(1, { message: `${fieldName} is required and cannot be empty` }) - .transform(sanitizeId) + .max(100, { message: `${fieldName} cannot exceed 100 characters` }) + .transform(sanitizeRoomId) .refine((data) => data !== '', { message: `${fieldName} cannot be empty after sanitization` }); @@ -83,7 +84,8 @@ const RoomRequestOptionsSchema: z.ZodType = z.object({ .optional(), roomIdPrefix: z .string() - .transform(sanitizeId) + .max(50, 'roomIdPrefix cannot exceed 50 characters') + .transform(sanitizeRoomId) .optional() .default(''), preferences: RoomPreferencesSchema.optional().default({ @@ -135,7 +137,7 @@ const BulkDeleteRoomsSchema = z.object({ // Pre-sanitize to check for duplicates that would become identical for (const id of roomIds) { - const transformed = sanitizeId(id); + const transformed = sanitizeRoomId(id); // Only add non-empty IDs if (transformed !== '') { @@ -190,7 +192,7 @@ export const withValidRoomPreferences = (req: Request, res: Response, next: Next }; export const withValidRoomId = (req: Request, res: Response, next: NextFunction) => { - const { success, error, data } = nonEmptySanitizedString('roomId').safeParse(req.params.roomId); + const { success, error, data } = nonEmptySanitizedRoomId('roomId').safeParse(req.params.roomId); if (!success) { return rejectRequest(res, error); @@ -213,7 +215,7 @@ export const withValidRoomBulkDeleteRequest = (req: Request, res: Response, next }; export const withValidRoomDeleteRequest = (req: Request, res: Response, next: NextFunction) => { - const roomIdResult = nonEmptySanitizedString('roomId').safeParse(req.params.roomId); + const roomIdResult = nonEmptySanitizedRoomId('roomId').safeParse(req.params.roomId); if (!roomIdResult.success) { return rejectRequest(res, roomIdResult.error); diff --git a/backend/tests/integration/api/rooms/create-room.test.ts b/backend/tests/integration/api/rooms/create-room.test.ts index 61a4d62..0be51b2 100644 --- a/backend/tests/integration/api/rooms/create-room.test.ts +++ b/backend/tests/integration/api/rooms/create-room.test.ts @@ -215,5 +215,17 @@ describe('OpenVidu Meet Room API Tests', () => { expect(response.body.error).toContain('Bad Request'); expect(response.body.message).toContain('Malformed Body'); }); + + it('should fail when roomIdPrefix is too long', async () => { + const longRoomId = 'a'.repeat(51); + const payload = { + roomIdPrefix: longRoomId, + autoDeletionDate: validAutoDeletionDate + }; + + const response = await request(app).post(ROOMS_PATH).set('Cookie', userCookie).send(payload).expect(422); + + expect(JSON.stringify(response.body.details)).toContain('roomIdPrefix cannot exceed 50 characters'); + }); }); });