Completed
Push — update/editor-blocks-icon-colo... ( 093ab2...3cfb5e )
by
unknown
08:47
created

modules/sharedaddy/recaptcha.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Class that handles reCAPTCHA.
5
 */
6
class Jetpack_ReCaptcha {
7
8
	/**
9
	 * URL to which requests are POSTed.
10
	 *
11
	 * @const string
12
	 */
13
	const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
14
15
	/**
16
	 * Site key to use in HTML code.
17
	 *
18
	 * @var string
19
	 */
20
	private $site_key;
21
22
	/**
23
	 * Shared secret for the site.
24
	 *
25
	 * @var string
26
	 */
27
	private $secret_key;
28
29
	/**
30
	 * Config for reCAPTCHA instance.
31
	 *
32
	 * @var array
33
	 */
34
	private $config;
35
36
	/**
37
	 * Error codes returned from reCAPTCHA API.
38
	 *
39
	 * @see https://developers.google.com/recaptcha/docs/verify
40
	 *
41
	 * @var array
42
	 */
43
	private $error_codes;
44
45
	/**
46
	 * Create a configured instance to use the reCAPTCHA service.
47
	 *
48
	 * @param string $site_key   Site key to use in HTML code.
49
	 * @param string $secret_key Shared secret between site and reCAPTCHA server.
50
	 * @param array  $config     Config array to optionally configure reCAPTCHA instance.
51
	 */
52
	public function __construct( $site_key, $secret_key, $config = array() ) {
53
		$this->site_key   = $site_key;
54
		$this->secret_key = $secret_key;
55
		$this->config     = wp_parse_args( $config, $this->get_default_config() );
0 ignored issues
show
$this->get_default_config() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation Bug introduced by
It seems like wp_parse_args($config, $...->get_default_config()) can be null. However, the property $config is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
56
57
		$this->error_codes = array(
58
			'missing-input-secret'   => __( 'The secret parameter is missing', 'jetpack' ),
59
			'invalid-input-secret'   => __( 'The secret parameter is invalid or malformed', 'jetpack' ),
60
			'missing-input-response' => __( 'The response parameter is missing', 'jetpack' ),
61
			'invalid-input-response' => __( 'The response parameter is invalid or malformed', 'jetpack' ),
62
			'invalid-json'           => __( 'Invalid JSON', 'jetpack' ),
63
			'unexpected-response'    => __( 'Unexpected response', 'jetpack' ),
64
			'unexpected-hostname'    => __( 'Unexpected hostname', 'jetpack' ),
65
		);
66
	}
67
68
	/**
69
	 * Get default config for this reCAPTCHA instance.
70
	 *
71
	 * @return array Default config
72
	 */
73
	public function get_default_config() {
74
		return array(
75
			'language'       => get_locale(),
76
			'script_async'   => true,
77
			'tag_class'      => 'g-recaptcha',
78
			'tag_attributes' => array(
79
				'theme'    => 'light',
80
				'type'     => 'image',
81
				'tabindex' => 0,
82
			),
83
		);
84
	}
85
86
	/**
87
	 * Calls the reCAPTCHA siteverify API to verify whether the user passes
88
	 * CAPTCHA test.
89
	 *
90
	 * @param string $response  The value of 'g-recaptcha-response' in the submitted
91
	 *                          form.
92
	 * @param string $remote_ip The end user's IP address.
93
	 *
94
	 * @return bool|WP_Error Returns true if verified. Otherwise WP_Error is returned.
95
	 */
96
	public function verify( $response, $remote_ip ) {
97
		// No need make a request if response is empty.
98
		if ( empty( $response ) ) {
99
			return new WP_Error( 'missing-input-response', $this->error_codes['missing-input-response'], 400 );
100
		}
101
102
		$resp = wp_remote_post( self::VERIFY_URL, $this->get_verify_request_params( $response, $remote_ip ) );
103
		if ( is_wp_error( $resp ) ) {
104
			return $resp;
105
		}
106
107
		$resp_decoded = json_decode( wp_remote_retrieve_body( $resp ), true );
108
		if ( ! $resp_decoded ) {
109
			return new WP_Error( 'invalid-json', $this->error_codes['invalid-json'], 400 );
110
		}
111
112
		// Default error code and message.
113
		$error_code    = 'unexpected-response';
114
		$error_message = $this->error_codes['unexpected-response'];
115
116
		// Use the first error code if exists.
117
		if ( isset( $resp_decoded['error-codes'] ) && is_array( $resp_decoded['error-codes'] ) ) {
118
			if ( isset( $resp_decoded['error-codes'][0] ) && isset( $this->error_codes[ $resp_decoded['error-codes'][0] ] ) ) {
119
				$error_message = $this->error_codes[ $resp_decoded['error-codes'][0] ];
120
				$error_code    = $resp_decoded['error-codes'][0];
121
			}
122
		}
123
124
		if ( ! isset( $resp_decoded['success'] ) ) {
125
			return new WP_Error( $error_code, $error_message );
126
		}
127
128
		if ( true !== $resp_decoded['success'] ) {
129
			return new WP_Error( $error_code, $error_message );
130
		}
131
132
		// Validate the hostname matches expected source
133
		if ( isset( $resp_decoded['hostname'] ) ) {
134
			$url = wp_parse_url( get_home_url() );
135
			if ( $url['host'] !== $resp_decoded['hostname'] ) {
136
				return new WP_Error( 'unexpected-host', $this->error_codes['unexpected-hostname'] );
137
			}
138
		}
139
140
		return true;
141
	}
142
143
	/**
144
	 * Get siteverify request parameters.
145
	 *
146
	 * @param string $response  The value of 'g-recaptcha-response' in the submitted
147
	 *                          form.
148
	 * @param string $remote_ip The end user's IP address.
149
	 *
150
	 * @return array
151
	 */
152
	public function get_verify_request_params( $response, $remote_ip ) {
153
		return array(
154
			'body' => array(
155
				'secret'   => $this->secret_key,
156
				'response' => $response,
157
				'remoteip' => $remote_ip,
158
			),
159
			'sslverify' => true,
160
		);
161
	}
162
163
	/**
164
	 * Get reCAPTCHA HTML to render.
165
	 *
166
	 * @return string
167
	 */
168
	public function get_recaptcha_html() {
169
		return sprintf(
170
			'
171
			<div
172
				class="%s"
173
				data-sitekey="%s"
174
				data-theme="%s"
175
				data-type="%s"
176
				data-tabindex="%s"></div>
177
			<script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=%s"%s></script>
178
			',
179
			esc_attr( $this->config['tag_class'] ),
180
			esc_attr( $this->site_key ),
181
			esc_attr( $this->config['tag_attributes']['theme'] ),
182
			esc_attr( $this->config['tag_attributes']['type'] ),
183
			esc_attr( $this->config['tag_attributes']['tabindex'] ),
184
			rawurlencode( $this->config['language'] ),
185
			$this->config['script_async'] ? ' async' : ''
186
		);
187
	}
188
}
189