WordPress · Report #4029867
WordCamp Meta gpre: password-protected event RSVP list readable without password via /gpre/v1/.../rsvp-responses
Summary
Password-protected GatherPress events are supposed to hide the RSVP attendee list until someone enters the event password — the official GatherPress API enforces that. A WordCamp Meta REST wrapper (gpre/v1) exposed the same RSVP roster to anyone with no login and no password, while the core endpoint correctly blocked access.
How it was found
On events.wordpress.org (WordPress Groups / events Meta), recurring GatherPress events are served through a custom REST wrapper gpre/v1 (gatherpress-recurring-events mu-plugin in wordcamp.org Meta).
GatherPress core (since 0.35.1) intentionally gates RSVP roster reads with Event::can_read_rsvps(), which requires that a published event’s password gate is satisfied (! post_password_required( $post )). The core route:
GET /wp-json/gatherpress/v1/event/rsvp-responses?post_id={id}
uses permission_callback => can_read_event_rsvps and correctly returns 401 for password-protected events when the caller has not entered the password.
The Meta wrapper route:
GET /wp-json/gpre/v1/event/{recurrence_id}/rsvp-responses?post_id={id}
registers permission_callback => '__return_true' and only checks that the post is publish and the occurrence exists. It never calls Event::can_read_rsvps() / post_password_required().
Result: an unauthenticated caller who does not know the event password can still retrieve the occurrence-scoped RSVP roster (attendee names, user IDs, profile URLs, comment IDs, timestamps) via gpre, while the official GatherPress endpoint denies the same request.
This is not intentional public roster behavior for open events (that is by design). It is a password-gate bypass relative to GatherPress’s documented/security model and relative to other Meta code that does respect post_password_required (e.g. Groups frontend page-content block).
Not a duplicate of BuddyPress Activity REST issues such as #3812595 / #4020618 (different asset, different endpoint, different root cause).
Steps To Reproduce: Pre-conditions (local / Meta Docker — do not abuse production): WordPress with GatherPress ≥ 0.35.1 and the gatherpress-recurring-events (gpre) mu-plugin from wordcamp.org Meta. Activity/Groups-style event with RSVPs enabled.
Create a gatherpress_event that is published and set a post password (e.g. secret-pass). Configure it as a recurring series (gpre meta: weekly/etc.) so occurrences are projected and a recurrence_id exists (e.g. 20260921T100000). As a normal logged-in user, RSVP attending for that occurrence (so the RSVP comment is mapped to the occurrence via gpre). Log out (or use a clean browser / no cookies). Do not submit the post password. Call GatherPress core: GET /wp-json/gatherpress/v1/event/rsvp-responses?post_id=<EVENT_ID> Expected / Actual: 401 rest_forbidden. Call the Meta gpre wrapper with the same event and its recurrence_id: GET /wp-json/gpre/v1/event/<RECURRENCE_ID>/rsvp-responses?post_id=<EVENT_ID> Actual: 200 with JSON like: { "success": true, "data": { "attending": { "count": 1, "records": [ { "name": "rsvper", "user_id": 4, "profile": "http://…/author/rsvper/", "comment_id": 2 } ] } } } Confirm no wp-postpass_* cookie and no Authorization header were sent. Root cause (code): mu-plugins/gatherpress-recurring-events/includes/class-rest-api.php — route rsvp-responses uses __return_true; responses() only requires publish + valid occurrence, then returns ( new Rsvp( $post_id ) )->responses().
Recommendations Change the gpre/v1/.../rsvp-responses permission_callback to reuse GatherPress’s gate, e.g. Event::can_read_rsvps( (int) $request['post_id'] ) (same as core can_read_event_rsvps). Optionally enforce the same check at the start of responses() (defense in depth). Add a PHPUnit test: password-protected published recurring event + unauthenticated gpre GET → 401/403; after password satisfied → 200. Review sibling gpre read routes (/occurrences/...) for the same password omission if they expose sensitive data.
Impact
Unauthenticated attackers can read RSVP attendee identity data (name, user id, profile URL, comment id, attendance status/timestamps) for password-protected recurring events on the WordPress events/Groups Meta platform, bypassing the password protection that GatherPress core enforces on the equivalent API.
Impact is confidentiality of event attendance / community PII for events organizers intended to keep behind a password. No authentication required. No need for elevated roles.