| @@ -71,9 +71,9 @@ | ||
| 71 | 71 | * @param string $recaptcha_response The reCAPTCHA response. |
| 72 | 72 | * @param string $plugin_action The action to verify the reCAPTCHA response for. |
| 73 | 73 | * @return bool|WP_Error |
| 74 | 74 | */ |
| 75 | - public function verify_recaptcha( $recaptcha_response, $plugin_action ) { | |
| 75 | + public function verify( $recaptcha_response, $plugin_action ) { | |
| 76 | 76 | |
| 77 | 77 | // Don't run if the reCAPTCHA or scripts are disabled. |
| 78 | 78 | if ( ! $this->settings->has_recaptcha_site_and_secret_keys() || $this->settings->scripts_disabled() ) { |
| 79 | 79 | return true; |
| @@ -90,9 +90,9 @@ | ||
| 90 | 90 | ), |
| 91 | 91 | ) |
| 92 | 92 | ); |
| 93 | 93 | |
| 94 | - // Bail if an error occured. | |
| 94 | + // Bail if an error occurred. | |
| 95 | 95 | if ( is_wp_error( $response ) ) { |
| 96 | 96 | return $response; |
| 97 | 97 | } |
| 98 | 98 | |
| @@ -98,28 +98,39 @@ | ||
| 98 | 98 | |
| 99 | 99 | // Inspect response. |
| 100 | 100 | $body = json_decode( wp_remote_retrieve_body( $response ), true ); |
| 101 | 101 | |
| 102 | + // If the response body couldn't be decoded, treat that as a failure. | |
| 103 | + if ( ! is_array( $body ) ) { | |
| 104 | + return new WP_Error( | |
| 105 | + 'convertkit_recaptcha_failed', | |
| 106 | + __( 'Google reCAPTCHA failure: invalid response from siteverify.', 'convertkit' ) | |
| 107 | + ); | |
| 108 | + } | |
| 109 | + | |
| 102 | 110 | // If the request wasn't successful, return an error. |
| 103 | - if ( ! $body['success'] ) { | |
| 111 | + if ( empty( $body['success'] ) ) { | |
| 104 | 112 | return new WP_Error( |
| 105 | 113 | 'convertkit_recaptcha_failed', |
| 106 | 114 | sprintf( |
| 107 | 115 | /* translators: Error codes */ |
| 108 | 116 | __( 'Google reCAPTCHA failure: %s', 'convertkit' ), |
| 109 | - implode( ', ', $body['error-codes'] ) | |
| 117 | + implode( ', ', isset( $body['error-codes'] ) ? (array) $body['error-codes'] : array() ) | |
| 110 | 118 | ) |
| 111 | 119 | ); |
| 112 | 120 | } |
| 113 | 121 | |
| 114 | - // Return if the action doesn't match the Plugin action, this might not be a reCAPTCHA request | |
| 115 | - // for this request. | |
| 116 | - if ( $body['action'] !== $plugin_action ) { | |
| 117 | - return true; | |
| 122 | + // If the action doesn't match the Plugin action, the token was generated for a different action. | |
| 123 | + // Treat this as a failure, so the minimum score check can't be bypassed. | |
| 124 | + if ( ! isset( $body['action'] ) || $body['action'] !== $plugin_action ) { | |
| 125 | + return new WP_Error( | |
| 126 | + 'convertkit_recaptcha_failed', | |
| 127 | + __( 'Google reCAPTCHA failed', 'convertkit' ) | |
| 128 | + ); | |
| 118 | 129 | } |
| 119 | 130 | |
| 120 | 131 | // If the score is less than the required minimum score, it's likely a spam submission. |
| 121 | - if ( $body['score'] < $this->settings->recaptcha_minimum_score() ) { | |
| 132 | + if ( ! isset( $body['score'] ) || $body['score'] < $this->settings->recaptcha_minimum_score() ) { | |
| 122 | 133 | return new WP_Error( |
| 123 | 134 | 'convertkit_recaptcha_failed', |
| 124 | 135 | __( 'Google reCAPTCHA failed', 'convertkit' ) |
| 125 | 136 | ); |
| @@ -126,8 +137,57 @@ | ||
| 126 | 137 | } |
| 127 | 138 | |
| 128 | 139 | // If here, the submission looks genuine. Continue the request. |
| 129 | 140 | return true; |
| 141 | + | |
| 142 | + } | |
| 143 | + | |
| 144 | + /** | |
| 145 | + * Attaches the reCAPTCHA v3 invisible-badge attributes to the given submit | |
| 146 | + * button element within an existing DOM tree, so that the challenge is | |
| 147 | + * executed when the button is clicked and the form is submitted via the | |
| 148 | + * `convertKitRecaptchaFormSubmit` callback. | |
| 149 | + * | |
| 150 | + * @since 3.3.7 | |
| 151 | + * | |
| 152 | + * @param ConvertKit_HTML_Parser $parser Parser wrapping the DOM (unused). | |
| 153 | + * @param DOMElement $button <button> element to attach attributes to. | |
| 154 | + * @param string $plugin_action Plugin action string. | |
| 155 | + */ | |
| 156 | + public function attach_to_form_button_dom( $parser, $button, $plugin_action ) { | |
| 157 | + | |
| 158 | + unset( $parser ); | |
| 159 | + | |
| 160 | + $button->setAttribute( 'data-sitekey', esc_attr( $this->settings->recaptcha_site_key() ) ); | |
| 161 | + $button->setAttribute( 'data-callback', 'convertKitRecaptchaFormSubmit' ); | |
| 162 | + $button->setAttribute( 'data-action', $plugin_action ); | |
| 163 | + $button->setAttribute( 'class', trim( $button->getAttribute( 'class' ) . ' g-recaptcha' ) ); | |
| 164 | + | |
| 165 | + } | |
| 166 | + | |
| 167 | + /** | |
| 168 | + * Returns the HTML for a submit button with reCAPTCHA v3 invisible-badge | |
| 169 | + * attributes attached, used by templates that don't have a DOM parser | |
| 170 | + * available (e.g. the Restrict Content tag view). | |
| 171 | + * | |
| 172 | + * @since 3.3.7 | |
| 173 | + * | |
| 174 | + * @param string $label Button's visible label. | |
| 175 | + * @param string $plugin_action Plugin action string. | |
| 176 | + * @param string[] $css_classes CSS classes for the button. | |
| 177 | + * @return string | |
| 178 | + */ | |
| 179 | + public function get_submit_button_html( $label, $plugin_action, $css_classes = array() ) { | |
| 180 | + | |
| 181 | + $css_classes[] = 'g-recaptcha'; | |
| 182 | + | |
| 183 | + return sprintf( | |
| 184 | + '<input type="submit" class="%1$s" value="%2$s" data-sitekey="%3$s" data-callback="convertKitRecaptchaFormSubmit" data-action="%4$s" />', | |
| 185 | + esc_attr( implode( ' ', $css_classes ) ), | |
| 186 | + esc_attr( $label ), | |
| 187 | + esc_attr( $this->settings->recaptcha_site_key() ), | |
| 188 | + esc_attr( $plugin_action ) | |
| 189 | + ); | |
| 130 | 190 | |
| 131 | 191 | } |
| 132 | 192 | |
| 133 | 193 | } |