Merge pull request #1685 from ultimatemember/development/2.9.x

Version 2.10.4
This commit is contained in:
Mykyta Synelnikov
2025-05-15 01:26:07 +03:00
committed by GitHub
16 changed files with 4044 additions and 107 deletions
+1 -1
View File
@@ -15,7 +15,7 @@
"step": "installPlugin",
"pluginZipFile": {
"resource": "url",
"url": "https:\/\/downloads.wordpress.org\/plugin\/ultimate-member.2.10.3.zip"
"url": "https:\/\/downloads.wordpress.org\/plugin\/ultimate-member.2.10.4.zip"
},
"options": {
"activate": true
+1 -1
View File
@@ -44,7 +44,7 @@ GNU Version 2 or Any Later Version
### IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSION 2.6.7 PATCHES SECURITY PRIVILEGE ESCALATION VULNERABILITY. PLEASE SEE [THIS ARTICLE](https://docs.ultimatemember.com/article/1866-security-incident-update-and-recommended-actions) FOR MORE INFORMATION
[Official Release Version: 2.10.3](https://github.com/ultimatemember/ultimatemember/releases/tag/2.10.3).
[Official Release Version: 2.10.4](https://github.com/ultimatemember/ultimatemember/releases/tag/2.10.4).
## Changelog
+9
View File
@@ -1,5 +1,14 @@
== Changelog ==
= 2.10.4 May 15, 2025 =
* Bugfixes:
- Fixed: Security issue CVE ID: CVE-2025-47691. Used "sniccowp/php-scoper-wordpress-excludes" for getting the recent WordPress functions list and added them to the dynamic blacklist based on the WordPress version.
- Fixed: The Action Scheduler action `um_set_default_account_status`. Case when some users were approved manually or deleted, and we need to reset the admin notice. Added `error_log()` to the wrong conditions.
- Fixed: Reset Password request from not a predefined password reset page. It's possible to submit reset password form sitewide using block or shortcode.
- Fixed: Setting 'Allow users to change email' for the Account page. It works now for any role instead of only the roles with 'Can edit other member accounts?' capability enabled.
= 2.10.3 April 24, 2025 =
* Enhancements:
+7 -1
View File
@@ -33,7 +33,13 @@
"phpcompatibility/phpcompatibility-wp": "*",
"wp-coding-standards/wpcs": "2.3.0",
"squizlabs/php_codesniffer": "3.*",
"phpdocumentor/phpdocumentor": "3.1.*"
"phpdocumentor/phpdocumentor": "3.1.*",
"sniccowp/php-scoper-wordpress-excludes": "6.8.*"
},
"scripts": {
"wordpress-excludes": [
"@php -r \"$dest = 'includes/lib/php-scoper-wordpress-excludes/'; if (!is_dir($dest)) { mkdir($dest, 0755, true); }; copy('vendor/sniccowp/php-scoper-wordpress-excludes/generated/exclude-wordpress-functions.json', $dest . 'exclude-wordpress-functions.json');\""
]
},
"extra": {
"installer-paths": {
+7 -1
View File
@@ -55,6 +55,12 @@ if ( ! class_exists( 'um\admin\core\Admin_Builder' ) ) {
return $errors;
}
$functions_blacklist_error = UM()->builtin()->functions_blacklist_field_err( $submission_data['post'] );
if ( ! empty( $functions_blacklist_error ) ) {
$errors['_custom_dropdown_options_source'] = $functions_blacklist_error;
return $errors;
}
$field_attr = UM()->builtin()->get_core_field_attrs( $submission_data['field_type'] );
if ( ! array_key_exists( 'validate', $field_attr ) ) {
return $errors;
@@ -1151,7 +1157,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Builder' ) ) {
* @return boolean
*/
public function skip_field_validation( $skip, $post_input, $array ) {
if ( $post_input === '_options' && isset( $array['post']['_custom_dropdown_options_source'] ) ) {
if ( '_options' === $post_input && isset( $array['post']['_custom_dropdown_options_source'] ) ) {
$skip = function_exists( wp_unslash( $array['post']['_custom_dropdown_options_source'] ) );
}
+18 -1
View File
@@ -45,6 +45,8 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) {
}
public function status_check() {
// Make the control checking of the empty user statuses inside the function `get_empty_status_users`.
// Maybe some users were approved manually or deleted and we need to reset the admin notice.
$total_users = UM()->common()->users()::get_empty_status_users();
if ( empty( $total_users ) ) {
return;
@@ -114,8 +116,11 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) {
foreach ( $results as $user_id ) {
$res = UM()->common()->users()->approve( $user_id, true, true );
if ( $res ) {
if ( $res || UM()->common()->users()->has_status( $user_id, 'approved' ) ) {
++$um_empty_status_users[0];
} else {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( 'Cannot set `approved` status for the user with ID:' . $user_id );
}
}
@@ -135,7 +140,19 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) {
'pages' => $pages,
)
);
} else {
// Make the control checking of the empty user statuses.
// Maybe some users were approved manually or deleted and we need to reset the admin notice.
$total_users = UM()->common()->users()::get_empty_status_users();
if ( empty( $total_users ) ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( 'There aren\'t users with empty `account_status`. Maybe some users were approved manually or deleted.' );
}
}
} else {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( 'There aren\'t users with empty `account_status`. Maybe some users were approved manually or deleted.' );
delete_option( '_um_log_empty_status_users' );
}
}
}
+10 -5
View File
@@ -840,18 +840,23 @@ class Users {
global $wpdb;
$total_users = $wpdb->get_var(
"SELECT COUNT(u.ID)
"SELECT COUNT(DISTINCT u.ID)
FROM {$wpdb->users} u
LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status'
LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = '_um_registration_in_progress'
WHERE ( um.meta_value IS NULL OR um.meta_value = '' ) AND
um2.meta_value IS NULL OR um2.meta_value != '1'"
( um2.meta_value IS NULL OR um2.meta_value != '1' )"
);
$total_users = absint( $total_users );
// In WordPress, an underscore prefix before the option name (e.g., _my_option_name) is commonly used to indicate that the option is private.
// This option has a format: {updated_users}/{total_users_for_update}.
update_option( '_um_log_empty_status_users', array( 0, $total_users ) );
if ( $total_users > 0 ) {
// In WordPress, an underscore prefix before the option name (e.g., _my_option_name) is commonly used to indicate that the option is private.
// This option has a format: {updated_users}/{total_users_for_update}.
update_option( '_um_log_empty_status_users', array( 0, $total_users ) );
} else {
// Delete option for the cases when there aren't empty `account_status` users. But admin notice is still displayed.
delete_option( '_um_log_empty_status_users' );
}
return $total_users;
}
+2 -2
View File
@@ -676,8 +676,8 @@ if ( ! class_exists( 'um\core\Account' ) ) {
$args = 'user_login,user_email';
}
if ( ! UM()->options()->get( 'account_email' ) && ! um_user( 'can_edit_everyone' ) ) {
$args = str_replace(',user_email','', $args );
if ( ! UM()->options()->get( 'account_email' ) ) {
$args = str_replace( ',user_email', '', $args );
}
if ( $this->current_password_is_required( $id ) ) {
+21
View File
@@ -193,6 +193,27 @@ if ( ! class_exists( 'um\core\Builtin' ) ) {
return 0;
}
/**
* Checks for a blacklist function in the custom callback field error.
*
* @since 2.10.4
*
* @param array $args Custom field submission data.
*
* @return int|string Empty or error string.
*/
public function functions_blacklist_field_err( $args ) {
if ( empty( $args['_custom_dropdown_options_source'] ) ) {
return 0;
}
if ( UM()->fields()->is_source_blacklisted( $args['_custom_dropdown_options_source'] ) ) {
return __( 'This is not possible for security reasons.', 'ultimate-member' );
}
return 0;
}
/**
* Check date range errors (start date)
*
+34 -2
View File
@@ -1379,8 +1379,40 @@ if ( ! class_exists( 'um\core\Fields' ) ) {
public function dropdown_options_source_blacklist() {
$list = get_defined_functions();
$blacklist = ! empty( $list['internal'] ) ? $list['internal'] : array();
$blacklist = apply_filters( 'um_dropdown_options_source_blacklist', $blacklist );
return $blacklist;
// Get the saved version from the database
$wp_functions_version = get_option( 'um_wp_functions_version' );
if ( empty( $wp_functions_version ) || version_compare( UM_WP_FUNCTIONS_VERSION, $wp_functions_version, '>' ) ) {
// Load the JSON file's content
$jsonContent = file_get_contents( UM_PATH . 'includes/lib/php-scoper-wordpress-excludes/exclude-wordpress-functions.json' );
// Parse the JSON string into a PHP array
$um_wp_native_functions_list = json_decode( $jsonContent, true );
// Save the decoded JSON into wp_option
update_option( 'um_wp_functions_list', $um_wp_native_functions_list );
// Update the saved version in the database
update_option( 'um_wp_functions_version', UM_WP_FUNCTIONS_VERSION );
} else {
$um_wp_native_functions_list = get_option( 'um_wp_functions_list', array() );
}
if ( ! empty( $um_wp_native_functions_list ) ) {
$blacklist = array_merge( $blacklist, $um_wp_native_functions_list );
}
/**
* Filters the blacklist of the functions that cannot be used as custom callback for dropdown field to populate the options.
*
* @since 2.5.1
* @hook um_dropdown_options_source_blacklist
*
* @param {array} $blacklist Functions blacklist.
*
* @return {array} Functions blacklist.
*/
return apply_filters( 'um_dropdown_options_source_blacklist', $blacklist );
}
/**
+10 -13
View File
@@ -217,15 +217,11 @@ if ( ! class_exists( 'um\core\Password' ) ) {
*
* @return bool
*/
function is_reset_request() {
if ( um_is_core_page( 'password-reset' ) && isset( $_POST['_um_password_reset'] ) ) {
return true;
}
return false;
public function is_reset_request() {
// phpcs:ignore WordPress.Security.NonceVerification -- already verified here
return ! empty( $_POST['_um_password_reset'] );
}
/**
* Check if a legitimate password change request is in action
*
@@ -233,17 +229,19 @@ if ( ! class_exists( 'um\core\Password' ) ) {
*
* @return bool
*/
function is_change_request() {
if ( isset( $_POST['_um_account'] ) == 1 && isset( $_POST['_um_account_tab'] ) && sanitize_key( $_POST['_um_account_tab'] ) === 'password' ) {
public function is_change_request() {
// phpcs:ignore WordPress.Security.NonceVerification -- already verified here
if ( ! empty( $_POST['_um_account'] ) && isset( $_POST['_um_account_tab'] ) && 'password' === sanitize_key( $_POST['_um_account_tab'] ) ) {
return true;
} elseif ( isset( $_POST['_um_password_change'] ) && $_POST['_um_password_change'] == 1 ) {
}
if ( ! empty( $_POST['_um_password_change'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- already verified here
return true;
}
return false;
}
/**
* Password page form
*/
@@ -482,7 +480,6 @@ if ( ! class_exists( 'um\core\Password' ) ) {
exit;
}
/**
* Error handler: changing password
*
@@ -495,7 +492,7 @@ if ( ! class_exists( 'um\core\Password' ) ) {
wp_die( esc_html__( 'Hello, spam bot!', 'ultimate-member' ) );
}
if ( isset( $args['_um_account'] ) == 1 && isset( $args['_um_account_tab'] ) && 'password' === sanitize_key( $args['_um_account_tab'] ) ) {
if ( ! empty( $args['_um_account'] ) && isset( $args['_um_account_tab'] ) && 'password' === sanitize_key( $args['_um_account_tab'] ) ) {
// validate for security on the account change password page
if ( ! is_user_logged_in() ) {
wp_die( esc_html__( 'This is not possible for security reasons.', 'ultimate-member' ) );
File diff suppressed because it is too large Load Diff
+2 -7
View File
@@ -1376,21 +1376,16 @@ function um_get_metadefault( $id ) {
return isset( $core_form_meta_all[ '_um_' . $id ] ) ? $core_form_meta_all[ '_um_' . $id ] : '';
}
/**
* boolean for account page editing
*
* @return bool
*/
function um_submitting_account_page() {
if ( isset( $_POST['_um_account'] ) && $_POST['_um_account'] == 1 && is_user_logged_in() ) {
return true;
}
return false;
// phpcs:ignore WordPress.Security.NonceVerification -- already verified here
return ( ! empty( $_POST['_um_account'] ) && is_user_logged_in() );
}
/**
* Get a user's display name
*
+13 -67
View File
@@ -6,7 +6,7 @@ Tags: community, member, membership, user-profile, user-registration
Requires PHP: 7.0
Requires at least: 6.2
Tested up to: 6.8
Stable tag: 2.10.2
Stable tag: 2.10.4
License: GPLv3
License URI: http://www.gnu.org/licenses/gpl-3.0.txt
@@ -167,6 +167,15 @@ No specific extensions are needed. But we highly recommended keep active these P
IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSION 2.6.7 PATCHES SECURITY PRIVILEGE ESCALATION VULNERABILITY. PLEASE SEE [THIS ARTICLE](https://docs.ultimatemember.com/article/1866-security-incident-update-and-recommended-actions) FOR MORE INFORMATION
= 2.10.4 2025-05-15 =
**Bugfixes**
* Fixed: Security issue CVE ID: CVE-2025-47691. Used "sniccowp/php-scoper-wordpress-excludes" for getting the recent WordPress functions list and added them to the dynamic blacklist based on the WordPress version.
* Fixed: The Action Scheduler action `um_set_default_account_status`. Case when some users were approved manually or deleted, and we need to reset the admin notice. Added `error_log()` to the wrong conditions.
* Fixed: Reset Password request from not a predefined password reset page. It's possible to submit reset password form sitewide using block or shortcode.
* Fixed: Setting 'Allow users to change email' for the Account page. It works now for any role instead of only the roles with 'Can edit other member accounts?' capability enabled.
= 2.10.3 2025-04-24 =
**Enhancements**
@@ -237,76 +246,13 @@ IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSI
**Cached and optimized/minified assets(JS/CSS) must be flushed/re-generated after upgrade**
= 2.9.2 2025-01-14 =
**Enhancements**
* Added: Compatibility with the new [Ultimate Member - Zapier](https://ultimatemember.com/extensions/zapier/) extension
* Added: Only approved user Reset Password setting defined as true by default
* Added: `UM()->is_new_ui()` function for future enhancements related to new UI
* Added: Filter hook `um_before_user_submitted_registration_data`
* Tweak: Changed hook's priority for initialization of email templates paths
* Tweak: Removed `load_plugin_textdomain` due to (article)[https://make.wordpress.org/core/2024/10/21/i18n-improvements-6-7/#Enhanced-support-for-only-using-PHP-translation-files]
**Bugfixes**
* Fixed: Security issue CVE ID: CVE-2025-0308
* Fixed: Security issue CVE ID: CVE-2025-0318
* Fixed: Using placeholders in email templates when Action Scheduler is active. Using `fetch_user_id` attribute for fetching necessary user before sending email
* Fixed: PHP 8.4 compatibility. Using WordPress native `wp_is_mobile()` instead of MobileDetect library
* Fixed: PHP errors related to `UM()->localize()` function
* Fixed: PHP errors in user meta header when `last_update` meta is empty
* Fixed: Small CSS changes and avoid duplicates
* Fixed: Removed ms-native show password button for type="password" field in UM forms
* Fixed: Define scalable attribute for cropper
**Deprecated**
* Fully deprecated `UM()->mobile()` function
* Fully deprecated `UM()->localize()` function
* Fully deprecated `um_language_textdomain` filter hook
**Templates required update**
* account.php
**Cached and optimized/minified assets(JS/CSS) must be flushed/re-generated after upgrade**
= 2.9.1 2024-11-15 =
**Enhancements**
* Added: `um_image_upload_validation` hook for 3rd-party validation during upload images
**Bugfixes**
* Fixed: "Load textdomain just in time" issue
* Fixed: Capabilities checking in the wp-admin > Users list table
* Fixed: File/image upload on the role specific profile form
* Fixed: Issues when the form's custom fields meta has a wrong format
* Fixed: Validation of the "Registration Default Role" slug
* Fixed: Allowed query variables via registered REST API class only when REST_REQUEST is defined
= 2.9.0 2024-11-12 =
**Enhancements**
* Added: Action Scheduler (version 3.8.1) for email sending. More info is [here](https://actionscheduler.org/).
* Added: Supporting new `wp_register_block_metadata_collection()` function for registering WP Blocks
**Bugfixes**
* Fixed: `ajax_image_upload()` and `ajax_resize_image()` handlers vulnerability. CVE ID: CVE-2024-10528
* Fixed: Disabling user status column wp-admin > Users screen
* Fixed: User status filter on wp-admin > Users on mobile devices
* Fixed: Extra unwrapping of the WP Editor field's value
**Cached and optimized/minified assets(JS/CSS) must be flushed/re-generated after upgrade**
[See changelog for all versions](https://plugins.svn.wordpress.org/ultimate-member/trunk/changelog.txt).
== Upgrade Notice ==
= 2.10.4 =
This version fixes a security related bug. Upgrade immediately.
= 2.10.2 =
This version fixes a security related bug. Upgrade immediately.
+29 -5
View File
@@ -5,12 +5,36 @@ function um_test_generate_random_string( $length = 10 ) {
return substr( str_shuffle( str_repeat( $x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil( $length / strlen( $x ) ) ) ), 1, $length );
}
function api_call() {
$response = wp_remote_get( 'https://randomuser.me/api/?results=1000' );
if ( is_wp_error( $response ) ) {
return 'Something went wrong!';
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body );
$users = array();
foreach ( $data->results as $user ) {
$users[] = array(
'first' => $user->name->first,
'last' => $user->name->last,
'email' => $user->email,
'username' => $user->login->username,
);
}
return $users;
}
$users = api_call();
for ( $i = 0; $i < 1000; $i++ ) {
$random_user_name = um_test_generate_random_string( 8 ); // Generate a random user name
$random_user_email = $random_user_name . '@example.com'; // Append the user name to a dummy email domain
$random_user_pass = um_test_generate_random_string( 12 ); // Generate a random user password
$random_first_name = um_test_generate_random_string( 5 ); // Generate a random first name
$random_last_name = um_test_generate_random_string( 8 ); // Generate a random last name
$random_user_name = $users[ $i ]['username'] ?? um_test_generate_random_string( 8 );
$random_user_email = $users[ $i ]['email'] ?? $random_user_name . '@example.com';
$random_first_name = $users[ $i ]['first'] ?? um_test_generate_random_string( 5 );
$random_last_name = $users[ $i ]['last'] ?? um_test_generate_random_string( 8 );
$userdata = array(
'user_login' => $random_user_name,
+2 -1
View File
@@ -3,7 +3,7 @@
* Plugin Name: Ultimate Member
* Plugin URI: http://ultimatemember.com/
* Description: The easiest way to create powerful online communities and beautiful user profiles with WordPress
* Version: 2.10.3
* Version: 2.10.4
* Author: Ultimate Member
* Author URI: http://ultimatemember.com/
* Text Domain: ultimate-member
@@ -32,6 +32,7 @@ define( 'UM_PATH', plugin_dir_path( __FILE__ ) );
define( 'UM_PLUGIN', plugin_basename( __FILE__ ) );
define( 'UM_VERSION', $plugin_data['Version'] );
define( 'UM_PLUGIN_NAME', $plugin_data['Name'] );
define( 'UM_WP_FUNCTIONS_VERSION', '6.8.0' ); // Updates every major WordPress release.
// define( 'UM_DEV_MODE', true );
require_once 'includes/class-functions.php';