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 +186 -57 2.30.04.16.9 View file →
@@ -8,8 +8,12 @@
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;
14 +use Give\Helpers\Utils;
15 +
12 16 // Exit if accessed directly.
13 17 if ( ! defined( 'ABSPATH' ) ) {
14 18 exit;
15 19 }
@@ -19,8 +23,11 @@
19 23 *
20 24 * Handles the donation form process.
21 25 *
22 26 * @access private
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
23 30 * @since 1.0
24 31 *
25 32 * @throws ReflectionException Exception Handling.
26 33 *
@@ -48,8 +55,48 @@
48 55 give_send_back_to_checkout();
49 56 }
50 57 }
51 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 +
52 99 /**
53 100 * Fires before processing the donation form.
54 101 *
55 102 * @since 1.0
@@ -116,8 +163,21 @@
116 163 'last_name' => $user['user_last'],
117 164 'address' => $user['address'],
118 165 ];
119 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 +
120 180 $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
121 181
122 182 // Donation form ID.
123 183 $form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0;
@@ -150,14 +210,15 @@
150 210 $purchase_key
151 211 );
152 212
153 213 // Setup donation information.
214 + $user_info = stripslashes_deep( $user_info );
154 215 $donation_data = [
155 216 'price' => $price,
156 217 'purchase_key' => $purchase_key,
157 218 'user_email' => $user['user_email'],
158 219 'date' => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
159 - 'user_info' => stripslashes_deep( $user_info ),
220 + 'user_info' => $user_info,
160 221 'post_data' => $post_data,
161 222 'gateway' => $valid_data['gateway'],
162 223 'card_info' => $valid_data['cc_info'],
163 224 ];
@@ -276,8 +337,9 @@
276 337 /**
277 338 * Process the checkout login form
278 339 *
279 340 * @access private
341 + * @since 4.16.7 Require a valid nonce before processing the login form.
280 342 * @since 1.0
281 343 *
282 344 * @return void
283 345 */
@@ -282,12 +344,23 @@
282 344 * @return void
283 345 */
284 346 function give_process_form_login() {
285 347
286 - $is_ajax = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
287 - $referrer = wp_get_referer();
288 - $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();
289 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 +
290 363 if ( give_get_errors() || $user_data['user_id'] < 1 ) {
291 364 if ( $is_ajax ) {
292 365 /**
293 366 * Fires when AJAX sends back errors from the donation form.
@@ -298,8 +371,9 @@
298 371 do_action( 'give_ajax_donation_errors' );
299 372 $message = ob_get_contents();
300 373 ob_end_clean();
301 374 wp_send_json_error( $message );
375 + return;
302 376 } else {
303 377 wp_safe_redirect( $referrer );
304 378 exit;
305 379 }
@@ -330,8 +404,9 @@
330 404 /**
331 405 * Donation Form Validate Fields.
332 406 *
333 407 * @access private
408 + * @since 3.5.0 validate serialized fields
334 409 * @since 1.0
335 410 *
336 411 * @return bool|array
337 412 */
@@ -337,8 +412,9 @@
337 412 */
338 413 function give_donation_form_validate_fields() {
339 414
340 415 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
416 + give_donation_form_validate_name_fields($post_data);
341 417
342 418 // Validate Honeypot First.
343 419 if ( ! empty( $post_data['give-honeypot'] ) ) {
344 420 give_set_error( 'invalid_honeypot', esc_html__( 'Honeypot field detected. Go away bad bot!', 'give' ) );
@@ -343,8 +419,13 @@
343 419 if ( ! empty( $post_data['give-honeypot'] ) ) {
344 420 give_set_error( 'invalid_honeypot', esc_html__( 'Honeypot field detected. Go away bad bot!', 'give' ) );
345 421 }
346 422
423 + // Validate serialized fields.
424 + if (give_donation_form_has_serialized_fields($post_data)) {
425 + give_set_error('invalid_serialized_fields', esc_html__('Serialized fields detected. Go away!', 'give'));
426 + }
427 +
347 428 // Check spam detect.
348 429 if (
349 430 isset( $post_data['action'] )
350 431 && give_is_spam_donation()
@@ -406,8 +487,30 @@
406 487 return $valid_data;
407 488 }
408 489
409 490 /**
491 + * Detect serialized fields.
492 + *
493 + * @since 3.17.2 Use Utils::isSerialized() method which add supports to find hidden serialized data in the middle of a string
494 + * @since 3.16.5 Make sure only string parameters are used with the ltrim() method to prevent PHP 8+ fatal errors
495 + * @since 3.16.4 updated to check all values for serialized fields
496 + * @since 3.16.2 added additional check for stripslashes_deep
497 + * @since 3.14.2 add give-form-title, give_title
498 + * @since 3.5.0
499 + */
500 +function give_donation_form_has_serialized_fields(array $post_data): bool
501 +{
502 + foreach ($post_data as $value) {
503 +
504 + if (Utils::isSerialized($value)) {
505 + return true;
506 + }
507 + }
508 +
509 + return false;
510 +}
511 +
512 +/**
410 513 * Detect spam donation.
411 514 *
412 515 * @since 1.8.14
413 516 *
@@ -784,8 +887,9 @@
784 887 /**
785 888 * Donation Form Validate Logged In User.
786 889 *
787 890 * @access private
891 + * @since 4.16.7.2 Sanitize first and last name values when falling back to stored user data.
788 892 * @since 1.0
789 893 *
790 894 * @return array
791 895 */
@@ -819,13 +923,13 @@
819 923 'user_email' => ! empty( $post_data['give_email'] )
820 924 ? sanitize_email( $post_data['give_email'] )
821 925 : $user_data->user_email,
822 926 'user_first' => ! empty( $post_data['give_first'] )
823 - ? $post_data['give_first']
824 - : $user_data->first_name,
927 + ? give_clean( $post_data['give_first'] )
928 + : give_clean( $user_data->first_name ),
825 929 'user_last' => ! empty( $post_data['give_last'] )
826 - ? $post_data['give_last']
827 - : $user_data->last_name,
930 + ? give_clean( $post_data['give_last'] )
931 + : give_clean( $user_data->last_name ),
828 932 ];
829 933
830 934 // Validate essential form fields.
831 935 give_donation_form_validate_name_fields( $post_data );
@@ -849,8 +953,9 @@
849 953 /**
850 954 * Donate Form Validate New User
851 955 *
852 956 * @access private
957 + * @since 4.16.6 Flag data as coming from the checkout registration flow.
853 958 * @since 1.0
854 959 *
855 960 * @return array
856 961 */
@@ -910,8 +1015,11 @@
910 1015 if ( give_validate_user_email( $user_data['give_email'], $registering_new_user ) ) {
911 1016 $valid_user_data['user_email'] = $user_data['give_email'];
912 1017 }
913 1018
1019 + // Mark this data as coming from the nonce-verified checkout flow.
1020 + $valid_user_data['give_donation_checkout_registration'] = true;
1021 +
914 1022 return $valid_user_data;
915 1023 }
916 1024
917 1025 /**
@@ -917,8 +1025,9 @@
917 1025 /**
918 1026 * Donation Form Validate User Login
919 1027 *
920 1028 * @access private
1029 + * @since 4.16.7 Authenticate via wp_authenticate() and return a single generic error.
921 1030 * @since 1.0
922 1031 *
923 1032 * @return array
924 1033 */
@@ -940,61 +1049,55 @@
940 1049 return $valid_user_data;
941 1050 }
942 1051
943 1052 $give_user_login = strip_tags( $post_data['give_user_login'] );
944 - if ( is_email( $give_user_login ) ) {
945 - // Get the user data by email.
946 - $user_data = get_user_by( 'email', $give_user_login );
947 - } else {
948 - // Get the user data by login.
949 - $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;
950 1058 }
951 1059
952 - // Check if user exists.
953 - 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'] );
954 1063
955 - // Get password.
956 - $user_pass = ! empty( $post_data['give_user_pass'] ) ? $post_data['give_user_pass'] : false;
1064 + if ( is_wp_error( $user_data ) ) {
957 1065
958 - // Check user_pass.
959 - if ( $user_pass ) {
1066 + $core_auth_error_codes = [
1067 + 'incorrect_password',
1068 + 'invalid_username',
1069 + 'invalid_email',
1070 + 'empty_username',
1071 + 'empty_password',
1072 + ];
960 1073
961 - // Check if password is valid.
962 - 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() );
963 1080
964 - $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 + }
965 1085
966 - // Incorrect password.
967 - give_set_error(
968 - 'password_incorrect',
969 - sprintf(
970 - '%1$s <a href="%2$s">%3$s</a>',
971 - __( 'The password you entered is incorrect.', 'give' ),
972 - wp_lostpassword_url( $current_page_url ),
973 - __( 'Reset Password', 'give' )
974 - )
975 - );
1086 + give_set_error( 'invalid_credentials', $error_message );
976 1087
977 - } else {
1088 + return $valid_user_data;
1089 + }
978 1090
979 - // Repopulate the valid user data array.
980 - $valid_user_data = [
981 - 'user_id' => $user_data->ID,
982 - 'user_login' => $user_data->user_login,
983 - 'user_email' => $user_data->user_email,
984 - 'user_first' => $user_data->first_name,
985 - 'user_last' => $user_data->last_name,
986 - 'user_pass' => $user_pass,
987 - ];
988 - }
989 - } else {
990 - // Empty password.
991 - give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) );
992 - }
993 - } else {
994 - // No username.
995 - give_set_error( 'username_incorrect', __( 'The username you entered does not exist.', 'give' ) );
996 - } // 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 + ];
997 1100
998 1101 return $valid_user_data;
999 1102 }
1000 1103
@@ -1585,8 +1688,12 @@
1585 1688 * Validates and checks if name fields are valid or not.
1586 1689 *
1587 1690 * @param array $post_data List of post data.
1588 1691 *
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
1589 1696 * @since 2.1
1590 1697 *
1591 1698 * @return void
1592 1699 */
@@ -1591,11 +1698,33 @@
1591 1698 * @return void
1592 1699 */
1593 1700 function give_donation_form_validate_name_fields( $post_data ) {
1594 1701
1595 - $is_alpha_first_name = ( ! is_email( $post_data['give_first'] ) && ! preg_match( '~[0-9]~', $post_data['give_first'] ) );
1596 - $is_alpha_last_name = ( ! is_email( $post_data['give_last'] ) && ! preg_match( '~[0-9]~', $post_data['give_last'] ) );
1702 + $formId = absint( $post_data['give-form-id'] );
1597 1703
1598 - if ( ! $is_alpha_first_name || ( ! empty( $post_data['give_last'] ) && ! $is_alpha_last_name ) ) {
1599 - give_set_error( 'invalid_name', esc_html__( 'The First Name and Last Name fields cannot contain an email address or numbers.', 'give' ) );
1600 - }
1704 + if (!give_is_name_title_prefix_enabled($formId) && isset($post_data['give_title'])) {
1705 + give_set_error( 'disabled_name_title', esc_html__( 'The name title prefix field is not enabled.', 'give' ) );
1706 + }
1707 +
1708 + if (!give_is_company_field_enabled($formId) && isset($post_data['give_company_name'])) {
1709 + give_set_error( 'disabled_company', esc_html__( 'The company field is not enabled.', 'give' ) );
1710 + }
1711 +
1712 + if (give_is_name_title_prefix_enabled($formId) && isset($post_data['give_title']) && !in_array($post_data['give_title'], array_values(give_get_name_title_prefixes($formId)))) {
1713 + give_set_error( 'invalid_name_title', esc_html__( 'The name title prefix field is not valid.', 'give' ) );
1714 + }
1715 +
1716 + $is_alpha_first_name = ( ! is_email( $post_data['give_first'] ) && ! preg_match( '~[0-9]~', $post_data['give_first'] ) );
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 +
1721 + $is_alpha_title = ( isset($post_data['give_title']) && ! is_email( $post_data['give_title'] ) && ! preg_match( '~[0-9]~', $post_data['give_title'] ) );
1722 +
1723 + if ( ! $is_alpha_first_name || ( ! empty( $lastName ) && ! $is_alpha_last_name ) || ( ! empty( $post_data['give_title'] ) && ! $is_alpha_title ) ) {
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' ) );
1729 + }
1601 1730 }