v4.1.1
Security release. Every install on the default (standalone) configuration should upgrade.
No collection schema change, no migration, no import-map regeneration.
Security
-
Standalone mode left Reservations and Customers on Payload's default access, so any customer with a login could take over any other customer's account.
With
payloadReserve()and no options (the Quick Start), the generatedcustomersauth
collection and thereservationscollection carried noread/update/deleterules, so
Payload's built-in default —Boolean(user), any authenticated user — applied. Through the
stock collection REST API a logged-in customer could read, modify and delete every other
customer's reservation; list every customer's name, email, phone and internal notes; and
PATCH /api/customers/:idwith a newpassword, then log in as that customer. Onlycreate
was guarded (enforceCustomerOwnership). Reported privately by an external researcher
(Trenyx) against 4.1.0; reproduced before the fix.Standalone mode now ships scoped defaults with nothing to configure:
Collection Customer Staff/admin (any other auth collection) Reservations read/update own rows only ( customer equals req.user.id); no delete; cannot re-assigncustomereverything Customers read/update own document only; notesis staff-only at field level; no deleteeverything createis unchanged on both, soaccess.customers.create: () => truestill opens
self-registration without reopening reads. Any rule you pass inaccess.reservationsor
access.customersreplaces the default for that operation only.enforceCustomerOwnership
now also runs onupdate, so a customer editing their own reservation cannot hand it to
another account.userCollectionmode is deliberately not changed. Staff and customers share one
collection there and the plugin cannot tell them apart without configured roles, so
Reservations stays on Payload's default. The plugin now warns at boot whenuserCollection
is set with nothing narrowingaccess.reservations.readand noresourceOwnerMode; if
customers can log in to that collection, supply the rule yourself — a copy-paste example is
in docs/configuration.md, "Access control for customers".resourceOwnerModeinstalls
already had their own rules and are unaffected.Covered by
dev/standaloneAccess.int.spec.ts(18 cases, access checks on).
Fixed
- Date-only schedule fields resolved to the previous day in business timezones west of UTC.
exceptions[].date/endDateandmanualSlots[].datename a calendar day but are stored as
instants; the plugin re-keyed them in the businesstimezone, so an exception written as
'2025-12-25'(the form the README shows; it parses to midnight UTC) with
timezone: 'America/New_York'closed December 24 and left the 25th bookable. The admin day
picker stores noon UTC and was fine west of UTC, but the same re-keying put it on the 26th at
UTC+13. The day is now the UTC calendar date of the stored value everywhere it is read
(dateFieldToDayKey): slot generation, conflict-free availability, the resource-availability
endpoint, the availability overview grid, and theendDate >= datevalidation. Also reported
by Trenyx. guestCountaccepted a fractional value.min: 1let1.5through on the collection
(top-level and peritems[]entry) while/api/reserve/holdalready rejected it. Both now
reject a non-integer withguestCount must be a whole number.
Docs
- docs/collections.md no longer claims customer
notesare "visible only to admins" on the
strength of nothing — the field now actually is, in standalone mode — and documents the
access matrix for Reservations per mode. docs/configuration.md gains "Access control for
customers". README "Business Timezone" and docs/booking-features.md state the date-only rule.