Group-capacity slots — classes, workshops, shared resources — require a count-against-capacity check, not just a slot-existence check. Without it, 30 attendees can book a room with a capacity of 10, because the system only asked "is the slot taken?" rather than "is the slot full?". OWASP A04 (Insecure Design) applies: the domain model includes a capacity constraint that the server never enforces. Overbooking forces refunds, damages customer trust, and requires manual communication with every affected attendee when the overbooking is discovered.
Medium because the defect allows overbooking of capacity-constrained resources, causing real-world operational failures and refunds, but does not expose sensitive data.
Compare the live booking count against slot.maxCapacity in your server-side availability check (e.g., src/lib/booking-service.ts). This must run server-side on every booking creation path.
const currentCount = await db.booking.count({
where: { slotId: requestedSlotId, status: { not: 'CANCELLED' } }
});
const slot = await db.slot.findUniqueOrThrow({ where: { id: requestedSlotId } });
if (currentCount >= slot.maxCapacity) {
throw new CapacityError('Slot is at capacity');
}
ID: booking-flow-lifecycle.booking-creation.overbooking-prevention
Label: Overbooking prevention logic
Severity: medium
What to look for: If the system allows multiple bookings per slot (e.g., a class with capacity 10, a group booking), look for count < capacity logic in the availability check. Count all booking creation paths and verify each one includes a capacity comparison. Locate the maxCapacity, capacity, or max_attendees field in the slot/event model. Enumerate: "X of Y booking paths enforce capacity limits."
Pass criteria: The availability check explicitly compares current booking count against the maximum capacity. A booking is only allowed if count < max_capacity. The capacity field must be stored per-slot or per-event (not globally hardcoded). At least 1 capacity comparison must exist in every booking creation path. Report: "X of Y booking paths enforce capacity limits against [capacity_field] in [model]."
Fail criteria: No capacity check exists, or capacity is hardcoded as 1 and not configurable, or the check is missing in at least 1 code path. Do NOT pass when capacity is only checked on the frontend — server-side enforcement is required.
Skip (N/A) when: The system is strictly 1:1 (one booking per slot) and enforces this via a unique constraint on (slotId) or (slotId, startTime) in the database schema.
Detail on fail: "No capacity check found. The system allows unlimited bookings for an event with max_capacity = 10."
Cross-reference: The conflict-check in this category covers basic availability; this check extends it to capacity-aware scenarios.
Cross-reference: The waitlist-handling check in Conflict Prevention verifies what happens when capacity is reached.
Cross-reference: The concurrent-request-handling check in Conflict Prevention ensures capacity checks are race-condition safe.
Remediation: Enforce capacity limits in the availability check. Add this in your booking service (e.g., src/lib/booking-service.ts) or API handler.
const currentCount = await db.booking.count({
where: { slotId: requestedSlotId, status: { not: 'CANCELLED' } }
});
const slot = await db.slot.findUnique({ where: { id: slotId } });
if (currentCount >= slot.maxCapacity) {
throw new Error("Slot is at capacity");
}