Severity: High — remotely exploitable by any logged-in student, direct financial impact.
Problem
BookingEndpoint::book() (src/Booking/BookingEndpoint.php:99-161) takes offering_id straight from the request and never verifies it matches the slot's own offering (or even the slot's instructor):
A student can book a paid slot while passing the offering_id of any free offering (or one with price = 0). No Payment row is created, yet the lesson/series is reserved and markBooked() runs → free lessons.
A student can point at a different instructor's offering, so price and etransfer_email are sourced from an unrelated offering while instructor_id comes from the slot → payment misrouting.
Fix
Require the chosen offering to belong to $slot->instructorId and/or to be the slot's own offeringId; reject mismatches with a 400/403.
**Severity: High** — remotely exploitable by any logged-in student, direct financial impact.
## Problem
`BookingEndpoint::book()` ([src/Booking/BookingEndpoint.php:99-161](src/Booking/BookingEndpoint.php#L99-L161)) takes `offering_id` straight from the request and never verifies it matches the slot's own offering (or even the slot's instructor):
```php
$offeringId = absint( $request->get_param( 'offering_id' ) );
if ( 0 === $offeringId ) { $offeringId = (int) ( $slot->offeringId ?? 0 ); }
...
if ( null !== $offering && $offering->price > 0.0 ) {
$this->payments->createForRegistration( ..., $offering->price, ... );
}
```
## Impact
- A student can book a **paid** slot while passing the `offering_id` of any **free** offering (or one with `price = 0`). No `Payment` row is created, yet the lesson/series is reserved and `markBooked()` runs → **free lessons**.
- A student can point at a **different instructor's** offering, so price and `etransfer_email` are sourced from an unrelated offering while `instructor_id` comes from the slot → **payment misrouting**.
## Fix
Require the chosen offering to belong to `$slot->instructorId` and/or to be the slot's own `offeringId`; reject mismatches with a 400/403.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: High — remotely exploitable by any logged-in student, direct financial impact.
Problem
BookingEndpoint::book()(src/Booking/BookingEndpoint.php:99-161) takesoffering_idstraight from the request and never verifies it matches the slot's own offering (or even the slot's instructor):Impact
offering_idof any free offering (or one withprice = 0). NoPaymentrow is created, yet the lesson/series is reserved andmarkBooked()runs → free lessons.etransfer_emailare sourced from an unrelated offering whileinstructor_idcomes from the slot → payment misrouting.Fix
Require the chosen offering to belong to
$slot->instructorIdand/or to be the slot's ownofferingId; reject mismatches with a 400/403.