Advance booking limits that exist only as a UI filter are not limits at all. A direct POST to the booking API skips the date picker entirely. OWASP A03 (Injection) and CWE-20 (Improper Input Validation) apply to any business rule that can be bypassed by sending a raw HTTP request. Without a server-side ceiling, a misbehaving client can book a slot 5 years in the future, creating junk data in the schedule, blocking the resource for phantom appointments, and polluting analytics.
Low because far-future junk bookings are annoying and pollute analytics but do not expose sensitive data or create immediate revenue loss, making it lower urgency than buffer or duration violations.
Define MAX_ADVANCE_DAYS as a server-side constant and validate it in the booking API handler. The check belongs next to duration and cutoff validation so all time-boundary rules are visible in one file.
// src/app/api/book/route.ts
const MAX_ADVANCE_DAYS = 90
export async function POST(req: Request) {
const { startTime } = await req.json()
const requested = new Date(startTime)
const ceiling = new Date(Date.now() + MAX_ADVANCE_DAYS * 86_400_000)
if (requested > ceiling) {
return Response.json(
{
error: 'Booking too far in advance',
detail: `Bookings cannot exceed ${MAX_ADVANCE_DAYS} days from today`,
},
{ status: 400 }
)
}
// ... create booking
}
ID: booking-calendar-availability.availability-logic.advance-booking-limits
Severity: low
What to look for: Search for max advance booking configuration (e.g., MAX_ADVANCE_DAYS = 90, maxBookingDays, advanceLimitDays). Count all booking creation API endpoints. For each, verify the server compares the requested time against now + maxAdvanceDays and rejects requests that exceed the limit with a structured JSON error (HTTP 400). Examine files matching src/app/api/book*/route.ts, src/app/api/appointment*/route.ts, src/lib/*config*, src/lib/*booking*.
Pass criteria: Count all booking API endpoints. At least 1 max advance booking constant must be defined server-side (e.g., MAX_ADVANCE_DAYS). 100% of booking creation endpoints must compare requestedTime against now + maxAdvanceDays and return HTTP 400 with a structured JSON error when exceeded. Report: "Max advance days: [value]. X of Y endpoints enforce server-side."
Fail criteria: Max advance limit is only applied when filtering available slots for display, not when validating a booking request. Or no advance limit constant exists in server-side code.
Skip (N/A) when: Platform explicitly does not enforce max advance booking limits (allows booking arbitrarily far in the future by design).
Detail on fail: Example: "Calendar hides slots beyond 90 days, but API does not check. A direct POST with a date 120 days in the future succeeds."
Cross-reference: Check same-day-cutoff — advance limits guard the far end; cutoff guards the near end. Both should be in the same validation layer.
Cross-reference: Check duration-validation — all booking time boundaries (duration, cutoff, advance) should be validated together.
Cross-reference: Check buffer-enforcement — advance limits and buffer time are both server-side time validations.
Remediation: Validate max advance booking server-side:
// pages/api/appointments.ts
const MAX_ADVANCE_DAYS = 90
export async function POST(req: Request) {
const { startTime } = await req.json()
const now = new Date()
const requestedTime = new Date(startTime)
const maxAllowedTime = new Date(
now.getTime() + MAX_ADVANCE_DAYS * 24 * 60 * 60 * 1000
)
if (requestedTime > maxAllowedTime) {
return Response.json(
{
error: 'Booking too far in advance',
detail: `Bookings cannot be made more than ${MAX_ADVANCE_DAYS} days in advance`,
},
{ status: 400 }
)
}
// ... create booking
}