PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.9
GiveWP – Donation Plugin and Fundraising Platform v4.16.9
4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 All 255 releases
← All changes | includes/process-donation.php +136 -58 4.15.04.16.9 View file →
@@ -8,8 +8,10 @@
8 8 * @license https://opensource.org/licenses/gpl-license GNU Public License
9 9 * @since 1.0
10 10 */
11 11
12 +use Give\Helpers\Form\Utils as FormUtils;
13 +use Give\Helpers\Frontend\Shortcode as ShortcodeUtils;
12 14 use Give\Helpers\Utils;
13 15
14 16 // Exit if accessed directly.
15 17 if ( ! defined( 'ABSPATH' ) ) {
@@ -21,9 +23,11 @@
21 23 *
22 24 * Handles the donation form process.
23 25 *
24 26 * @access private
25 - * @since 3.16.1 Use give_maybe_safe_unserialize() on $user_info data
27 + * @since 4.16.7.2 Reject serialized data in name fields before storing donation data.
28 + * @since 4.16.6 Bail early when the form ID is not a give_forms post or is a Visual Form Builder (v3) form.
29 + * @since 3.16.1 Use give_maybe_safe_unserialize() on $user_info data
26 30 * @since 1.0
27 31 *
28 32 * @throws ReflectionException Exception Handling.
29 33 *
@@ -51,8 +55,48 @@
51 55 give_send_back_to_checkout();
52 56 }
53 57 }
54 58
59 + $form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0;
60 +
61 + if ( ! ShortcodeUtils::isValidForm( $form_id ) ) {
62 + give_set_error(
63 + 'give_invalid_donation_form',
64 + __( 'The donation form ID is invalid. Please reload the page and try again.', 'give' )
65 + );
66 +
67 + if ( $is_ajax ) {
68 + /** This action is documented in this file (see give_ajax_donation_errors above). */
69 + do_action( 'give_ajax_donation_errors' );
70 + give_die();
71 + return;
72 + }
73 +
74 + give_send_back_to_checkout();
75 +
76 + return false;
77 + }
78 +
79 + // Visual Form Builder (v3) forms are processed through the givewp-donate route,
80 + // so bail out when the legacy donation processor receives one.
81 + if ( FormUtils::isV3Form( $form_id ) ) {
82 + give_set_error(
83 + 'give_unsupported_form_version',
84 + __( 'This donation form cannot be processed through this endpoint. Please reload the page and try again.', 'give' )
85 + );
86 +
87 + if ( $is_ajax ) {
88 + /** This action is documented in this file (see give_ajax_donation_errors above). */
89 + do_action( 'give_ajax_donation_errors' );
90 + give_die();
91 + return;
92 + }
93 +
94 + give_send_back_to_checkout();
95 +
96 + return false;
97 + }
98 +
55 99 /**
56 100 * Fires before processing the donation form.
57 101 *
58 102 * @since 1.0
@@ -119,8 +163,21 @@
119 163 'last_name' => $user['user_last'],
120 164 'address' => $user['address'],
121 165 ];
122 166
167 + // Reject serialized data in name fields.
168 + $serialized_keys = array_filter(
169 + $user_info,
170 + static function ( $value ) {
171 + return is_string( $value ) && \Give\Helpers\Utils::isSerialized( $value );
172 + }
173 + );
174 +
175 + if ( ! empty( $serialized_keys ) ) {
176 + give_set_error( 'give_serialized_user_info', esc_html__( 'Name fields cannot contain serialized data.', 'give' ) );
177 + return;
178 + }
179 +
123 180 $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
124 181
125 182 // Donation form ID.
126 183 $form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0;
@@ -153,9 +210,9 @@
153 210 $purchase_key
154 211 );
155 212
156 213 // Setup donation information.
157 - $user_info = array_map('\Give\Helpers\Utils::maybeSafeUnserialize', stripslashes_deep( $user_info ));
214 + $user_info = stripslashes_deep( $user_info );
158 215 $donation_data = [
159 216 'price' => $price,
160 217 'purchase_key' => $purchase_key,
161 218 'user_email' => $user['user_email'],
@@ -280,8 +337,9 @@
280 337 /**
281 338 * Process the checkout login form
282 339 *
283 340 * @access private
341 + * @since 4.16.7 Require a valid nonce before processing the login form.
284 342 * @since 1.0
285 343 *
286 344 * @return void
287 345 */
@@ -286,12 +344,23 @@
286 344 * @return void
287 345 */
288 346 function give_process_form_login() {
289 347
290 - $is_ajax = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
291 - $referrer = wp_get_referer();
292 - $user_data = give_donation_form_validate_user_login();
348 + $is_ajax = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
349 + $referrer = wp_get_referer();
293 350
351 + // Default to no user until the login form is validated.
352 + $user_data = [
353 + 'user_id' => - 1,
354 + ];
355 +
356 + // Require a valid nonce before processing the login form.
357 + if ( empty( $_POST['give_login_nonce'] ) || ! wp_verify_nonce( $_POST['give_login_nonce'], 'give-login-nonce' ) ) {
358 + give_set_error( 'invalid_nonce', __( 'Your session has expired. Please reload the page and try again.', 'give' ) );
359 + } else {
360 + $user_data = give_donation_form_validate_user_login();
361 + }
362 +
294 363 if ( give_get_errors() || $user_data['user_id'] < 1 ) {
295 364 if ( $is_ajax ) {
296 365 /**
297 366 * Fires when AJAX sends back errors from the donation form.
@@ -302,8 +371,9 @@
302 371 do_action( 'give_ajax_donation_errors' );
303 372 $message = ob_get_contents();
304 373 ob_end_clean();
305 374 wp_send_json_error( $message );
375 + return;
306 376 } else {
307 377 wp_safe_redirect( $referrer );
308 378 exit;
309 379 }
@@ -817,8 +887,9 @@
817 887 /**
818 888 * Donation Form Validate Logged In User.
819 889 *
820 890 * @access private
891 + * @since 4.16.7.2 Sanitize first and last name values when falling back to stored user data.
821 892 * @since 1.0
822 893 *
823 894 * @return array
824 895 */
@@ -852,13 +923,13 @@
852 923 'user_email' => ! empty( $post_data['give_email'] )
853 924 ? sanitize_email( $post_data['give_email'] )
854 925 : $user_data->user_email,
855 926 'user_first' => ! empty( $post_data['give_first'] )
856 - ? $post_data['give_first']
857 - : $user_data->first_name,
927 + ? give_clean( $post_data['give_first'] )
928 + : give_clean( $user_data->first_name ),
858 929 'user_last' => ! empty( $post_data['give_last'] )
859 - ? $post_data['give_last']
860 - : $user_data->last_name,
930 + ? give_clean( $post_data['give_last'] )
931 + : give_clean( $user_data->last_name ),
861 932 ];
862 933
863 934 // Validate essential form fields.
864 935 give_donation_form_validate_name_fields( $post_data );
@@ -882,8 +953,9 @@
882 953 /**
883 954 * Donate Form Validate New User
884 955 *
885 956 * @access private
957 + * @since 4.16.6 Flag data as coming from the checkout registration flow.
886 958 * @since 1.0
887 959 *
888 960 * @return array
889 961 */
@@ -943,8 +1015,11 @@
943 1015 if ( give_validate_user_email( $user_data['give_email'], $registering_new_user ) ) {
944 1016 $valid_user_data['user_email'] = $user_data['give_email'];
945 1017 }
946 1018
1019 + // Mark this data as coming from the nonce-verified checkout flow.
1020 + $valid_user_data['give_donation_checkout_registration'] = true;
1021 +
947 1022 return $valid_user_data;
948 1023 }
949 1024
950 1025 /**
@@ -950,8 +1025,9 @@
950 1025 /**
951 1026 * Donation Form Validate User Login
952 1027 *
953 1028 * @access private
1029 + * @since 4.16.7 Authenticate via wp_authenticate() and return a single generic error.
954 1030 * @since 1.0
955 1031 *
956 1032 * @return array
957 1033 */
@@ -973,61 +1049,55 @@
973 1049 return $valid_user_data;
974 1050 }
975 1051
976 1052 $give_user_login = strip_tags( $post_data['give_user_login'] );
977 - if ( is_email( $give_user_login ) ) {
978 - // Get the user data by email.
979 - $user_data = get_user_by( 'email', $give_user_login );
980 - } else {
981 - // Get the user data by login.
982 - $user_data = get_user_by( 'login', $give_user_login );
1053 +
1054 + // Bailout, if Password is empty.
1055 + if ( empty( $post_data['give_user_pass'] ) ) {
1056 + give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) );
1057 + return $valid_user_data;
983 1058 }
984 1059
985 - // Check if user exists.
986 - if ( $user_data ) {
1060 + // Authenticate through WordPress's login machinery so its authentication
1061 + // hooks, password checks, and failed-login actions all apply.
1062 + $user_data = wp_authenticate( $give_user_login, $post_data['give_user_pass'] );
987 1063
988 - // Get password.
989 - $user_pass = ! empty( $post_data['give_user_pass'] ) ? $post_data['give_user_pass'] : false;
1064 + if ( is_wp_error( $user_data ) ) {
990 1065
991 - // Check user_pass.
992 - if ( $user_pass ) {
1066 + $core_auth_error_codes = [
1067 + 'incorrect_password',
1068 + 'invalid_username',
1069 + 'invalid_email',
1070 + 'empty_username',
1071 + 'empty_password',
1072 + ];
993 1073
994 - // Check if password is valid.
995 - if ( ! wp_check_password( $user_pass, $user_data->user_pass, $user_data->ID ) ) {
1074 + if ( in_array( $user_data->get_error_code(), $core_auth_error_codes, true ) ) {
1075 + // A single generic message for an unknown login and a wrong password.
1076 + $error_message = __( 'The login/password does not match or is incorrect.', 'give' );
1077 + } else {
1078 + // Any other error comes from an authentication hook; surface its message.
1079 + $error_message = wp_strip_all_tags( $user_data->get_error_message() );
996 1080
997 - $current_page_url = site_url() . '/' . get_page_uri();
1081 + if ( '' === $error_message ) {
1082 + $error_message = __( 'The login/password does not match or is incorrect.', 'give' );
1083 + }
1084 + }
998 1085
999 - // Incorrect password.
1000 - give_set_error(
1001 - 'password_incorrect',
1002 - sprintf(
1003 - '%1$s <a href="%2$s">%3$s</a>',
1004 - __( 'The password you entered is incorrect.', 'give' ),
1005 - wp_lostpassword_url( $current_page_url ),
1006 - __( 'Reset Password', 'give' )
1007 - )
1008 - );
1086 + give_set_error( 'invalid_credentials', $error_message );
1009 1087
1010 - } else {
1088 + return $valid_user_data;
1089 + }
1011 1090
1012 - // Repopulate the valid user data array.
1013 - $valid_user_data = [
1014 - 'user_id' => $user_data->ID,
1015 - 'user_login' => $user_data->user_login,
1016 - 'user_email' => $user_data->user_email,
1017 - 'user_first' => $user_data->first_name,
1018 - 'user_last' => $user_data->last_name,
1019 - 'user_pass' => $user_pass,
1020 - ];
1021 - }
1022 - } else {
1023 - // Empty password.
1024 - give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) );
1025 - }
1026 - } else {
1027 - // No username.
1028 - give_set_error( 'username_incorrect', __( 'The username you entered does not exist.', 'give' ) );
1029 - } // End if().
1091 + // Repopulate the valid user data array.
1092 + $valid_user_data = [
1093 + 'user_id' => $user_data->ID,
1094 + 'user_login' => $user_data->user_login,
1095 + 'user_email' => $user_data->user_email,
1096 + 'user_first' => $user_data->first_name,
1097 + 'user_last' => $user_data->last_name,
1098 + 'user_pass' => $post_data['give_user_pass'],
1099 + ];
1030 1100
1031 1101 return $valid_user_data;
1032 1102 }
1033 1103
@@ -1618,11 +1688,12 @@
1618 1688 * Validates and checks if name fields are valid or not.
1619 1689 *
1620 1690 * @param array $post_data List of post data.
1621 1691 *
1622 - * @since 3.16.5 Check if "give_title" is set to prevent PHP warnings
1623 - * @since 3.16.4 Add additional validation for company name field
1624 - * @since 3.16.3 Add additional validations for name title prefix field
1692 + * @since 4.16.7.2 Validate last name field even when omitted.
1693 + * @since 3.16.5 Check if "give_title" is set to prevent PHP warnings
1694 + * @since 3.16.4 Add additional validation for company name field
1695 + * @since 3.16.3 Add additional validations for name title prefix field
1625 1696 * @since 2.1
1626 1697 *
1627 1698 * @return void
1628 1699 */
@@ -1642,11 +1713,18 @@
1642 1713 give_set_error( 'invalid_name_title', esc_html__( 'The name title prefix field is not valid.', 'give' ) );
1643 1714 }
1644 1715
1645 1716 $is_alpha_first_name = ( ! is_email( $post_data['give_first'] ) && ! preg_match( '~[0-9]~', $post_data['give_first'] ) );
1646 - $is_alpha_last_name = ( ! is_email( $post_data['give_last'] ) && ! preg_match( '~[0-9]~', $post_data['give_last'] ) );
1717 +
1718 + $lastName = isset( $post_data['give_last'] ) ? $post_data['give_last'] : '';
1719 + $is_alpha_last_name = ( ! is_email( $lastName ) && ! preg_match( '~[0-9]~', $lastName ) );
1720 +
1647 1721 $is_alpha_title = ( isset($post_data['give_title']) && ! is_email( $post_data['give_title'] ) && ! preg_match( '~[0-9]~', $post_data['give_title'] ) );
1648 1722
1649 - if (!$is_alpha_first_name || ( ! empty( $post_data['give_last'] ) && ! $is_alpha_last_name) || ( ! empty( $post_data['give_title'] ) && ! $is_alpha_title) ) {
1723 + if ( ! $is_alpha_first_name || ( ! empty( $lastName ) && ! $is_alpha_last_name ) || ( ! empty( $post_data['give_title'] ) && ! $is_alpha_title ) ) {
1650 1724 give_set_error( 'invalid_name', esc_html__( 'The First Name and Last Name fields cannot contain an email address or numbers.', 'give' ) );
1725 + }
1726 +
1727 + if ( give_is_last_name_required( $formId ) && empty( $lastName ) ) {
1728 + give_set_error( 'invalid_last_name', esc_html__( 'Please enter your last name.', 'give' ) );
1651 1729 }
1652 1730 }