WordPress.org

Make WordPress Core

Ticket #45142: 45142.2.diff

File 45142.2.diff, 13.2 KB (added by swissspidy, 3 years ago)
  • src/wp-includes/class-wp-oembed-controller.php

    diff --git src/wp-includes/class-wp-oembed-controller.php src/wp-includes/class-wp-oembed-controller.php
    index 7ee7950b07..9efeb54ed0 100644
    final class WP_oEmbed_Controller { 
    173173                        $args['height'] = $args['maxheight'];
    174174                }
    175175
     176                // Short-circuit process for URLs belonging to the current site.
     177                $data = get_oembed_response_data_for_url( $url, $args );
     178
     179                if ( $data ) {
     180                        return $data;
     181                }
     182
    176183                $data = _wp_oembed_get_object()->get_data( $url, $args );
    177184
    178185                if ( false === $data ) {
    179186                        return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
    180187                }
    181188
     189                /** This filter is documented in wp-includes/class-oembed.php */
     190                $data->html = apply_filters( 'oembed_result', _wp_oembed_get_object()->data2html( (object) $data, $url ), $url, $args );
     191
    182192                /**
    183193                 * Filters the oEmbed TTL value (time to live).
    184194                 *
  • src/wp-includes/embed.php

    diff --git src/wp-includes/embed.php src/wp-includes/embed.php
    index 176988057b..efd991104a 100644
    function get_oembed_response_data( $post, $width ) { 
    555555        return apply_filters( 'oembed_response_data', $data, $post, $width, $height );
    556556}
    557557
     558
     559/**
     560 * Retrieves the oEmbed response data for a given URL.
     561 *
     562 * @since 5.0.0
     563 *
     564 * @param string $url  The URL that should be inspected for discovery `<link>` tags.
     565 * @param array  $args oEmbed remote get arguments.
     566 * @return object|false oEmbed response data if the URL does belong to the current site. False otherwise.
     567 */
     568function get_oembed_response_data_for_url( $url, $args ) {
     569        $switched_blog = false;
     570
     571        if ( is_multisite() ) {
     572                $url_parts = wp_parse_args( wp_parse_url( $url ), array(
     573                        'host'   => '',
     574                        'path'   => '/',
     575                ) );
     576
     577                $qv = array( 'domain' => $url_parts['host'], 'path' => '/' );
     578
     579                // In case of subdirectory configs, set the path.
     580                if ( ! is_subdomain_install() ) {
     581                        $path = explode( '/', ltrim( $url_parts['path'], '/' ) );
     582                        $path = reset( $path );
     583
     584                        if ( $path ) {
     585                                $qv['path'] = get_network()->path . $path . '/';
     586                        }
     587                }
     588
     589                $sites = get_sites( $qv );
     590                $site  = reset( $sites );
     591
     592                if ( $site && (int) $site->blog_id !== get_current_blog_id() ) {
     593                        switch_to_blog( $site->blog_id );
     594                        $switched_blog = true;
     595                }
     596        }
     597
     598        $post_id = url_to_postid( $url );
     599
     600        /** This filter is documented in wp-includes/class-wp-oembed-controller.php */
     601        $post_id = apply_filters( 'oembed_request_post_id', $post_id, $url );
     602
     603        if ( ! $post_id ) {
     604                if ( $switched_blog ) {
     605                        restore_current_blog();
     606                }
     607
     608                return false;
     609        }
     610
     611        $width = isset( $args['width'] ) ? $args['width'] : 0;
     612
     613        $data = get_oembed_response_data( $post_id, $width );
     614
     615        if ( $switched_blog ) {
     616                restore_current_blog();
     617        }
     618
     619        return $data ? (object) $data : false;
     620}
     621
     622
    558623/**
    559624 * Filters the oEmbed response data to return an iframe embed code.
    560625 *
    function the_embed_site_title() { 
    10711136 *                     Null if the URL does not belong to the current site.
    10721137 */
    10731138function wp_filter_pre_oembed_result( $result, $url, $args ) {
    1074         $switched_blog = false;
    1075 
    1076         if ( is_multisite() ) {
    1077                 $url_parts = wp_parse_args( wp_parse_url( $url ), array(
    1078                         'host'   => '',
    1079                         'path'   => '/',
    1080                 ) );
    1081 
    1082                 $qv = array( 'domain' => $url_parts['host'], 'path' => '/' );
    1083 
    1084                 // In case of subdirectory configs, set the path.
    1085                 if ( ! is_subdomain_install() ) {
    1086                         $path = explode( '/', ltrim( $url_parts['path'], '/' ) );
    1087                         $path = reset( $path );
    1088 
    1089                         if ( $path ) {
    1090                                 $qv['path'] = get_network()->path . $path . '/';
    1091                         }
    1092                 }
    1093 
    1094                 $sites = get_sites( $qv );
    1095                 $site  = reset( $sites );
    1096 
    1097                 if ( $site && (int) $site->blog_id !== get_current_blog_id() ) {
    1098                         switch_to_blog( $site->blog_id );
    1099                         $switched_blog = true;
    1100                 }
    1101         }
    1102 
    1103         $post_id = url_to_postid( $url );
    1104 
    1105         /** This filter is documented in wp-includes/class-wp-oembed-controller.php */
    1106         $post_id = apply_filters( 'oembed_request_post_id', $post_id, $url );
    1107 
    1108         if ( ! $post_id ) {
    1109                 if ( $switched_blog ) {
    1110                         restore_current_blog();
    1111                 }
     1139        $data = get_oembed_response_data_for_url( $url, $args );
    11121140
    1113                 return $result;
     1141        if ( $data ) {
     1142                return _wp_oembed_get_object()->data2html( $data, $url );
    11141143        }
    11151144
    1116         $width = isset( $args['width'] ) ? $args['width'] : 0;
    1117 
    1118         $data = get_oembed_response_data( $post_id, $width );
    1119         $data = _wp_oembed_get_object()->data2html( (object) $data, $url );
    1120 
    1121         if ( $switched_blog ) {
    1122                 restore_current_blog();
    1123         }
    1124 
    1125         if ( ! $data ) {
    1126                 return $result;
    1127         }
    1128 
    1129         return $data;
     1145        return $result;
    11301146}
  • tests/phpunit/tests/oembed/controller.php

    diff --git tests/phpunit/tests/oembed/controller.php tests/phpunit/tests/oembed/controller.php
    index 9f9aa6dd46..2d6afaf76c 100644
    class Test_oEmbed_Controller extends WP_UnitTestCase { 
    1414        protected static $subscriber;
    1515        const YOUTUBE_VIDEO_ID = 'OQSNhk5ICTI';
    1616        const INVALID_OEMBED_URL = 'https://www.notreallyanoembedprovider.com/watch?v=awesome-cat-video';
     17        const UNTRUSTED_PROVIDER_URL = 'https://www.untrustedprovider.com';
    1718
    1819        public static function wpSetUpBeforeClass( $factory ) {
    1920                self::$subscriber = $factory->user->create( array(
    class Test_oEmbed_Controller extends WP_UnitTestCase { 
    7172                unset( $preempt, $r );
    7273
    7374                $parsed_url = wp_parse_url( $url );
    74                 parse_str( $parsed_url['query'], $query_params );
     75                $query      = isset( $parsed_url['query'] ) ? $parsed_url['query'] : '';
     76                parse_str( $query, $query_params );
    7577                $this->request_count += 1;
    7678
    7779                // Mock request to YouTube Embed.
    class Test_oEmbed_Controller extends WP_UnitTestCase { 
    8082                                'response' => array(
    8183                                        'code' => 200,
    8284                                ),
    83                                 'body' => wp_json_encode(
     85                                'body'     => wp_json_encode(
    8486                                        array(
    8587                                                'version'          => '1.0',
    8688                                                'type'             => 'video',
    class Test_oEmbed_Controller extends WP_UnitTestCase { 
    9092                                                'width'            => $query_params['maxwidth'],
    9193                                                'thumbnail_height' => $query_params['maxheight'],
    9294                                                'height'           => $query_params['maxheight'],
    93                                                 'html'             => '<iframe width="' . $query_params['maxwidth'] . '" height="' . $query_params['maxheight'] . '" src="https://www.youtube.com/embed/' . self::YOUTUBE_VIDEO_ID . '?feature=oembed" frameborder="0" allowfullscreen></iframe>',
     95                                                'html'             => '<b>Unfiltered</b><iframe width="' . $query_params['maxwidth'] . '" height="' . $query_params['maxheight'] . '" src="https://www.youtube.com/embed/' . self::YOUTUBE_VIDEO_ID . '?feature=oembed" frameborder="0" allowfullscreen></iframe>',
    9496                                                'author_name'      => 'Yosemitebear62',
    9597                                                'thumbnail_url'    => 'https://i.ytimg.com/vi/' . self::YOUTUBE_VIDEO_ID . '/hqdefault.jpg',
    9698                                                'title'            => 'Yosemitebear Mountain Double Rainbow 1-8-10',
    9799                                        )
    98100                                ),
    99101                        );
    100                 } else {
     102                }
     103
     104                if ( $url === self::UNTRUSTED_PROVIDER_URL ) {
    101105                        return array(
    102106                                'response' => array(
    103                                         'code' => 404,
     107                                        'code' => 200,
    104108                                ),
     109                                'body'     => '<html><head><link rel="alternate" type="application/json+oembed" href="' . self::UNTRUSTED_PROVIDER_URL . '" /></head><body></body></html>',
    105110                        );
    106111                }
     112
     113                if ( ! empty( $query_params['url'] ) && false !== strpos( $query_params['url'], self::UNTRUSTED_PROVIDER_URL ) ) {
     114                        return array(
     115                                'response' => array(
     116                                        'code' => 200,
     117                                ),
     118                                'body'     => wp_json_encode(
     119                                        array(
     120                                                'version'          => '1.0',
     121                                                'type'             => 'rich',
     122                                                'provider_name'    => 'Untrusted',
     123                                                'provider_url'     => self::UNTRUSTED_PROVIDER_URL,
     124                                                'html'             => '<b>Filtered</b><a href="">Unfiltered</a>',
     125                                                'author_name'      => 'Untrusted Embed Author',
     126                                                'title'            => 'Untrusted Embed',
     127                                        )
     128                                ),
     129                        );
     130                }
     131
     132                return array(
     133                        'response' => array(
     134                                'code' => 404,
     135                        ),
     136                );
    107137        }
    108138
    109139        function test_wp_oembed_ensure_format() {
    class Test_oEmbed_Controller extends WP_UnitTestCase { 
    510540                $data = $response->get_data();
    511541
    512542                $this->assertNotEmpty( $data );
    513                 $this->assertTrue( is_object( $data ) );
     543                $this->assertInternalType( 'object', $data );
    514544                $this->assertEquals( 'YouTube', $data->provider_name );
    515545                $this->assertEquals( 'https://i.ytimg.com/vi/' . self::YOUTUBE_VIDEO_ID . '/hqdefault.jpg', $data->thumbnail_url );
    516546                $this->assertEquals( $data->width, $request['maxwidth'] );
    class Test_oEmbed_Controller extends WP_UnitTestCase { 
    552582                $data = $response->get_data();
    553583                $this->assertEquals( $data['code'], 'rest_invalid_param' );
    554584        }
     585
     586        /**
     587         * @ticket 45142
     588         */
     589        function test_proxy_with_internal_url() {
     590                wp_set_current_user( self::$editor );
     591
     592                $user = self::factory()->user->create_and_get( array(
     593                        'display_name' => 'John Doe',
     594                ) );
     595                $post = self::factory()->post->create_and_get( array(
     596                        'post_author' => $user->ID,
     597                        'post_title'  => 'Hello World',
     598                ) );
     599
     600                $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' );
     601                $request->set_param( 'url', get_permalink( $post->ID ) );
     602                $request->set_param( 'maxwidth', 400 );
     603
     604                $response = $this->server->dispatch( $request );
     605                $data     = $response->get_data();
     606
     607                $data = (array) $data;
     608
     609                $this->assertNotEmpty( $data );
     610
     611                $this->assertArrayHasKey( 'version', $data );
     612                $this->assertArrayHasKey( 'provider_name', $data );
     613                $this->assertArrayHasKey( 'provider_url', $data );
     614                $this->assertArrayHasKey( 'author_name', $data );
     615                $this->assertArrayHasKey( 'author_url', $data );
     616                $this->assertArrayHasKey( 'title', $data );
     617                $this->assertArrayHasKey( 'type', $data );
     618                $this->assertArrayHasKey( 'width', $data );
     619
     620                $this->assertEquals( '1.0', $data['version'] );
     621                $this->assertEquals( get_bloginfo( 'name' ), $data['provider_name'] );
     622                $this->assertEquals( get_home_url(), $data['provider_url'] );
     623                $this->assertEquals( $user->display_name, $data['author_name'] );
     624                $this->assertEquals( get_author_posts_url( $user->ID, $user->user_nicename ), $data['author_url'] );
     625                $this->assertEquals( $post->post_title, $data['title'] );
     626                $this->assertEquals( 'rich', $data['type'] );
     627                $this->assertTrue( $data['width'] <= $request['maxwidth'] );
     628        }
     629
     630        /**
     631         * @ticket 45142
     632         */
     633        function test_proxy_with_static_front_page_url() {
     634                wp_set_current_user( self::$editor );
     635
     636                $post = self::factory()->post->create_and_get( array(
     637                        'post_title'  => 'Front page',
     638                        'post_type'   => 'page',
     639                        'post_author' => 0,
     640                ) );
     641
     642                update_option( 'show_on_front', 'page' );
     643                update_option( 'page_on_front', $post->ID );
     644
     645                $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' );
     646                $request->set_param( 'url', home_url() );
     647                $request->set_param( 'maxwidth', 400 );
     648
     649                $response = $this->server->dispatch( $request );
     650                $data     = $response->get_data();
     651
     652                $this->assertInternalType( 'object', $data );
     653
     654                $data = (array) $data;
     655
     656                $this->assertNotEmpty( $data );
     657
     658                $this->assertArrayHasKey( 'version', $data );
     659                $this->assertArrayHasKey( 'provider_name', $data );
     660                $this->assertArrayHasKey( 'provider_url', $data );
     661                $this->assertArrayHasKey( 'author_name', $data );
     662                $this->assertArrayHasKey( 'author_url', $data );
     663                $this->assertArrayHasKey( 'title', $data );
     664                $this->assertArrayHasKey( 'type', $data );
     665                $this->assertArrayHasKey( 'width', $data );
     666
     667                $this->assertEquals( '1.0', $data['version'] );
     668                $this->assertEquals( get_bloginfo( 'name' ), $data['provider_name'] );
     669                $this->assertEquals( get_home_url(), $data['provider_url'] );
     670                $this->assertEquals( get_bloginfo( 'name' ), $data['author_name'] );
     671                $this->assertEquals( get_home_url(), $data['author_url'] );
     672                $this->assertEquals( $post->post_title, $data['title'] );
     673                $this->assertEquals( 'rich', $data['type'] );
     674                $this->assertTrue( $data['width'] <= $request['maxwidth'] );
     675
     676                update_option( 'show_on_front', 'posts' );
     677        }
     678
     679        /**
     680         * @ticket 45142
     681         */
     682        public function test_proxy_filters_result_of_untrusted_oembed_provider() {
     683                wp_set_current_user( self::$editor );
     684
     685                $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' );
     686                $request->set_param( 'url', self::UNTRUSTED_PROVIDER_URL );
     687                $request->set_param( 'maxwidth', 456 );
     688                $request->set_param( 'maxheight', 789 );
     689                $request->set_param( '_wpnonce', wp_create_nonce( 'wp_rest' ) );
     690
     691                $response = $this->server->dispatch( $request );
     692                $data     = $response->get_data();
     693
     694                $this->assertInternalType( 'object', $data );
     695
     696                $this->markTestIncomplete( 'Need to test that the resulting HTML is passed through the oembed_dataparse filter' );
     697        }
     698
     699        /**
     700         * @ticket 45142
     701         */
     702        public function test_proxy_does_not_filter_result_of_trusted_oembed_provider() {
     703                wp_set_current_user( self::$editor );
     704
     705                $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' );
     706                $request->set_param( 'url', 'https://www.youtube.com/watch?v=' . self::YOUTUBE_VIDEO_ID );
     707                $request->set_param( 'maxwidth', 456 );
     708                $request->set_param( 'maxheight', 789 );
     709                $request->set_param( '_wpnonce', wp_create_nonce( 'wp_rest' ) );
     710
     711                $response = $this->server->dispatch( $request );
     712                $data     = $response->get_data();
     713
     714                $this->assertInternalType( 'object', $data );
     715
     716                $this->assertStringStartsWith( '<b>Unfiltered</b>', $data->html );
     717        }
    555718}