From 096a1caf27179fac0cdbdf85e85feb20fc72ae19 Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Thu, 27 Oct 2022 01:42:27 +0300 Subject: [PATCH 01/15] - changed versions; --- readme.txt | 3 +++ ultimate-member.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index f53a6dca..724c14c1 100644 --- a/readme.txt +++ b/readme.txt @@ -163,6 +163,9 @@ No, you do not need to use our plugin’s login or registration pages and can us * To learn more about version 2.1 please see this [docs](https://docs.ultimatemember.com/article/1512-upgrade-2-1-0) * UM2.1+ is a significant update to the Member Directories' code base from 2.0.x. Please make sure you take a full-site backup with restore point before updating the plugin += 2.5.2: November xx, 2022 = + + = 2.5.1: October 26, 2022 = * Enhancements: diff --git a/ultimate-member.php b/ultimate-member.php index a1526ef6..550ed3e1 100644 --- a/ultimate-member.php +++ b/ultimate-member.php @@ -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.5.1 +Version: 2.5.2-alpha Author: Ultimate Member Author URI: http://ultimatemember.com/ Text Domain: ultimate-member From 88a9f6c7db46c379b884a040f43880946fd8f52a Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Wed, 2 Nov 2022 12:32:02 +0800 Subject: [PATCH 02/15] Fix sanitization text --- templates/members.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/members.php b/templates/members.php index e1dc9adf..cf10f6c3 100644 --- a/templates/members.php +++ b/templates/members.php @@ -45,7 +45,7 @@ if ( count( $args['view_types'] ) == 1 ) { } else { $args['default_view'] = ! empty( $args['default_view'] ) ? $args['default_view'] : $args['view_types'][0]; $default_view = $args['default_view']; - $current_view = ( ! empty( $_GET[ 'view_type_' . $unique_hash ] ) && in_array( $_GET[ 'view_type_' . $unique_hash ], $args['view_types'] ) ) ? $_GET[ 'view_type_' . $unique_hash ] : $args['default_view']; + $current_view = ( ! empty( $_GET[ 'view_type_' . $unique_hash ] ) && in_array( $_GET[ 'view_type_' . $unique_hash ], $args['view_types'] ) ) ? sanitize_text_field( $_GET[ 'view_type_' . $unique_hash ] ) : $args['default_view']; } // Sorting From 907ac38a76b882b24acb5654eca86e31f3f66ca6 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Wed, 2 Nov 2022 12:33:22 +0800 Subject: [PATCH 03/15] Add validation for upgrade package --- includes/admin/core/class-admin-upgrade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/core/class-admin-upgrade.php b/includes/admin/core/class-admin-upgrade.php index e6d0c39c..0fca265c 100644 --- a/includes/admin/core/class-admin-upgrade.php +++ b/includes/admin/core/class-admin-upgrade.php @@ -67,7 +67,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Upgrade' ) ) { if ( ! empty( $this->necessary_packages ) ) { add_action( 'admin_menu', array( $this, 'admin_menu' ), 0 ); - if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can("manage_options") ) { $this->init_packages_ajax(); add_action( 'wp_ajax_um_run_package', array( $this, 'ajax_run_package' ) ); From faa5a9d4f9cfcf76723c0f2d7da6c964dba853be Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Thu, 3 Nov 2022 01:46:31 +0200 Subject: [PATCH 04/15] Update class-admin-upgrade.php --- includes/admin/core/class-admin-upgrade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/core/class-admin-upgrade.php b/includes/admin/core/class-admin-upgrade.php index 0fca265c..0c56bee0 100644 --- a/includes/admin/core/class-admin-upgrade.php +++ b/includes/admin/core/class-admin-upgrade.php @@ -67,7 +67,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Upgrade' ) ) { if ( ! empty( $this->necessary_packages ) ) { add_action( 'admin_menu', array( $this, 'admin_menu' ), 0 ); - if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can("manage_options") ) { + if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( 'manage_options' ) ) { $this->init_packages_ajax(); add_action( 'wp_ajax_um_run_package', array( $this, 'ajax_run_package' ) ); From 2004aa7ddecfe84c91b75ad29a19579d2d5bcf0d Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Wed, 9 Nov 2022 03:17:23 +0200 Subject: [PATCH 05/15] Custom dropdown callback functions security enhancements: - avoid using different letter case for bypass the blacklist e.g. phpInfo - avoid using root namespace for bypass the blacklist e.g. \phpinfo --- includes/admin/core/class-admin-builder.php | 2 +- includes/core/class-fields.php | 33 ++++++++++++++++----- includes/core/class-form.php | 2 +- includes/core/class-validation.php | 2 +- includes/core/um-actions-profile.php | 2 +- includes/core/um-filters-fields.php | 2 +- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/includes/admin/core/class-admin-builder.php b/includes/admin/core/class-admin-builder.php index b655d341..ebfc1aa6 100644 --- a/includes/admin/core/class-admin-builder.php +++ b/includes/admin/core/class-admin-builder.php @@ -1220,7 +1220,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Builder' ) ) { $arr_options['function_exists'] = function_exists( $um_callback_func ); } - if ( in_array( $um_callback_func, UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( UM()->fields()->is_source_blacklisted( $um_callback_func ) ) { wp_send_json_error( __( 'This is not possible for security reasons. Don\'t use internal PHP functions.', 'ultimate-member' ) ); } diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index 970cfff6..704228fa 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -112,7 +112,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $output = null; foreach ( $fields as $key => $data ) { - $output .= UM()->fields()->edit_field( $key, $data ); + $output .= $this->edit_field( $key, $data ); } echo $output; @@ -147,7 +147,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( array_key_exists( 'custom_dropdown_options_source', $args ) ) { if ( function_exists( wp_unslash( $args['custom_dropdown_options_source'] ) ) ) { - if ( ! in_array( $args['custom_dropdown_options_source'], UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( ! $this->is_source_blacklisted( $args['custom_dropdown_options_source'] ) ) { $allowed_callbacks = UM()->options()->get( 'allowed_choice_callbacks' ); if ( ! empty( $allowed_callbacks ) ) { $allowed_callbacks = array_map( 'rtrim', explode( "\n", $allowed_callbacks ) ); @@ -204,7 +204,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( array_key_exists( 'custom_dropdown_options_source', $args ) ) { if ( function_exists( wp_unslash( $args['custom_dropdown_options_source'] ) ) ) { - if ( ! in_array( $args['custom_dropdown_options_source'], UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( ! $this->is_source_blacklisted( $args['custom_dropdown_options_source'] ) ) { $allowed_callbacks = UM()->options()->get( 'allowed_choice_callbacks' ); if ( ! empty( $allowed_callbacks ) ) { $allowed_callbacks = array_map( 'rtrim', explode( "\n", $allowed_callbacks ) ); @@ -1309,6 +1309,25 @@ if ( ! class_exists( 'um\core\Fields' ) ) { return $blacklist; } + /** + * Is the dropdown source callback function blacklisted? + * + * @param string $source Function name + * + * @return bool + */ + public function is_source_blacklisted( $source ) { + // avoid using different letter case for bypass the blacklist e.g. phpInfo + // avoid using root namespace for bypass the blacklist e.g. \phpinfo + $source = trim( strtolower( $source ), '\\' ); + + if ( in_array( $source, $this->dropdown_options_source_blacklist(), true ) ) { + return true; + } + + return false; + } + /** * Gets selected option value from a callback function * @@ -1322,7 +1341,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( in_array( $type, array( 'select', 'multiselect' ) ) && ! empty( $data['custom_dropdown_options_source'] ) ) { - if ( in_array( $data['custom_dropdown_options_source'], $this->dropdown_options_source_blacklist(), true ) ) { + if ( $this->is_source_blacklisted( $data['custom_dropdown_options_source'] ) ) { return $value; } @@ -1393,7 +1412,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( in_array( $type, array( 'select', 'multiselect' ) ) && ! empty( $data['custom_dropdown_options_source'] ) ) { - if ( in_array( $data['custom_dropdown_options_source'], $this->dropdown_options_source_blacklist(), true ) ) { + if ( $this->is_source_blacklisted( $data['custom_dropdown_options_source'] ) ) { return $arr_options; } @@ -3062,7 +3081,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( ! empty( $data['custom_dropdown_options_source'] ) && $has_parent_option && function_exists( $data['custom_dropdown_options_source'] ) && um_user( $data['parent_dropdown_relationship'] ) ) { - if ( ! in_array( $data['custom_dropdown_options_source'], $this->dropdown_options_source_blacklist(), true ) ) { + if ( ! $this->is_source_blacklisted( $data['custom_dropdown_options_source'] ) ) { $options = call_user_func( $data['custom_dropdown_options_source'], $data['parent_dropdown_relationship'] ); } @@ -3082,7 +3101,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( $has_parent_option ) { if ( ! empty( $data['custom_dropdown_options_source'] ) && $has_parent_option && function_exists( $data['custom_dropdown_options_source'] ) && isset( UM()->form()->post_form[ $form_key ] ) ) { - if ( ! in_array( $data['custom_dropdown_options_source'], $this->dropdown_options_source_blacklist(), true ) ) { + if ( ! $this->is_source_blacklisted( $data['custom_dropdown_options_source'] ) ) { $options = call_user_func( $data['custom_dropdown_options_source'], $data['parent_dropdown_relationship'] ); } } diff --git a/includes/core/class-form.php b/includes/core/class-form.php index ab12b1ca..eeab460b 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -148,7 +148,7 @@ if ( ! class_exists( 'um\core\Form' ) ) { wp_send_json( $arr_options ); } - if ( in_array( $ajax_source_func, UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( UM()->fields()->is_source_blacklisted( $ajax_source_func ) ) { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'This is not possible for security reasons.', 'ultimate-member' ); diff --git a/includes/core/class-validation.php b/includes/core/class-validation.php index 6ef84de6..4f98a5e7 100644 --- a/includes/core/class-validation.php +++ b/includes/core/class-validation.php @@ -95,7 +95,7 @@ if ( ! class_exists( 'um\core\Validation' ) ) { isset( $fields[ $key ]['custom_dropdown_options_source'] ) && ! empty( $fields[ $key ]['custom_dropdown_options_source'] ) && function_exists( $fields[ $key ]['custom_dropdown_options_source'] ) ) { - if ( ! in_array( $fields[ $key ]['custom_dropdown_options_source'], UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( ! UM()->fields()->is_source_blacklisted( $fields[ $key ]['custom_dropdown_options_source'] ) ) { $arr_options = call_user_func( $fields[ $key ]['custom_dropdown_options_source'] ); $fields[ $key ]['options'] = array_keys( $arr_options ); } diff --git a/includes/core/um-actions-profile.php b/includes/core/um-actions-profile.php index 49f01f7d..7f7e9b37 100644 --- a/includes/core/um-actions-profile.php +++ b/includes/core/um-actions-profile.php @@ -295,7 +295,7 @@ function um_user_edit_profile( $args ) { $options = array(); if ( ! empty( $array['custom_dropdown_options_source'] ) && function_exists( $array['custom_dropdown_options_source'] ) && ! $has_custom_source ) { - if ( ! in_array( $array['custom_dropdown_options_source'], UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( ! UM()->fields()->is_source_blacklisted( $array['custom_dropdown_options_source'] ) ) { $callback_result = call_user_func( $array['custom_dropdown_options_source'], $array['options'] ); if ( is_array( $callback_result ) ) { $options = array_keys( $callback_result ); diff --git a/includes/core/um-filters-fields.php b/includes/core/um-filters-fields.php index d431a434..29527944 100644 --- a/includes/core/um-filters-fields.php +++ b/includes/core/um-filters-fields.php @@ -694,7 +694,7 @@ add_filter( 'um_field_non_utf8_value', 'um_field_non_utf8_value' ); */ function um_select_dropdown_dynamic_callback_options( $options, $data ) { if ( ! empty( $data['custom_dropdown_options_source'] ) && function_exists( $data['custom_dropdown_options_source'] ) ) { - if ( in_array( $data['custom_dropdown_options_source'], UM()->fields()->dropdown_options_source_blacklist(), true ) ) { + if ( UM()->fields()->is_source_blacklisted( $data['custom_dropdown_options_source'] ) ) { return $options; } $options = call_user_func( $data['custom_dropdown_options_source'] ); From 0771f56df25aeada96a47730c933fd74f658af7e Mon Sep 17 00:00:00 2001 From: yuriinalivaiko Date: Thu, 8 Dec 2022 14:49:28 +0200 Subject: [PATCH 06/15] - added data type verification for the 'url' field --- includes/core/class-form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-form.php b/includes/core/class-form.php index eeab460b..99476051 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -653,7 +653,7 @@ if ( ! class_exists( 'um\core\Form' ) ) { case 'url': $f = UM()->builtin()->get_a_field( $k ); - if ( array_key_exists( 'match', $f ) && array_key_exists( 'advanced', $f ) && 'social' === $f['advanced'] ) { + if ( is_array( $f ) && array_key_exists( 'match', $f ) && array_key_exists( 'advanced', $f ) && 'social' === $f['advanced'] ) { $v = sanitize_text_field( $form[ $k ] ); // Make a proper social link From 19c83933f2ebbb9722073eaad64a18654dcf5f26 Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 14:53:35 +0200 Subject: [PATCH 07/15] - closed #1101; --- includes/admin/core/class-admin-settings.php | 9 +++++++++ includes/class-config.php | 1 + includes/core/class-password.php | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/includes/admin/core/class-admin-settings.php b/includes/admin/core/class-admin-settings.php index a37ddbee..50f8c98a 100644 --- a/includes/admin/core/class-admin-settings.php +++ b/includes/admin/core/class-admin-settings.php @@ -759,6 +759,9 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) { 'reset_password_limit_number' => array( 'sanitize' => 'absint', ), + 'change_password_request_limit' => array( + 'sanitize' => 'bool', + ), 'blocked_emails' => array( 'sanitize' => 'textarea', ), @@ -1285,6 +1288,12 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) { 'conditional' => array( 'enable_reset_password_limit', '=', 1 ), 'size' => 'small', ), + array( + 'id' => 'change_password_request_limit', + 'type' => 'checkbox', + 'label' => __( 'Change Password request limit', 'ultimate-member' ), + 'tooltip' => __( 'This option adds rate limit when submitting the change password form in the Account page. Users are only allowed to submit 1 request per 30 minutes to prevent from any brute-force attacks or password guessing with the form.', 'ultimate-member' ), + ), array( 'id' => 'blocked_emails', 'type' => 'textarea', diff --git a/includes/class-config.php b/includes/class-config.php index 5938a261..296286d5 100644 --- a/includes/class-config.php +++ b/includes/class-config.php @@ -553,6 +553,7 @@ if ( ! class_exists( 'um\Config' ) ) { 'restricted_block_message' => '', 'enable_reset_password_limit' => 1, 'reset_password_limit_number' => 3, + 'change_password_request_limit' => false, 'blocked_emails' => '', 'blocked_words' => 'admin' . "\r\n" . 'administrator' . "\r\n" . 'webmaster' . "\r\n" . 'support' . "\r\n" . 'staff', 'allowed_choice_callbacks' => '', diff --git a/includes/core/class-password.php b/includes/core/class-password.php index 9b18caa7..92e88479 100644 --- a/includes/core/class-password.php +++ b/includes/core/class-password.php @@ -526,6 +526,18 @@ if ( ! class_exists( 'um\core\Password' ) ) { } } + if ( ! empty( $args['user_password'] ) && UM()->options()->get( 'change_password_request_limit' ) && is_user_logged_in() ) { + $transient_id = '_um_change_password_rate_limit__' . um_user( 'ID' ); + $last_request = get_transient( $transient_id ); + $request_limit_time = apply_filters( 'um_change_password_attempt_limit_interval', 30 * MINUTE_IN_SECONDS ); + if ( ! $last_request ) { + set_transient( $transient_id, time(), $request_limit_time ); + } else { + UM()->form()->add_error( 'user_password', __( 'Unable to change password because of password change limit. Please try again later.', 'ultimate-member' ) ); + return; + } + } + if ( isset( $args['user_password'] ) && empty( $args['user_password'] ) ) { UM()->form()->add_error( 'user_password', __( 'You must enter a new password', 'ultimate-member' ) ); } From 0f8b87f288118f99ee096bba035a1cfc9e124f7a Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 15:25:32 +0200 Subject: [PATCH 08/15] - closed #1100; --- includes/core/class-password.php | 22 ++++++++++++++++++++++ includes/core/um-actions-account.php | 15 +++++++++++++++ includes/core/um-actions-form.php | 8 ++++++++ 3 files changed, 45 insertions(+) diff --git a/includes/core/class-password.php b/includes/core/class-password.php index 92e88479..b2dc4053 100644 --- a/includes/core/class-password.php +++ b/includes/core/class-password.php @@ -556,10 +556,24 @@ if ( ! class_exists( 'um\core\Password' ) ) { } if ( UM()->options()->get( 'require_strongpass' ) ) { + wp_fix_server_vars(); + + $rp_cookie = 'wp-resetpass-' . COOKIEHASH; + if ( ! is_user_logged_in() && isset( $_COOKIE[ $rp_cookie ] ) && 0 < strpos( $_COOKIE[ $rp_cookie ], ':' ) ) { + list( $rp_login, $rp_key ) = explode( ':', wp_unslash( $_COOKIE[ $rp_cookie ] ), 2 ); + + $user = check_password_reset_key( $rp_key, $rp_login ); + um_fetch_user( $user->ID ); + } elseif ( is_user_logged_in() ) { + um_fetch_user( get_current_user_id() ); + } + $min_length = UM()->options()->get( 'password_min_chars' ); $min_length = ! empty( $min_length ) ? $min_length : 8; $max_length = UM()->options()->get( 'password_max_chars' ); $max_length = ! empty( $max_length ) ? $max_length : 30; + $user_login = um_user( 'user_login' ); + $user_email = um_user( 'user_email' ); if ( mb_strlen( wp_unslash( $args['user_password'] ) ) < $min_length ) { UM()->form()->add_error( 'user_password', sprintf( __( 'Your password must contain at least %d characters', 'ultimate-member' ), $min_length ) ); @@ -569,6 +583,14 @@ if ( ! class_exists( 'um\core\Password' ) ) { UM()->form()->add_error( 'user_password', sprintf( __( 'Your password must contain less than %d characters', 'ultimate-member' ), $max_length ) ); } + if ( strpos( strtolower( $user_login ), strtolower( $args['user_password'] ) ) > -1 ) { + UM()->form()->add_error( 'user_password', __( 'Your password cannot contain the part of your username', 'ultimate-member' ) ); + } + + if ( strpos( strtolower( $user_email ), strtolower( $args['user_password'] ) ) > -1 ) { + UM()->form()->add_error( 'user_password', __( 'Your password cannot contain the part of your email address', 'ultimate-member' ) ); + } + if ( ! UM()->validation()->strong_pass( $args['user_password'] ) ) { UM()->form()->add_error( 'user_password', __( 'Your password must contain at least one lowercase letter, one capital letter and one number', 'ultimate-member' ) ); } diff --git a/includes/core/um-actions-account.php b/includes/core/um-actions-account.php index 723663a5..84e099fd 100644 --- a/includes/core/um-actions-account.php +++ b/includes/core/um-actions-account.php @@ -93,6 +93,13 @@ function um_submit_account_errors_hook( $args ) { $max_length = UM()->options()->get( 'password_max_chars' ); $max_length = ! empty( $max_length ) ? $max_length : 30; + if ( is_user_logged_in() ) { + um_fetch_user( get_current_user_id() ); + } + + $user_login = um_user( 'user_login' ); + $user_email = um_user( 'user_email' ); + if ( mb_strlen( wp_unslash( $args['user_password'] ) ) < $min_length ) { UM()->form()->add_error( 'user_password', sprintf( __( 'Your password must contain at least %d characters', 'ultimate-member' ), $min_length ) ); } @@ -101,6 +108,14 @@ function um_submit_account_errors_hook( $args ) { UM()->form()->add_error( 'user_password', sprintf( __( 'Your password must contain less than %d characters', 'ultimate-member' ), $max_length ) ); } + if ( strpos( strtolower( $user_login ), strtolower( $args['user_password'] ) ) > -1 ) { + UM()->form()->add_error( 'user_password', __( 'Your password cannot contain the part of your username', 'ultimate-member' ) ); + } + + if ( strpos( strtolower( $user_email ), strtolower( $args['user_password'] ) ) > -1 ) { + UM()->form()->add_error( 'user_password', __( 'Your password cannot contain the part of your email address', 'ultimate-member' ) ); + } + if ( ! UM()->validation()->strong_pass( $args['user_password'] ) ) { UM()->form()->add_error( 'user_password', __( 'Your password must contain at least one lowercase letter, one capital letter and one number', 'ultimate-member' ) ); } diff --git a/includes/core/um-actions-form.php b/includes/core/um-actions-form.php index 15b1ae94..16adb5d5 100644 --- a/includes/core/um-actions-form.php +++ b/includes/core/um-actions-form.php @@ -621,6 +621,14 @@ function um_submit_form_errors_hook_( $args ) { } if ( isset( $array['force_good_pass'] ) && $array['force_good_pass'] == 1 ) { + if ( isset( $args['user_login'] ) && strpos( strtolower( $args['user_login'] ), strtolower( $args['user_password'] ) ) > -1 ) { + UM()->form()->add_error( 'user_password', __( 'Your password cannot contain the part of your username', 'ultimate-member' )); + } + + if ( isset( $args['user_email'] ) && strpos( strtolower( $args['user_email'] ), strtolower( $args['user_password'] ) ) > -1 ) { + UM()->form()->add_error( 'user_password', __( 'Your password cannot contain the part of your email address', 'ultimate-member' )); + } + if ( ! UM()->validation()->strong_pass( $args[ $key ] ) ) { UM()->form()->add_error( $key, __( 'Your password must contain at least one lowercase letter, one capital letter and one number', 'ultimate-member' ) ); } From 8b5bb5d7840cdc4edf7307fddbba3965a1222023 Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 15:52:57 +0200 Subject: [PATCH 09/15] - fixed #1082; - used wp_mkdir to avoid the filesystem conflict; --- includes/admin/core/class-admin-settings.php | 12 +++++----- includes/core/class-mail.php | 25 +++++++------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/includes/admin/core/class-admin-settings.php b/includes/admin/core/class-admin-settings.php index 50f8c98a..aaa0b977 100644 --- a/includes/admin/core/class-admin-settings.php +++ b/includes/admin/core/class-admin-settings.php @@ -3457,7 +3457,6 @@ Use Only Cookies: mail()->get_template_file( 'theme', $template ); - if ( ! file_exists( $theme_template_path ) ) { UM()->mail()->copy_email_template( $template ); } - $fp = fopen( $theme_template_path, "w" ); - $result = fputs( $fp, $content ); - fclose( $fp ); + if ( file_exists( $theme_template_path ) ) { + $fp = fopen( $theme_template_path, "w" ); + $result = fputs( $fp, $content ); + fclose( $fp ); + } - if ( $result !== false ) { + if ( isset( $result ) && $result !== false ) { unset( $settings['um_email_template'] ); unset( $settings[ $template ] ); } diff --git a/includes/core/class-mail.php b/includes/core/class-mail.php index bef34181..e7c7b288 100644 --- a/includes/core/class-mail.php +++ b/includes/core/class-mail.php @@ -552,28 +552,19 @@ if ( ! class_exists( 'um\core\Mail' ) ) { * @return bool */ function copy_email_template( $template ) { - $in_theme = $this->template_in_theme( $template ); if ( $in_theme ) { return false; } - $plugin_template_path = $this->get_template_file( 'plugin', $template ); - $theme_template_path = $this->get_template_file( 'theme', $template ); + $plugin_template_path = wp_normalize_path( $this->get_template_file( 'plugin', $template ) ); + $theme_template_path = wp_normalize_path( $this->get_template_file( 'theme', $template ) ); + $template_filename = $this->get_template_filename( $template ) . '.php'; - $temp_path = str_replace( trailingslashit( get_stylesheet_directory() ), '', $theme_template_path ); - $temp_path = str_replace( '/', DIRECTORY_SEPARATOR, $temp_path ); - $folders = explode( DIRECTORY_SEPARATOR, $temp_path ); - $folders = array_splice( $folders, 0, count( $folders ) - 1 ); - $cur_folder = ''; - $theme_dir = trailingslashit( get_stylesheet_directory() ); - - foreach ( $folders as $folder ) { - $prev_dir = $cur_folder; - $cur_folder .= $folder . DIRECTORY_SEPARATOR; - if ( ! is_dir( $theme_dir . $cur_folder ) && wp_is_writable( $theme_dir . $prev_dir ) ) { - mkdir( $theme_dir . $cur_folder, 0777 ); - } + $template_dir = wp_normalize_path( str_replace( $template_filename, '', $theme_template_path ) ); + $result = wp_mkdir_p( $template_dir ); + if ( ! $result ) { + return false; } if ( file_exists( $plugin_template_path ) && copy( $plugin_template_path, $theme_template_path ) ) { @@ -621,4 +612,4 @@ if ( ! class_exists( 'um\core\Mail' ) ) { return $replace_placeholders; } } -} \ No newline at end of file +} From c5501fe6d61cd3f89bf46ad6d2637dda3b20e68b Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 16:13:29 +0200 Subject: [PATCH 10/15] - fixed #1094; --- includes/core/class-user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-user.php b/includes/core/class-user.php index fae8e09d..a5c01eb9 100644 --- a/includes/core/class-user.php +++ b/includes/core/class-user.php @@ -1546,7 +1546,7 @@ if ( ! class_exists( 'um\core\User' ) ) { add_filter( 'um_template_tags_patterns_hook', array( UM()->password(), 'add_placeholder' ), 10, 1 ); add_filter( 'um_template_tags_replaces_hook', array( UM()->password(), 'add_replace_placeholder' ), 10, 1 ); - UM()->mail()->send( um_user( 'user_email' ), 'resetpw_email' ); + UM()->mail()->send( $userdata->user_email, 'resetpw_email' ); } From 43d2fe6a45fd33dbb0a80f4e3b056fae63a54e3d Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 16:41:35 +0200 Subject: [PATCH 11/15] - possible solution for #1091; --- includes/core/um-actions-login.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/includes/core/um-actions-login.php b/includes/core/um-actions-login.php index ec19ca6f..cc76afdb 100644 --- a/includes/core/um-actions-login.php +++ b/includes/core/um-actions-login.php @@ -59,18 +59,29 @@ function um_submit_form_errors_hook_login( $args ) { UM()->form()->add_error( 'user_password', __( 'Password is incorrect. Please try again.', 'ultimate-member' ) ); } + // Integration with 3rd-party login handlers e.g. 3rd-party reCAPTCHA etc. + $third_party_codes = apply_filters( 'um_custom_authenticate_error_codes', array() ); + // @since 4.18 replacement for 'wp_login_failed' action hook // see WP function wp_authenticate() $ignore_codes = array( 'empty_username', 'empty_password' ); $user = apply_filters( 'authenticate', null, $authenticate, $args['user_password'] ); if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes ) ) { - UM()->form()->add_error( $user->get_error_code(), __( 'Password is incorrect. Please try again.', 'ultimate-member' ) ); + if ( ! empty( $third_party_codes ) && in_array( $user->get_error_code(), $third_party_codes ) ) { + UM()->form()->add_error( $user->get_error_code(), $user->get_error_message() ); + } else { + UM()->form()->add_error( $user->get_error_code(), __( 'Password is incorrect. Please try again.', 'ultimate-member' ) ); + } } $user = apply_filters( 'wp_authenticate_user', $user, $args['user_password'] ); if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes ) ) { - UM()->form()->add_error( $user->get_error_code(), __( 'Password is incorrect. Please try again.', 'ultimate-member' ) ); + if ( ! empty( $third_party_codes ) && in_array( $user->get_error_code(), $third_party_codes ) ) { + UM()->form()->add_error( $user->get_error_code(), $user->get_error_message() ); + } else { + UM()->form()->add_error( $user->get_error_code(), __( 'Password is incorrect. Please try again.', 'ultimate-member' ) ); + } } // if there is an error notify wp From 8b2770b12ea207129d91ffbdc30be6b9f4443c9d Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 23:16:33 +0200 Subject: [PATCH 12/15] - deprecated: UM()->user()->password_reset_key variable; - fixed: #1083, #991 --- includes/core/class-user.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/includes/core/class-user.php b/includes/core/class-user.php index a5c01eb9..a3fc5f7f 100644 --- a/includes/core/class-user.php +++ b/includes/core/class-user.php @@ -33,7 +33,6 @@ if ( ! class_exists( 'um\core\User' ) ) { $this->data = null; $this->profile = null; $this->cannot_edit = null; - $this->password_reset_key = null; $this->deleted_user_id = null; global $wpdb; @@ -1527,11 +1526,7 @@ if ( ! class_exists( 'um\core\User' ) ) { * @return string|\WP_Error */ function maybe_generate_password_reset_key( $userdata ) { - if ( empty( $this->password_reset_key ) ) { - $this->password_reset_key = get_password_reset_key( $userdata ); - } - - return $this->password_reset_key ; + return get_password_reset_key( $userdata ); } From 172e6817141df0e579fde08b61aee2459c6b8e53 Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Tue, 13 Dec 2022 23:39:42 +0200 Subject: [PATCH 13/15] - probably fixed: #1084; --- includes/core/class-fields.php | 7 ++++++- includes/um-short-functions.php | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index 704228fa..8ed31ee5 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -87,7 +87,12 @@ if ( ! class_exists( 'um\core\Fields' ) ) { foreach ( $social as $k => $arr ) { if ( um_profile( $k ) ) { - $match = is_array( $arr['match'] ) ? $arr['match'][0] : $arr['match']; ?> + if ( array_key_exists( 'match' , $arr ) ) { + $match = is_array( $arr['match'] ) ? $arr['match'][0] : $arr['match']; + } else { + $match = null; + } + ?> Date: Wed, 14 Dec 2022 00:23:05 +0200 Subject: [PATCH 14/15] - updated release notes; --- readme.txt | 27 ++++++++++++++++++++++++--- ultimate-member.php | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/readme.txt b/readme.txt index 724c14c1..c711b56b 100644 --- a/readme.txt +++ b/readme.txt @@ -6,8 +6,8 @@ Donate link: Tags: community, member, membership, user-profile, user-registration Requires PHP: 5.6 Requires at least: 5.0 -Tested up to: 6.0 -Stable tag: 2.5.1 +Tested up to: 6.1 +Stable tag: 2.5.2 License: GNU Version 2 or Any Later Version License URI: http://www.gnu.org/licenses/gpl-3.0.txt @@ -163,8 +163,29 @@ No, you do not need to use our plugin’s login or registration pages and can us * To learn more about version 2.1 please see this [docs](https://docs.ultimatemember.com/article/1512-upgrade-2-1-0) * UM2.1+ is a significant update to the Member Directories' code base from 2.0.x. Please make sure you take a full-site backup with restore point before updating the plugin -= 2.5.2: November xx, 2022 = += 2.5.2: December 14, 2022 = +* Enhancements: + + - Added: Custom dropdown callback functions security enhancements. Avoid using blacklisted functions through namespace or uppercase format + - Added: Validation for upgrade package in wp-admin + - Added: `Change Password request limit` option for prevent from any brute-force attacks or password guessing with the form + - Added: Strong password checking for not using username|email inside the password + - Added: `um_custom_authenticate_error_codes` hook for handling 3rd-party login errors on UM invalid form + +* Bugfixes: + + - Fixed: Some texts sanitizing + - Fixed: PHP Error in url-type field on UM Forms + - Fixed: Using `wp_mkdir` to avoid the filesystem conflict when copy email template to theme + - Fixed: Password Reset URL generating + - Fixed: Multiple users approve + - Fixed: Using regular URL-type field for displaying + +* Templates required update: + - members.php + +* Cached and optimized/minified assets(JS/CSS) must be flushed/re-generated after upgrade = 2.5.1: October 26, 2022 = diff --git a/ultimate-member.php b/ultimate-member.php index 550ed3e1..b251d51f 100644 --- a/ultimate-member.php +++ b/ultimate-member.php @@ -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.5.2-alpha +Version: 2.5.2 Author: Ultimate Member Author URI: http://ultimatemember.com/ Text Domain: ultimate-member From 39c592f0f461f4a2b03bed71e400a5114867ec4e Mon Sep 17 00:00:00 2001 From: Nikita Sinelnikov Date: Wed, 14 Dec 2022 00:23:44 +0200 Subject: [PATCH 15/15] - updated readme; --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6e1431e..54d03a87 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ GNU Version 2 or Any Later Version ## Releases -[Official Release Version: 2.5.1](https://github.com/ultimatemember/ultimatemember/releases/tag/2.5.1). +[Official Release Version: 2.5.2](https://github.com/ultimatemember/ultimatemember/releases/tag/2.5.2). ## Changelog