Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/rest-api.php (4 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
 * REST API functions.
4
 *
5
 * @package WordPress
6
 * @subpackage REST_API
7
 * @since 4.4.0
8
 */
9
10
/**
11
 * Version number for our API.
12
 *
13
 * @var string
14
 */
15
define( 'REST_API_VERSION', '2.0' );
16
17
/**
18
 * Registers a REST API route.
19
 *
20
 * @since 4.4.0
21
 *
22
 * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
23
 *
24
 * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
25
 * @param string $route     The base URL for route you are adding.
26
 * @param array  $args      Optional. Either an array of options for the endpoint, or an array of arrays for
27
 *                          multiple methods. Default empty array.
28
 * @param bool   $override  Optional. If the route already exists, should we override it? True overrides,
29
 *                          false merges (with newer overriding if duplicate keys exist). Default false.
30
 * @return bool True on success, false on error.
31
 */
32
function register_rest_route( $namespace, $route, $args = array(), $override = false ) {
33
	/** @var WP_REST_Server $wp_rest_server */
34
	global $wp_rest_server;
35
36
	if ( empty( $namespace ) ) {
37
		/*
38
		 * Non-namespaced routes are not allowed, with the exception of the main
39
		 * and namespace indexes. If you really need to register a
40
		 * non-namespaced route, call `WP_REST_Server::register_route` directly.
41
		 */
42
		_doing_it_wrong( 'register_rest_route', __( 'Routes must be namespaced with plugin or theme name and version.' ), '4.4.0' );
43
		return false;
44
	} else if ( empty( $route ) ) {
45
		_doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' );
46
		return false;
47
	}
48
49
	if ( isset( $args['callback'] ) ) {
50
		// Upgrade a single set to multiple.
51
		$args = array( $args );
52
	}
53
54
	$defaults = array(
55
		'methods'         => 'GET',
56
		'callback'        => null,
57
		'args'            => array(),
58
	);
59
	foreach ( $args as $key => &$arg_group ) {
60
		if ( ! is_numeric( $arg_group ) ) {
61
			// Route option, skip here.
62
			continue;
63
		}
64
65
		$arg_group = array_merge( $defaults, $arg_group );
66
	}
67
68
	$full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' );
69
	$wp_rest_server->register_route( $namespace, $full_route, $args, $override );
70
	return true;
71
}
72
73
/**
74
 * Registers rewrite rules for the API.
75
 *
76
 * @since 4.4.0
77
 *
78
 * @see rest_api_register_rewrites()
79
 * @global WP $wp Current WordPress environment instance.
80
 */
81
function rest_api_init() {
82
	rest_api_register_rewrites();
83
84
	global $wp;
85
	$wp->add_query_var( 'rest_route' );
86
}
87
88
/**
89
 * Adds REST rewrite rules.
90
 *
91
 * @since 4.4.0
92
 *
93
 * @see add_rewrite_rule()
94
 */
95
function rest_api_register_rewrites() {
96
	add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' );
97
	add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' );
98
}
99
100
/**
101
 * Registers the default REST API filters.
102
 *
103
 * Attached to the {@see 'rest_api_init'} action
104
 * to make testing and disabling these filters easier.
105
 *
106
 * @since 4.4.0
107
 */
108
function rest_api_default_filters() {
109
	// Deprecated reporting.
110
	add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
111
	add_filter( 'deprecated_function_trigger_error', '__return_false' );
112
	add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
113
	add_filter( 'deprecated_argument_trigger_error', '__return_false' );
114
115
	// Default serving.
116
	add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
117
	add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 );
118
119
	add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
120
}
121
122
/**
123
 * Loads the REST API.
124
 *
125
 * @since 4.4.0
126
 *
127
 * @global WP             $wp             Current WordPress environment instance.
128
 * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
129
 */
130
function rest_api_loaded() {
131
	if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
132
		return;
133
	}
134
135
	/**
136
	 * Whether this is a REST Request.
137
	 *
138
	 * @since 4.4.0
139
	 * @var bool
140
	 */
141
	define( 'REST_REQUEST', true );
142
143
	// Initialize the server.
144
	$server = rest_get_server();
145
146
	// Fire off the request.
147
	$server->serve_request( $GLOBALS['wp']->query_vars['rest_route'] );
148
149
	// We're done.
150
	die();
151
}
152
153
/**
154
 * Retrieves the URL prefix for any API resource.
155
 *
156
 * @since 4.4.0
157
 *
158
 * @return string Prefix.
159
 */
160
function rest_get_url_prefix() {
161
	/**
162
	 * Filters the REST URL prefix.
163
	 *
164
	 * @since 4.4.0
165
	 *
166
	 * @param string $prefix URL prefix. Default 'wp-json'.
167
	 */
168
	return apply_filters( 'rest_url_prefix', 'wp-json' );
169
}
170
171
/**
172
 * Retrieves the URL to a REST endpoint on a site.
173
 *
174
 * Note: The returned URL is NOT escaped.
175
 *
176
 * @since 4.4.0
177
 *
178
 * @todo Check if this is even necessary
179
 *
180
 * @param int    $blog_id Optional. Blog ID. Default of null returns URL for current blog.
181
 * @param string $path    Optional. REST route. Default '/'.
182
 * @param string $scheme  Optional. Sanitization scheme. Default 'rest'.
183
 * @return string Full URL to the endpoint.
184
 */
185
function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) {
186
	if ( empty( $path ) ) {
187
		$path = '/';
188
	}
189
190
	if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) {
191
		$url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme );
192
		$url .= '/' . ltrim( $path, '/' );
193
	} else {
194
		$url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
195
196
		$path = '/' . ltrim( $path, '/' );
197
198
		$url = add_query_arg( 'rest_route', $path, $url );
199
	}
200
201
	if ( is_ssl() ) {
202
		// If the current host is the same as the REST URL host, force the REST URL scheme to HTTPS.
203
		if ( $_SERVER['SERVER_NAME'] === parse_url( get_home_url( $blog_id ), PHP_URL_HOST ) ) {
204
			$url = set_url_scheme( $url, 'https' );
205
		}
206
	}
207
208
	/**
209
	 * Filters the REST URL.
210
	 *
211
	 * Use this filter to adjust the url returned by the get_rest_url() function.
212
	 *
213
	 * @since 4.4.0
214
	 *
215
	 * @param string $url     REST URL.
216
	 * @param string $path    REST route.
217
	 * @param int    $blog_id Blog ID.
218
	 * @param string $scheme  Sanitization scheme.
219
	 */
220
	return apply_filters( 'rest_url', $url, $path, $blog_id, $scheme );
221
}
222
223
/**
224
 * Retrieves the URL to a REST endpoint.
225
 *
226
 * Note: The returned URL is NOT escaped.
227
 *
228
 * @since 4.4.0
229
 *
230
 * @param string $path   Optional. REST route. Default empty.
231
 * @param string $scheme Optional. Sanitization scheme. Default 'json'.
232
 * @return string Full URL to the endpoint.
233
 */
234
function rest_url( $path = '', $scheme = 'json' ) {
235
	return get_rest_url( null, $path, $scheme );
236
}
237
238
/**
239
 * Do a REST request.
240
 *
241
 * Used primarily to route internal requests through WP_REST_Server.
242
 *
243
 * @since 4.4.0
244
 *
245
 * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
246
 *
247
 * @param WP_REST_Request|string $request Request.
248
 * @return WP_REST_Response REST response.
249
 */
250
function rest_do_request( $request ) {
251
	$request = rest_ensure_request( $request );
0 ignored issues
show
It seems like $request can also be of type string; however, rest_ensure_request() does only seem to accept array|object<WP_REST_Request>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
252
	return rest_get_server()->dispatch( $request );
253
}
254
255
/**
256
 * Retrieves the current REST server instance.
257
 *
258
 * Instantiates a new instance if none exists already.
259
 *
260
 * @since 4.5.0
261
 *
262
 * @global WP_REST_Server $wp_rest_server REST server instance.
263
 *
264
 * @return WP_REST_Server REST server instance.
265
 */
266
function rest_get_server() {
267
	/* @var WP_REST_Server $wp_rest_server */
268
	global $wp_rest_server;
269
270
	if ( empty( $wp_rest_server ) ) {
271
		/**
272
		 * Filters the REST Server Class.
273
		 *
274
		 * This filter allows you to adjust the server class used by the API, using a
275
		 * different class to handle requests.
276
		 *
277
		 * @since 4.4.0
278
		 *
279
		 * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
280
		 */
281
		$wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
282
		$wp_rest_server = new $wp_rest_server_class;
283
284
		/**
285
		 * Fires when preparing to serve an API request.
286
		 *
287
		 * Endpoint objects should be created and register their hooks on this action rather
288
		 * than another action to ensure they're only loaded when needed.
289
		 *
290
		 * @since 4.4.0
291
		 *
292
		 * @param WP_REST_Server $wp_rest_server Server object.
293
		 */
294
		do_action( 'rest_api_init', $wp_rest_server );
295
	}
296
297
	return $wp_rest_server;
298
}
299
300
/**
301
 * Ensures request arguments are a request object (for consistency).
302
 *
303
 * @since 4.4.0
304
 *
305
 * @param array|WP_REST_Request $request Request to check.
306
 * @return WP_REST_Request REST request instance.
307
 */
308
function rest_ensure_request( $request ) {
309
	if ( $request instanceof WP_REST_Request ) {
310
		return $request;
311
	}
312
313
	return new WP_REST_Request( 'GET', '', $request );
314
}
315
316
/**
317
 * Ensures a REST response is a response object (for consistency).
318
 *
319
 * This implements WP_HTTP_Response, allowing usage of `set_status`/`header`/etc
320
 * without needing to double-check the object. Will also allow WP_Error to indicate error
321
 * responses, so users should immediately check for this value.
322
 *
323
 * @since 4.4.0
324
 *
325
 * @param WP_Error|WP_HTTP_Response|mixed $response Response to check.
326
 * @return mixed WP_Error if response generated an error, WP_HTTP_Response if response
327
 *               is a already an instance, otherwise returns a new WP_REST_Response instance.
328
 */
329
function rest_ensure_response( $response ) {
330
	if ( is_wp_error( $response ) ) {
331
		return $response;
332
	}
333
334
	if ( $response instanceof WP_HTTP_Response ) {
335
		return $response;
336
	}
337
338
	return new WP_REST_Response( $response );
339
}
340
341
/**
342
 * Handles _deprecated_function() errors.
343
 *
344
 * @since 4.4.0
345
 *
346
 * @param string $function    The function that was called.
347
 * @param string $replacement The function that should have been called.
348
 * @param string $version     Version.
349
 */
350 View Code Duplication
function rest_handle_deprecated_function( $function, $replacement, $version ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
	if ( ! empty( $replacement ) ) {
352
		/* translators: 1: function name, 2: WordPress version number, 3: new function name */
353
		$string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
354
	} else {
355
		/* translators: 1: function name, 2: WordPress version number */
356
		$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
357
	}
358
359
	header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
360
}
361
362
/**
363
 * Handles _deprecated_argument() errors.
364
 *
365
 * @since 4.4.0
366
 *
367
 * @param string $function    The function that was called.
368
 * @param string $message     A message regarding the change.
369
 * @param string $version     Version.
370
 */
371 View Code Duplication
function rest_handle_deprecated_argument( $function, $message, $version ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
	if ( ! empty( $message ) ) {
373
		/* translators: 1: function name, 2: WordPress version number, 3: error message */
374
		$string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $message );
375
	} else {
376
		/* translators: 1: function name, 2: WordPress version number */
377
		$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
378
	}
379
380
	header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
381
}
382
383
/**
384
 * Sends Cross-Origin Resource Sharing headers with API requests.
385
 *
386
 * @since 4.4.0
387
 *
388
 * @param mixed $value Response data.
389
 * @return mixed Response data.
390
 */
391
function rest_send_cors_headers( $value ) {
392
	$origin = get_http_origin();
393
394
	if ( $origin ) {
395
		header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
396
		header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
397
		header( 'Access-Control-Allow-Credentials: true' );
398
	}
399
400
	return $value;
401
}
402
403
/**
404
 * Handles OPTIONS requests for the server.
405
 *
406
 * This is handled outside of the server code, as it doesn't obey normal route
407
 * mapping.
408
 *
409
 * @since 4.4.0
410
 *
411
 * @param mixed           $response Current response, either response or `null` to indicate pass-through.
412
 * @param WP_REST_Server  $handler  ResponseHandler instance (usually WP_REST_Server).
413
 * @param WP_REST_Request $request  The request that was used to make current response.
414
 * @return WP_REST_Response Modified response, either response or `null` to indicate pass-through.
415
 */
416
function rest_handle_options_request( $response, $handler, $request ) {
417
	if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) {
418
		return $response;
419
	}
420
421
	$response = new WP_REST_Response();
422
	$data = array();
423
424
	foreach ( $handler->get_routes() as $route => $endpoints ) {
425
		$match = preg_match( '@^' . $route . '$@i', $request->get_route() );
426
427
		if ( ! $match ) {
428
			continue;
429
		}
430
431
		$data = $handler->get_data_for_route( $route, $endpoints, 'help' );
432
		$response->set_matched_route( $route );
433
		break;
434
	}
435
436
	$response->set_data( $data );
437
	return $response;
438
}
439
440
/**
441
 * Sends the "Allow" header to state all methods that can be sent to the current route.
442
 *
443
 * @since 4.4.0
444
 *
445
 * @param WP_REST_Response $response Current response being served.
446
 * @param WP_REST_Server   $server   ResponseHandler instance (usually WP_REST_Server).
447
 * @param WP_REST_Request  $request  The request that was used to make current response.
448
 * @return WP_REST_Response Response to be served, with "Allow" header if route has allowed methods.
449
 */
450
function rest_send_allow_header( $response, $server, $request ) {
451
	$matched_route = $response->get_matched_route();
452
453
	if ( ! $matched_route ) {
454
		return $response;
455
	}
456
457
	$routes = $server->get_routes();
458
459
	$allowed_methods = array();
460
461
	// Get the allowed methods across the routes.
462
	foreach ( $routes[ $matched_route ] as $_handler ) {
463
		foreach ( $_handler['methods'] as $handler_method => $value ) {
464
465
			if ( ! empty( $_handler['permission_callback'] ) ) {
466
467
				$permission = call_user_func( $_handler['permission_callback'], $request );
468
469
				$allowed_methods[ $handler_method ] = true === $permission;
470
			} else {
471
				$allowed_methods[ $handler_method ] = true;
472
			}
473
		}
474
	}
475
476
	// Strip out all the methods that are not allowed (false values).
477
	$allowed_methods = array_filter( $allowed_methods );
478
479
	if ( $allowed_methods ) {
480
		$response->header( 'Allow', implode( ', ', array_map( 'strtoupper', array_keys( $allowed_methods ) ) ) );
481
	}
482
483
	return $response;
484
}
485
486
/**
487
 * Adds the REST API URL to the WP RSD endpoint.
488
 *
489
 * @since 4.4.0
490
 *
491
 * @see get_rest_url()
492
 */
493
function rest_output_rsd() {
494
	$api_root = get_rest_url();
495
496
	if ( empty( $api_root ) ) {
497
		return;
498
	}
499
	?>
500
	<api name="WP-API" blogID="1" preferred="false" apiLink="<?php echo esc_url( $api_root ); ?>" />
501
	<?php
502
}
503
504
/**
505
 * Outputs the REST API link tag into page header.
506
 *
507
 * @since 4.4.0
508
 *
509
 * @see get_rest_url()
510
 */
511
function rest_output_link_wp_head() {
512
	$api_root = get_rest_url();
513
514
	if ( empty( $api_root ) ) {
515
		return;
516
	}
517
518
	echo "<link rel='https://api.w.org/' href='" . esc_url( $api_root ) . "' />\n";
519
}
520
521
/**
522
 * Sends a Link header for the REST API.
523
 *
524
 * @since 4.4.0
525
 */
526
function rest_output_link_header() {
527
	if ( headers_sent() ) {
528
		return;
529
	}
530
531
	$api_root = get_rest_url();
532
533
	if ( empty( $api_root ) ) {
534
		return;
535
	}
536
537
	header( 'Link: <' . esc_url_raw( $api_root ) . '>; rel="https://api.w.org/"', false );
538
}
539
540
/**
541
 * Checks for errors when using cookie-based authentication.
542
 *
543
 * WordPress' built-in cookie authentication is always active
544
 * for logged in users. However, the API has to check nonces
545
 * for each request to ensure users are not vulnerable to CSRF.
546
 *
547
 * @since 4.4.0
548
 *
549
 * @global mixed          $wp_rest_auth_cookie
550
 * @global WP_REST_Server $wp_rest_server      REST server instance.
551
 *
552
 * @param WP_Error|mixed $result Error from another authentication handler,
553
 *                               null if we should handle it, or another value
554
 *                               if not.
555
 * @return WP_Error|mixed|bool WP_Error if the cookie is invalid, the $result, otherwise true.
556
 */
557
function rest_cookie_check_errors( $result ) {
558
	if ( ! empty( $result ) ) {
559
		return $result;
560
	}
561
562
	global $wp_rest_auth_cookie, $wp_rest_server;
563
564
	/*
565
	 * Is cookie authentication being used? (If we get an auth
566
	 * error, but we're still logged in, another authentication
567
	 * must have been used).
568
	 */
569
	if ( true !== $wp_rest_auth_cookie && is_user_logged_in() ) {
570
		return $result;
571
	}
572
573
	// Determine if there is a nonce.
574
	$nonce = null;
575
576
	if ( isset( $_REQUEST['_wpnonce'] ) ) {
577
		$nonce = $_REQUEST['_wpnonce'];
578
	} elseif ( isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
579
		$nonce = $_SERVER['HTTP_X_WP_NONCE'];
580
	}
581
582
	if ( null === $nonce ) {
583
		// No nonce at all, so act as if it's an unauthenticated request.
584
		wp_set_current_user( 0 );
585
		return true;
586
	}
587
588
	// Check the nonce.
589
	$result = wp_verify_nonce( $nonce, 'wp_rest' );
590
591
	if ( ! $result ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
592
		return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie nonce is invalid' ), array( 'status' => 403 ) );
593
	}
594
595
	// Send a refreshed nonce in header.
596
	$wp_rest_server->send_header( 'X-WP-Nonce', wp_create_nonce( 'wp_rest' ) );
597
598
	return true;
599
}
600
601
/**
602
 * Collects cookie authentication status.
603
 *
604
 * Collects errors from wp_validate_auth_cookie for use by rest_cookie_check_errors.
605
 *
606
 * @since 4.4.0
607
 *
608
 * @see current_action()
609
 * @global mixed $wp_rest_auth_cookie
610
 */
611
function rest_cookie_collect_status() {
612
	global $wp_rest_auth_cookie;
613
614
	$status_type = current_action();
615
616
	if ( 'auth_cookie_valid' !== $status_type ) {
617
		$wp_rest_auth_cookie = substr( $status_type, 12 );
618
		return;
619
	}
620
621
	$wp_rest_auth_cookie = true;
622
}
623
624
/**
625
 * Parses an RFC3339 time into a Unix timestamp.
626
 *
627
 * @since 4.4.0
628
 *
629
 * @param string $date      RFC3339 timestamp.
630
 * @param bool   $force_utc Optional. Whether to force UTC timezone instead of using
631
 *                          the timestamp's timezone. Default false.
632
 * @return int Unix timestamp.
633
 */
634
function rest_parse_date( $date, $force_utc = false ) {
635
	if ( $force_utc ) {
636
		$date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
637
	}
638
639
	$regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';
640
641
	if ( ! preg_match( $regex, $date, $matches ) ) {
642
		return false;
643
	}
644
645
	return strtotime( $date );
646
}
647
648
/**
649
 * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
650
 *
651
 * @since 4.4.0
652
 *
653
 * @see rest_parse_date()
654
 *
655
 * @param string $date      RFC3339 timestamp.
656
 * @param bool   $force_utc Whether a UTC timestamp should be forced. Default false.
657
 * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
658
 *                    null on failure.
659
 */
660
function rest_get_date_with_gmt( $date, $force_utc = false ) {
661
	$date = rest_parse_date( $date, $force_utc );
662
663
	if ( empty( $date ) ) {
664
		return null;
665
	}
666
667
	$utc = date( 'Y-m-d H:i:s', $date );
668
	$local = get_date_from_gmt( $utc );
669
670
	return array( $local, $utc );
671
}
672