Make WordPress Core

Changeset 57645


Ignore:
Timestamp:
02/16/2024 11:32:48 PM (3 months ago)
Author:
peterwilsoncc
Message:

Canonical: Limit post types searched by redirect_guess_404_permalink().

Limit the post types searched in redirect_guess_404_permalink() to public, searchable post types. This prevents redirects to 404 pages and the exposure of private post type slugs.

Props francescocarlucci, peterwilsoncc, rajinsharwar.
Fixes #59795.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/canonical.php

    r57357 r57645  
    950950
    951951    if ( get_query_var( 'name' ) ) {
     952        $publicly_viewable_statuses   = array_filter( get_post_stati(), 'is_post_status_viewable' );
     953        $publicly_viewable_post_types = array_filter( get_post_types( array( 'exclude_from_search' => false ) ), 'is_post_type_viewable' );
     954
    952955        /**
    953956         * Filters whether to perform a strict guess for a 404 redirect.
     
    970973        if ( get_query_var( 'post_type' ) ) {
    971974            if ( is_array( get_query_var( 'post_type' ) ) ) {
     975                $post_types = array_intersect( get_query_var( 'post_type' ), $publicly_viewable_post_types );
     976                if ( empty( $post_types ) ) {
     977                    return false;
     978                }
    972979                $where .= " AND post_type IN ('" . join( "', '", esc_sql( get_query_var( 'post_type' ) ) ) . "')";
    973980            } else {
     981                if ( ! in_array( get_query_var( 'post_type' ), $publicly_viewable_post_types, true ) ) {
     982                    return false;
     983                }
    974984                $where .= $wpdb->prepare( ' AND post_type = %s', get_query_var( 'post_type' ) );
    975985            }
    976986        } else {
    977             $where .= " AND post_type IN ('" . implode( "', '", get_post_types( array( 'public' => true ) ) ) . "')";
     987            $where .= " AND post_type IN ('" . implode( "', '", esc_sql( $publicly_viewable_post_types ) ) . "')";
    978988        }
    979989
     
    988998        }
    989999
    990         $publicly_viewable_statuses = array_filter( get_post_stati(), 'is_post_status_viewable' );
    9911000        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    9921001        $post_id = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE $where AND post_status IN ('" . implode( "', '", esc_sql( $publicly_viewable_statuses ) ) . "')" );
  • trunk/tests/phpunit/tests/canonical.php

    r57357 r57645  
    1111class Tests_Canonical extends WP_Canonical_UnitTestCase {
    1212
     13    public static $private_cpt_post;
     14
     15    public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
     16        // Set up fixtures in WP_Canonical_UnitTestCase.
     17        parent::wpSetUpBeforeClass( $factory );
     18
     19        self::set_up_custom_post_types();
     20        self::$private_cpt_post = $factory->post->create(
     21            array(
     22                'post_type'  => 'wp_tests_private',
     23                'post_title' => 'private-cpt-post',
     24            )
     25        );
     26    }
     27
    1328    public function set_up() {
    1429        parent::set_up();
    1530        wp_set_current_user( self::$author_id );
     31        self::set_up_custom_post_types();
    1632
    1733        update_option( 'wp_attachment_pages_enabled', 1 );
     34    }
     35
     36    /**
     37     * Register custom post type for tests.
     38     *
     39     * Register non publicly queryable post type with public set to true.
     40     *
     41     * These arguments are intentionally contradictory for the test associated
     42     * with ticket #59795.
     43     */
     44    public static function set_up_custom_post_types() {
     45        register_post_type(
     46            'wp_tests_private',
     47            array(
     48                'public'             => true,
     49                'publicly_queryable' => false,
     50            )
     51        );
    1852    }
    1953
     
    344378     *
    345379     * @ticket 43056
    346      */
    347     public function test_redirect_guess_404_permalink_post_types() {
    348         /*
    349          * Sample-page is intentionally missspelt as sample-pag to ensure
    350          * the 404 post permalink guessing runs.
    351          *
    352          * Please do not correct the apparent typo.
    353          */
    354 
    355         // String format post type.
    356         $this->assertCanonical( '/?name=sample-pag&post_type=page', '/sample-page/' );
    357         // Array formatted post type or types.
    358         $this->assertCanonical( '/?name=sample-pag&post_type[]=page', '/sample-page/' );
    359         $this->assertCanonical( '/?name=sample-pag&post_type[]=page&post_type[]=post', '/sample-page/' );
     380     * @ticket 59795
     381     *
     382     * @dataProvider data_redirect_guess_404_permalink_post_types
     383     */
     384    public function test_redirect_guess_404_permalink_post_types( $original_url, $expected ) {
     385        $this->assertCanonical( $original_url, $expected );
     386    }
     387
     388    /**
     389     * Data provider for test_redirect_guess_404_permalink_post_types().
     390     *
     391     * In the original URLs the post names are intentionally misspelled
     392     * to test the redirection.
     393     *
     394     * Please do not correct the apparent typos.
     395     *
     396     * @return array[]
     397     */
     398    public function data_redirect_guess_404_permalink_post_types() {
     399        return array(
     400            'single string formatted post type'    => array(
     401                'original_url' => '/?name=sample-pag&post_type=page',
     402                'expected'     => '/sample-page/',
     403            ),
     404            'single array formatted post type'     => array(
     405                'original_url' => '/?name=sample-pag&post_type[]=page',
     406                'expected'     => '/sample-page/',
     407            ),
     408            'multiple array formatted post type'   => array(
     409                'original_url' => '/?name=sample-pag&post_type[]=page&post_type[]=post',
     410                'expected'     => '/sample-page/',
     411            ),
     412            'do not redirect to private post type' => array(
     413                'original_url' => '/?name=private-cpt-po&post_type[]=wp_tests_private',
     414                'expected'     => '/?name=private-cpt-po&post_type[]=wp_tests_private',
     415            ),
     416        );
    360417    }
    361418
Note: See TracChangeset for help on using the changeset viewer.