Completed
Push — renovate/gridicons-3.x ( c004c1...f8ccd4 )
by
unknown
284:06 queued 275:32
created

Jetpack_Signature::encode_3986()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
use \Automattic\Jetpack\Connection\Manager as Connection_Manager;
4
5
class Jetpack_Signature {
6
	public $token;
7
	public $secret;
8
	public $current_request_url;
9
10
	function __construct( $access_token, $time_diff = 0 ) {
11
		$secret = explode( '.', $access_token );
12
		if ( 2 != count( $secret ) ) {
13
			return;
14
		}
15
16
		$this->token     = $secret[0];
17
		$this->secret    = $secret[1];
18
		$this->time_diff = $time_diff;
0 ignored issues
show
Bug introduced by
The property time_diff does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
19
	}
20
21
	function sign_current_request( $override = array() ) {
22
		if ( isset( $override['scheme'] ) ) {
23
			$scheme = $override['scheme'];
24
			if ( ! in_array( $scheme, array( 'http', 'https' ) ) ) {
25
				return new WP_Error( 'invalid_scheme', 'Invalid URL scheme' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_scheme'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
26
			}
27
		} else {
28
			if ( is_ssl() ) {
29
				$scheme = 'https';
30
			} else {
31
				$scheme = 'http';
32
			}
33
		}
34
35
		$host_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? $_SERVER['HTTP_X_FORWARDED_PORT'] : $_SERVER['SERVER_PORT'];
36
37
		$connection = new Connection_Manager();
0 ignored issues
show
Unused Code introduced by
$connection is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
38
		/**
39
		 * Note: This port logic is tested in the Jetpack_Cxn_Tests->test__server_port_value() test.
40
		 * Please update the test if any changes are made in this logic.
41
		 */
42
		if ( is_ssl() ) {
43
			// 443: Standard Port
44
			// 80: Assume we're behind a proxy without X-Forwarded-Port. Hardcoding "80" here means most sites
45
			// with SSL termination proxies (self-served, Cloudflare, etc.) don't need to fiddle with
46
			// the JETPACK_SIGNATURE__HTTPS_PORT constant. The code also implies we can't talk to a
47
			// site at https://example.com:80/ (which would be a strange configuration).
48
			// JETPACK_SIGNATURE__HTTPS_PORT: Set this constant in wp-config.php to the back end webserver's port
49
			// if the site is behind a proxy running on port 443 without
50
			// X-Forwarded-Port and the back end's port is *not* 80. It's better,
51
			// though, to configure the proxy to send X-Forwarded-Port.
52
			$https_port = defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ? JETPACK_SIGNATURE__HTTPS_PORT : 443;
53
			$port       = in_array( $host_port, array( 443, 80, $https_port ) ) ? '' : $host_port;
54
		} else {
55
			// 80: Standard Port
56
			// JETPACK_SIGNATURE__HTTPS_PORT: Set this constant in wp-config.php to the back end webserver's port
57
			// if the site is behind a proxy running on port 80 without
58
			// X-Forwarded-Port. It's better, though, to configure the proxy to
59
			// send X-Forwarded-Port.
60
			$http_port = defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ? JETPACK_SIGNATURE__HTTP_PORT : 80;
61
			$port      = in_array( $host_port, array( 80, $http_port ) ) ? '' : $host_port;
62
		}
63
64
		$this->current_request_url = "{$scheme}://{$_SERVER['HTTP_HOST']}:{$port}" . stripslashes( $_SERVER['REQUEST_URI'] );
65
66
		if ( array_key_exists( 'body', $override ) && ! empty( $override['body'] ) ) {
67
			$body = $override['body'];
68
		} elseif ( 'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
69
			$body = isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ? $GLOBALS['HTTP_RAW_POST_DATA'] : null;
70
71
			// Convert the $_POST to the body, if the body was empty. This is how arrays are hashed
72
			// and encoded on the Jetpack side.
73
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
74
				if ( empty( $body ) && is_array( $_POST ) && count( $_POST ) > 0 ) {
75
					$body = $_POST;
76
				}
77
			}
78
		} elseif ( 'PUT' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
79
			// This is a little strange-looking, but there doesn't seem to be another way to get the PUT body
80
			$raw_put_data = file_get_contents( 'php://input' );
81
			parse_str( $raw_put_data, $body );
82
83
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
84
				$put_data = json_decode( $raw_put_data, true );
85
				if ( is_array( $put_data ) && count( $put_data ) > 0 ) {
86
					$body = $put_data;
87
				}
88
			}
89
		} else {
90
			$body = null;
91
		}
92
93
		if ( empty( $body ) ) {
94
			$body = null;
95
		}
96
97
		$a = array();
98
		foreach ( array( 'token', 'timestamp', 'nonce', 'body-hash' ) as $parameter ) {
99
			if ( isset( $override[ $parameter ] ) ) {
100
				$a[ $parameter ] = $override[ $parameter ];
101
			} else {
102
				$a[ $parameter ] = isset( $_GET[ $parameter ] ) ? stripslashes( $_GET[ $parameter ] ) : '';
103
			}
104
		}
105
106
		$method = isset( $override['method'] ) ? $override['method'] : $_SERVER['REQUEST_METHOD'];
107
		return $this->sign_request( $a['token'], $a['timestamp'], $a['nonce'], $a['body-hash'], $method, $this->current_request_url, $body, true );
108
	}
109
110
	// body_hash v. body-hash is annoying.  Refactor to accept an array?
111
	function sign_request( $token = '', $timestamp = 0, $nonce = '', $body_hash = '', $method = '', $url = '', $body = null, $verify_body_hash = true ) {
112
		if ( ! $this->secret ) {
113
			return new WP_Error( 'invalid_secret', 'Invalid secret' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_secret'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
114
		}
115
116
		if ( ! $this->token ) {
117
			return new WP_Error( 'invalid_token', 'Invalid token' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_token'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
118
		}
119
120
		list( $token ) = explode( '.', $token );
121
122
		$signature_details = compact( 'token', 'timestamp', 'nonce', 'body_hash', 'method', 'url' );
123
124
		if ( 0 !== strpos( $token, "$this->token:" ) ) {
125
			return new WP_Error( 'token_mismatch', 'Incorrect token', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'token_mismatch'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
126
		}
127
128
		// If we got an array at this point, let's encode it, so we can see what it looks like as a string.
129
		if ( is_array( $body ) ) {
130
			if ( count( $body ) > 0 ) {
131
				$body = json_encode( $body );
132
133
			} else {
134
				$body = '';
135
			}
136
		}
137
138
		$required_parameters = array( 'token', 'timestamp', 'nonce', 'method', 'url' );
139
		if ( ! is_null( $body ) ) {
140
			$required_parameters[] = 'body_hash';
141
			if ( ! is_string( $body ) ) {
142
				return new WP_Error( 'invalid_body', 'Body is malformed.', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_body'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
143
			}
144
		}
145
146
		foreach ( $required_parameters as $required ) {
147
			if ( ! is_scalar( $$required ) ) {
148
				return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', str_replace( '_', '-', $required ) ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_signature'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
149
			}
150
151 View Code Duplication
			if ( ! strlen( $$required ) ) {
152
				return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is missing.', str_replace( '_', '-', $required ) ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_signature'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
153
			}
154
		}
155
156
		if ( empty( $body ) ) {
157
			if ( $body_hash ) {
158
				return new WP_Error( 'invalid_body_hash', 'Invalid body hash for empty body.', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_body_hash'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
159
			}
160
		} else {
161
			$connection = new Connection_Manager();
162
			if ( $verify_body_hash && $connection->sha1_base64( $body ) !== $body_hash ) {
163
				return new WP_Error( 'invalid_body_hash', 'The body hash does not match.', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_body_hash'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
164
			}
165
		}
166
167
		$parsed = parse_url( $url );
168
		if ( ! isset( $parsed['host'] ) ) {
169
			return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'url' ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_signature'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
170
		}
171
172
		if ( ! empty( $parsed['port'] ) ) {
173
			$port = $parsed['port'];
174
		} else {
175
			if ( 'http' == $parsed['scheme'] ) {
176
				$port = 80;
177
			} elseif ( 'https' == $parsed['scheme'] ) {
178
				$port = 443;
179
			} else {
180
				return new WP_Error( 'unknown_scheme_port', "The scheme's port is unknown", compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unknown_scheme_port'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
181
			}
182
		}
183
184 View Code Duplication
		if ( ! ctype_digit( "$timestamp" ) || 10 < strlen( $timestamp ) ) { // If Jetpack is around in 275 years, you can blame mdawaffe for the bug.
185
			return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'timestamp' ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_signature'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
186
		}
187
188
		$local_time = $timestamp - $this->time_diff;
189 View Code Duplication
		if ( $local_time < time() - 600 || $local_time > time() + 300 ) {
190
			return new WP_Error( 'invalid_signature', 'The timestamp is too old.', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_signature'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
191
		}
192
193 View Code Duplication
		if ( 12 < strlen( $nonce ) || preg_match( '/[^a-zA-Z0-9]/', $nonce ) ) {
194
			return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'nonce' ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_signature'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
195
		}
196
197
		$normalized_request_pieces = array(
198
			$token,
199
			$timestamp,
200
			$nonce,
201
			$body_hash,
202
			strtoupper( $method ),
203
			strtolower( $parsed['host'] ),
204
			$port,
205
			$parsed['path'],
206
			// Normalized Query String
207
		);
208
209
		$normalized_request_pieces      = array_merge( $normalized_request_pieces, $this->normalized_query_parameters( isset( $parsed['query'] ) ? $parsed['query'] : '' ) );
210
		$flat_normalized_request_pieces = array();
211
		foreach ( $normalized_request_pieces as $piece ) {
212
			if ( is_array( $piece ) ) {
213
				foreach ( $piece as $subpiece ) {
214
					$flat_normalized_request_pieces[] = $subpiece;
215
				}
216
			} else {
217
				$flat_normalized_request_pieces[] = $piece;
218
			}
219
		}
220
		$normalized_request_pieces = $flat_normalized_request_pieces;
221
222
		$normalized_request_string = join( "\n", $normalized_request_pieces ) . "\n";
223
224
		return base64_encode( hash_hmac( 'sha1', $normalized_request_string, $this->secret, true ) );
225
	}
226
227
	function normalized_query_parameters( $query_string ) {
228
		parse_str( $query_string, $array );
229
		if ( get_magic_quotes_gpc() ) {
230
			$array = stripslashes_deep( $array );
231
		}
232
233
		unset( $array['signature'] );
234
235
		$names  = array_keys( $array );
236
		$values = array_values( $array );
237
238
		$names  = array_map( array( $this, 'encode_3986' ), $names );
239
		$values = array_map( array( $this, 'encode_3986' ), $values );
240
241
		$pairs = array_map( array( $this, 'join_with_equal_sign' ), $names, $values );
242
243
		sort( $pairs );
244
245
		return $pairs;
246
	}
247
248
	function encode_3986( $string_or_array ) {
249
		if ( is_array( $string_or_array ) ) {
250
			return array_map( array( $this, 'encode_3986' ), $string_or_array );
251
		}
252
253
		$string_or_array = rawurlencode( $string_or_array );
254
		return str_replace( '%7E', '~', $string_or_array ); // prior to PHP 5.3, rawurlencode was RFC 1738
255
	}
256
257
	function join_with_equal_sign( $name, $value ) {
258
		if ( is_array( $value ) ) {
259
			$result = array();
260
			foreach ( $value as $array_key => $array_value ) {
261
				$result[] = $name . '[' . $array_key . ']' . '=' . $array_value;
262
			}
263
			return $result;
264
		}
265
		return "{$name}={$value}";
266
	}
267
}
268