Duration validation that exists only in the UI is bypassed by any direct API call. OWASP A03 (Injection) and CWE-20 (Improper Input Validation) both flag trust in client-supplied numeric fields. Without server-side bounds, an attacker or misbehaving client can create a 1-minute booking (flooding the schedule with noise) or a 48-hour booking (occupying a resource for two days). Both have direct revenue impact. Explicit named constants for min and max durations are required so the bounds are testable, auditable, and not silently scattered across the codebase.
High because absent server-side duration bounds allow direct API callers to create arbitrarily short or long bookings, causing resource monopolization and schedule flooding without any exploit skills.
Define MIN_DURATION_MINUTES and MAX_DURATION_MINUTES as named server-side constants and validate them at the top of the booking API handler before touching the database.
// src/app/api/book/route.ts
const MIN_DURATION_MINUTES = 30
const MAX_DURATION_MINUTES = 480
export async function POST(req: Request) {
const { startTime, endTime } = await req.json()
const duration = (new Date(endTime).getTime() - new Date(startTime).getTime()) / 60_000
if (duration < MIN_DURATION_MINUTES) {
return Response.json(
{ error: 'Booking too short', detail: `Minimum is ${MIN_DURATION_MINUTES} min` },
{ status: 400 }
)
}
if (duration > MAX_DURATION_MINUTES) {
return Response.json(
{ error: 'Booking too long', detail: `Maximum is ${MAX_DURATION_MINUTES} min` },
{ status: 400 }
)
}
// ... create booking
}
ID: booking-calendar-availability.availability-logic.duration-validation
Severity: high
What to look for: Count all booking creation API endpoints (e.g., POST /api/book, POST /api/appointments). For each, search for validation logic that checks duration bounds. Before evaluating, extract and quote the exact min/max duration constants or configuration values (e.g., MIN_DURATION_MINUTES = 30, MAX_DURATION_MINUTES = 480). Verify the API returns a structured error response (JSON with error field and HTTP 400 status) when duration is out of bounds. Examine files matching src/app/api/book*/route.ts, src/app/api/appointment*/route.ts, src/lib/*validation*, src/lib/*booking*.
Pass criteria: Count all booking API endpoints. 100% of booking creation endpoints must validate duration server-side with explicit min and max bounds. Both min and max constants must be defined (not hardcoded inline). The API must return a structured JSON error response (HTTP 400) when duration is below minimum or above maximum. Report: "Min duration: [value]. Max duration: [value]. X of Y endpoints validate."
Fail criteria: Duration validation exists only in the UI, or no validation at all, or only one bound (min or max) is checked.
Do NOT pass when: Duration validation exists in client-side code (e.g., form validation in a React component) but not in the API route handler. Client-side validation alone is insufficient.
Skip (N/A) when: Never — duration validation is essential to prevent abuse.
Detail on fail: Example: "UI enforces 30-min minimum, but the API accepts any duration. A direct POST with 5-minute duration succeeds."
Cross-reference: Check buffer-enforcement — duration and buffer validations often share the same API middleware layer.
Cross-reference: Check same-day-cutoff — all server-side booking time validations should be co-located.
Cross-reference: Check advance-booking-limits — another server-side time boundary that must be validated alongside duration.
Remediation: Validate duration server-side:
// pages/api/appointments.ts
const MIN_DURATION_MINUTES = 30
const MAX_DURATION_MINUTES = 480 // 8 hours
export async function POST(req: Request) {
const { startTime, endTime, resourceId } = await req.json()
const durationMinutes =
(new Date(endTime).getTime() - new Date(startTime).getTime()) / (1000 * 60)
if (durationMinutes < MIN_DURATION_MINUTES) {
return Response.json(
{
error: 'Booking too short',
detail: `Minimum duration is ${MIN_DURATION_MINUTES} minutes`,
},
{ status: 400 }
)
}
if (durationMinutes > MAX_DURATION_MINUTES) {
return Response.json(
{
error: 'Booking too long',
detail: `Maximum duration is ${MAX_DURATION_MINUTES} minutes`,
},
{ status: 400 }
)
}
// ... create booking
}