-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Description
Issue Summary
When an event type is configured with both "Requires Confirmation" and a Duration Limit (e.g. "Max 1 hour per week"), the duration limit check completely ignores bookings in PENDING status. Because unconfirmed bookings are excluded from the tally, attendees can accumulate multiple pending bookings in the same period whose combined duration far exceeds the configured cap — and all of them get confirmed when the host processes them, silently violating the limit.
This is a silent correctness bug: no error is shown at any point. Hosts only discover the problem after they have already over-confirmed, meaning they end up working more hours than their limit was meant to protect. The same issue applies to the booking count limits (checkBookingLimits) for the same reason.
Steps to Reproduce
- Log in to cal.com (or a self-hosted instance on
main). - Go to Event Types → create or edit an event type with the following settings:
- Duration: 30 minutes
- Requires confirmation: ON
- Duration limit: Max 1 hour per week
- As an attendee (use a different browser or incognito), book three separate 30-minute slots within the same calendar week — for example Monday, Wednesday, and Friday at different times.
- All three bookings land in
PENDINGstatus (awaiting host confirmation).
- All three bookings land in
- As the same attendee, attempt to book a fourth 30-minute slot in the same week.
- The booking is accepted — it should have been blocked (3 × 30 min = 90 min already exceeds the 60-min limit).
- As the host, go to Bookings and confirm all four pending bookings.
- All four are confirmed: total = 2 hours, which violates the 1-hour/week limit.
Actual Results
- The 4th (and any further) booking attempt succeeds when it should be rejected.
- The duration limit check queries existing bookings filtered by
status: ACCEPTEDonly —PENDINGbookings are invisible to it. - After the host confirms all pending bookings, the actual confirmed total exceeds the configured duration limit with no error raised at any stage.
- There is no warning to the host at confirmation time that accepting these bookings would put them over limit.
Expected Results
-
PENDINGbookings represent reserved time that has not yet been formally released — they should count toward the duration limit the same wayACCEPTEDbookings do. -
When a new booking attempt would cause the combined total of
ACCEPTED+PENDINGbookings in the period to exceed the configured duration limit, the request should be rejected with a clear message to the attendee:"The maximum bookable duration of 1 hour for this week has been reached. Please try a different week."
-
The fix is narrow and low-risk: include
BookingStatus.PENDINGalongsideBookingStatus.ACCEPTEDin the status filter used bycheckDurationLimits(andcheckBookingLimits):
// Current (broken):
status: { in: [BookingStatus.ACCEPTED] }
// Fixed:
status: { in: [BookingStatus.ACCEPTED, BookingStatus.PENDING] }Technical Details
- Affected surface: Booking page (attendee-facing) +
handleNewBookingserver action - Relevant source file:
packages/lib/server/checkBookingLimits.ts- The
checkDurationLimitsfunction builds a Prisma query filtering onstatus: { in: [BookingStatus.ACCEPTED] }. The same pattern exists in thecheckBookingLimitsfunction (count-based limits).
- The
- Why this only manifests with "Requires Confirmation": Without confirmation, all bookings go directly to
ACCEPTEDand are counted correctly. The gap only opens when bookings sit inPENDINGlong enough for a second attendee (or the same attendee) to make another booking before the host confirms. - Secondary impact: A malicious attendee aware of this behaviour could intentionally saturate a host's calendar with pending bookings, consuming all available capacity for a period without any of them being confirmed.
- Node.js version: Reproducible on Node.js 18.x and 20.x.
Evidence
Reproduced on a self-hosted main branch instance (Docker Compose). Configured a 30-minute event type with "Requires Confirmation: ON" and "Max 1 hour per week" duration limit. Booked three 30-minute slots in the same week as an attendee — all three created as PENDING (verified in Prisma Studio). Attempted a fourth booking in the same week — it succeeded with no error.
Then tested the same event type with "Requires Confirmation: OFF" (bookings go straight to ACCEPTED): the third booking (which would exceed 60 minutes) was correctly blocked, confirming that the duration limit logic works for confirmed bookings but breaks when the PENDING status is in play.