PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.0
Jetpack – WP Security, Backup, Speed, & Growth v16.0
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
← All changes | class.json-api.php +55 -18 14.4.216.0 View file →
@@ -220,9 +220,9 @@
220 220 if ( ! is_string( $value ) ) {
221 221 return false;
222 222 }
223 223
224 - switch ( strtolower( (string) $value ) ) {
224 + switch ( strtolower( $value ) ) {
225 225 case '1':
226 226 case 't':
227 227 case 'true':
228 228 return true;
@@ -249,9 +249,9 @@
249 249 if ( ! is_string( $value ) ) {
250 250 return false;
251 251 }
252 252
253 - switch ( strtolower( (string) $value ) ) {
253 + switch ( strtolower( $value ) ) {
254 254 case '0':
255 255 case 'f':
256 256 case 'false':
257 257 return true;
@@ -398,9 +398,9 @@
398 398 * @param bool $exit Whether to exit.
399 399 * @return string|null Content type (assuming it didn't exit), or null in certain error cases.
400 400 */
401 401 public function serve( $exit = true ) {
402 - ini_set( 'display_errors', false ); // phpcs:ignore WordPress.PHP.IniSet.display_errors_Blacklisted
402 + ini_set( 'display_errors', false ); // phpcs:ignore WordPress.PHP.IniSet.display_errors_Disallowed
403 403
404 404 $this->exit = (bool) $exit;
405 405
406 406 // This was causing problems with Jetpack, but is necessary for wpcom
@@ -475,9 +475,10 @@
475 475 $four_oh_five = true;
476 476 }
477 477
478 478 // Find which endpoint to serve.
479 - $found = false;
479 + $found = false;
480 + $path_pieces = array();
480 481 foreach ( $this->endpoints as $endpoint_path_versions => $endpoints_by_method ) {
481 482 // @todo Determine if anything depends on this being serialized rather than e.g. JSON.
482 483 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Legacy, possibly depended on elsewhere.
483 484 $endpoint_path_versions = unserialize( $endpoint_path_versions );
@@ -498,8 +499,9 @@
498 499 // Normalize.
499 500 $endpoint_path = untrailingslashit( $endpoint_path );
500 501 if ( $is_help ) {
501 502 // Truncate path at help depth.
503 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $depth is set when $is_help is true.
502 504 $endpoint_path = implode( '/', array_slice( explode( '/', $endpoint_path ), 0, $depth ) );
503 505 }
504 506
505 507 // Generate regular expression from sprintf().
@@ -556,8 +558,9 @@
556 558 * @param string help.
557 559 */
558 560 do_action( 'wpcom_json_api_output', 'help' );
559 561 $proxied = function_exists( 'wpcom_is_proxied_request' ) ? wpcom_is_proxied_request() : false;
562 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $help_content_type is set when $is_help is true.
560 563 if ( 'json' === $help_content_type ) {
561 564 $docs = array();
562 565 foreach ( $matching_endpoints as $matching_endpoint ) {
563 566 if ( $matching_endpoint[0]->is_publicly_documentable() || $proxied || WPCOM_JSON_API__DEBUG ) {
@@ -575,15 +578,18 @@
575 578 }
576 579 exit( 0 );
577 580 }
578 581
582 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $endpoint is set when $find_all_matching_endpoints is false and $found is true, which is guaranteed here.
579 583 if ( $endpoint->in_testing && ! WPCOM_JSON_API__DEBUG ) {
580 584 return $this->output( 404, '', 'text/plain' );
581 585 }
582 586
583 587 /** This action is documented in class.json-api.php */
588 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $endpoint is set when $find_all_matching_endpoints is false and $found is true, which is guaranteed here.
584 589 do_action( 'wpcom_json_api_output', $endpoint->stat );
585 590
591 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $endpoint is set when $find_all_matching_endpoints is false and $found is true, which is guaranteed here.
586 592 $response = $this->process_request( $endpoint, $path_pieces );
587 593
588 594 if ( ! $response && ! is_array( $response ) ) {
589 595 return $this->output( 500, '', 'text/plain' );
@@ -605,8 +611,9 @@
605 611 * @return array|WP_Error Return value from the endpoint's callback.
606 612 */
607 613 public function process_request( WPCOM_JSON_API_Endpoint $endpoint, $path_pieces ) {
608 614 $this->endpoint = $endpoint;
615 + $this->maybe_switch_to_token_user_and_site();
609 616 return call_user_func_array( array( $endpoint, 'callback' ), $path_pieces );
610 617 }
611 618
612 619 /**
@@ -676,9 +683,9 @@
676 683 }
677 684
678 685 if ( 'text/plain' === $content_type ||
679 686 'text/html' === $content_type ) {
680 - status_header( (int) $status_code );
687 + status_header( $status_code );
681 688 header( 'Content-Type: ' . $content_type );
682 689 foreach ( $extra as $key => $value ) {
683 690 header( "$key: $value" );
684 691 }
@@ -698,9 +705,9 @@
698 705 $status_code = 200;
699 706 $content_type = 'application/json';
700 707 }
701 708
702 - status_header( (int) $status_code );
709 + status_header( $status_code );
703 710 header( "Content-Type: $content_type" );
704 711 if ( isset( $this->query['callback'] ) && is_string( $this->query['callback'] ) ) {
705 712 $callback = preg_replace( '/[^a-z0-9_.]/i', '', $this->query['callback'] );
706 713 } else {
@@ -713,9 +720,9 @@
713 720 // [1] <https://blog.miki.it/2014/7/8/abusing-jsonp-with-rosetta-flash/index.html>.
714 721 echo "/**/$callback("; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is JSONP output, not HTML.
715 722
716 723 }
717 - echo $this->json_encode( $response ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is JSON or JSONP output, not HTML.
724 + echo $this->json_encode( $response, JSON_UNESCAPED_SLASHES ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is JSON or JSONP output, not HTML.
718 725 if ( $callback ) {
719 726 echo ');';
720 727 }
721 728
@@ -767,14 +774,13 @@
767 774 * @return array with 'status_code' and 'errors' data.
768 775 */
769 776 public static function serializable_error( $error ) {
770 777
771 - $status_code = $error->get_error_data();
778 + // A missing or non-numeric status resolves to 0 and defaults to 400. Valid HTTP codes, including sub-400 ones, are preserved.
779 + $data = $error->get_error_data();
780 + $status_code = ( is_array( $data ) && isset( $data['status_code'] ) ) ? $data['status_code'] : $data;
781 + $status_code = is_numeric( $status_code ) ? (int) $status_code : 0;
772 782
773 - if ( is_array( $status_code ) && isset( $status_code['status_code'] ) ) {
774 - $status_code = $status_code['status_code'];
775 - }
776 -
777 783 if ( ! $status_code ) {
778 784 $status_code = 400;
779 785 }
780 786 $response = array(
@@ -850,9 +856,8 @@
850 856
851 857 foreach ( $response[ $key_to_filter ] as $key => $values ) {
852 858 if ( is_object( $values ) ) {
853 859 if ( is_object( $response[ $key_to_filter ] ) ) {
854 - // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found -- False positive.
855 860 $response[ $key_to_filter ]->$key = (object) array_intersect_key( ( (array) $values ), array_flip( $fields ) );
856 861 } elseif ( is_array( $response[ $key_to_filter ] ) ) {
857 862 $response[ $key_to_filter ][ $key ] = (object) array_intersect_key( ( (array) $values ), array_flip( $fields ) );
858 863 }
@@ -906,13 +911,16 @@
906 911
907 912 /**
908 913 * JSON encode.
909 914 *
910 - * @param mixed $data Data.
915 + * @param mixed $value The value to encode.
916 + * @param int $flags Options to be passed to json_encode(). Default 0.
917 + * @param int $depth Maximum depth to walk through $value. Must be greater than 0.
918 + *
911 919 * @return string|false
912 920 */
913 - public function json_encode( $data ) {
914 - return wp_json_encode( $data );
921 + public function json_encode( $value, $flags = 0, $depth = 512 ) {
922 + return wp_json_encode( $value, $flags, $depth );
915 923 }
916 924
917 925 /**
918 926 * Test if a string ends with a string.
@@ -970,8 +978,37 @@
970 978 return $blog_id;
971 979 }
972 980
973 981 /**
982 + * Switch to a user and blog based on the current request's Jetpack token when the endpoint accepts this feature.
983 + *
984 + * @return void
985 + */
986 + protected function maybe_switch_to_token_user_and_site() {
987 + if ( ! $this->endpoint->allow_jetpack_token_auth ) {
988 + return;
989 + }
990 +
991 + if ( ! class_exists( 'Jetpack_Server_Version' ) ) {
992 + return;
993 + }
994 +
995 + $token = Jetpack_Server_Version::get_token_from_authorization_header();
996 +
997 + if ( ! $token || is_wp_error( $token ) ) {
998 + return;
999 + }
1000 +
1001 + if ( get_current_user_id() !== $token->user_id ) {
1002 + wp_set_current_user( $token->user_id );
1003 + }
1004 +
1005 + if ( get_current_blog_id() !== $token->blog_id ) {
1006 + switch_to_blog( $token->blog_id );
1007 + }
1008 + }
1009 +
1010 + /**
974 1011 * Returns true if the specified blog ID is a restricted blog
975 1012 *
976 1013 * @param int $blog_id Blog ID.
977 1014 * @return bool
@@ -986,9 +1023,9 @@
986 1023 *
987 1024 * @param array $array Array of Blog IDs.
988 1025 */
989 1026 $restricted_blog_ids = apply_filters( 'wpcom_json_api_restricted_blog_ids', array() );
990 - return true === in_array( $blog_id, $restricted_blog_ids ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict -- I don't trust filters to return the right types.
1027 + return in_array( $blog_id, $restricted_blog_ids ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict -- I don't trust filters to return the right types.
991 1028 }
992 1029
993 1030 /**
994 1031 * Post like count.
@@ -1077,9 +1114,9 @@
1077 1114 /**
1078 1115 * Counts the number of comments on a site, including certain comment types.
1079 1116 *
1080 1117 * @param int $post_id Post ID.
1081 - * @return array Array of counts, matching the output of https://developer.wordpress.org/reference/functions/get_comment_count/.
1118 + * @return object The number of counts keyed by status, matching the output of https://developer.wordpress.org/reference/functions/get_comment_count/.
1082 1119 */
1083 1120 public function wp_count_comments( $post_id ) {
1084 1121 global $wpdb;
1085 1122 if ( 0 !== $post_id ) {