fix: add guest limits and rate limiting to booking-guests endpoint (#27494)

* fix: add guest limits and rate limiting to booking-guests endpoint

- Add ArrayMaxSize(10) validation to limit guests per request to 10
- Add aggressive rate limiting (5 requests/minute) via @Throttle decorator
- Add total guest limit check (max 30 guests per booking) to prevent abuse
- Update API documentation to reflect new limits

This prevents scammers from using the endpoint to send spam emails
to hundreds of guests through our system.

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* docs: update openapi.json with guest limits and rate limiting info

Co-Authored-By: morgan@cal.com <morgan@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Morgan
2026-02-02 11:49:25 +02:00
committed by GitHub
co-authored by morgan@cal.com <morgan@cal.com> morgan@cal.com <morgan@cal.com> Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent f91511b927
commit d29c8a4fa2
4 changed files with 33 additions and 5 deletions
@@ -3,6 +3,7 @@ import { AddGuestsOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/add
import { BookingGuestsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/booking-guests.service";
import { VERSION_2024_08_13_VALUE, VERSION_2024_08_13 } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { Throttle } from "@/lib/endpoint-throttler-decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -35,10 +36,19 @@ export class BookingGuestsController_2024_08_13 {
@HttpCode(HttpStatus.OK)
@Permissions([BOOKING_WRITE])
@UseGuards(ApiAuthGuard, BookingUidGuard)
@Throttle({
limit: 5,
ttl: 60000,
blockDuration: 60000,
name: "booking_guests_add",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary: "Add guests to an existing booking",
description: `Add one or more guests to an existing booking.
description: `Add one or more guests to an existing booking. Maximum 10 guests per request, with a limit of 30 total guests per booking.
**Rate Limiting:**
This endpoint is rate limited to 5 requests per minute to prevent abuse.
**Email Notifications:**
When guests are added, the following notifications are sent (unless disabled by event type settings):
@@ -2,11 +2,13 @@ import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/reposito
import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service";
import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service";
import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy";
import { Injectable, Logger, HttpException, NotFoundException } from "@nestjs/common";
import { Injectable, Logger, HttpException, NotFoundException, BadRequestException } from "@nestjs/common";
import { addGuestsHandler } from "@calcom/platform-libraries/bookings";
import type { AddGuestsInput_2024_08_13 } from "@calcom/platform-types";
const MAX_TOTAL_GUESTS_PER_BOOKING = 30;
@Injectable()
export class BookingGuestsService_2024_08_13 {
private readonly logger = new Logger("BookingGuestsService_2024_08_13");
@@ -23,6 +25,18 @@ export class BookingGuestsService_2024_08_13 {
throw new NotFoundException(`Booking with uid ${bookingUid} not found`);
}
const currentGuestCount = booking.attendees.length;
const newGuestCount = input.guests.length;
const totalGuestCount = currentGuestCount + newGuestCount;
if (totalGuestCount > MAX_TOTAL_GUESTS_PER_BOOKING) {
const remainingSlots = Math.max(0, MAX_TOTAL_GUESTS_PER_BOOKING - currentGuestCount);
throw new BadRequestException(
`Cannot add ${newGuestCount} guests. This booking already has ${currentGuestCount} attendees. ` +
`Maximum total guests allowed is ${MAX_TOTAL_GUESTS_PER_BOOKING}. You can add up to ${remainingSlots} more guests.`
);
}
const platformClientParams = booking.eventTypeId
? await this.platformBookingsService.getOAuthClientParams(booking.eventTypeId)
: undefined;