WordPress.org

Make WordPress Core


Ignore:
Timestamp:
02/02/2021 12:38:40 AM (9 months ago)
Author:
peterwilsoncc
Message:

Canonical: Prevent ID enumeration of private post slugs.

Add check to redirect_canonical() to ensure private posts only redirect for logged in users.

Modifies the read_post mata capability to user get_post_status() rather than the post's post_status property to allow attachments to redirect based on the inherited post status.

Introduces wp_force_ugly_post_permalink() to unify the check to determine if an ugly link should be displayed in each of the functions used for determining permalinks: get_permalink(), get_post_permalink(), _get_page_link() and get_attachment_link().

Improves logic of get_attachment_link() to validate parent post and resolution of inherited post status. This is an incomplete fix of #52373 to prevent the function returning links resulting in a file not found error. Required to unblock this ticket.

Props peterwilsoncc, TimothyBlynJacobs.
See #52373.
Fixes #5272.

File:
1 edited

Legend:

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

    r49789 r50132  
    8888            break;
    8989    }
     90}
     91
     92/**
     93 * Determine whether post should always use an ugly permalink structure.
     94 *
     95 * @since 5.7.0
     96 *
     97 * @param WP_Post|int|null $post   Optional. Post ID or post object. Defaults to global $post.
     98 * @param bool|null        $sample Optional. Whether to force consideration based on sample links.
     99 *                                 If omitted, a sample link is generated if a post object is passed
     100 *                                 with the filter property set to 'sample'.
     101 * @return bool Whether to use an ugly permalink structure.
     102 */
     103function wp_force_ugly_post_permalink( $post = null, $sample = null ) {
     104    if (
     105        null === $sample &&
     106        is_object( $post ) &&
     107        isset( $post->filter ) &&
     108        'sample' === $post->filter
     109    ) {
     110        $sample = true;
     111    } else {
     112        $post   = get_post( $post );
     113        $sample = null !== $sample ? $sample : false;
     114    }
     115
     116    if ( ! $post ) {
     117        return true;
     118    }
     119
     120    $post_status_obj = get_post_status_object( get_post_status( $post ) );
     121    $post_type_obj   = get_post_type_object( get_post_type( $post ) );
     122
     123    if ( ! $post_status_obj || ! $post_type_obj ) {
     124        return true;
     125    }
     126
     127    if (
     128        // Publicly viewable links never have ugly permalinks.
     129        is_post_status_viewable( $post_status_obj ) ||
     130        (
     131            // Private posts don't have ugly links if the user can read them.
     132            $post_status_obj->private &&
     133            current_user_can( 'read_post', $post->ID )
     134        ) ||
     135        // Protected posts don't have ugly links if getting a sample URL.
     136        ( $post_status_obj->protected && $sample )
     137    ) {
     138        return false;
     139    }
     140
     141    return true;
    90142}
    91143
     
    167219    if (
    168220        $permalink &&
    169         ! in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft', 'future', 'trash' ), true )
     221        ! wp_force_ugly_post_permalink( $post )
    170222    ) {
    171223
     
    278330    $slug = $post->post_name;
    279331
    280     $draft_or_pending = get_post_status( $post ) && in_array( get_post_status( $post ), array( 'draft', 'pending', 'auto-draft', 'future' ), true );
     332    $force_ugly_link = wp_force_ugly_post_permalink( $post );
    281333
    282334    $post_type = get_post_type_object( $post->post_type );
     
    286338    }
    287339
    288     if ( ! empty( $post_link ) && ( ! $draft_or_pending || $sample ) ) {
     340    if ( ! empty( $post_link ) && ( ! $force_ugly_link || $sample ) ) {
    289341        if ( ! $leavename ) {
    290342            $post_link = str_replace( "%$post->post_type%", $slug, $post_link );
     
    292344        $post_link = home_url( user_trailingslashit( $post_link ) );
    293345    } else {
    294         if ( $post_type->query_var && ( isset( $post->post_status ) && ! $draft_or_pending ) ) {
     346        if ( $post_type->query_var && ( isset( $post->post_status ) && ! $force_ugly_link ) ) {
    295347            $post_link = add_query_arg( $post_type->query_var, $slug, '' );
    296348        } else {
     
    374426    $post = get_post( $post );
    375427
    376     $draft_or_pending = in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ), true );
     428    $force_ugly_link = wp_force_ugly_post_permalink( $post );
    377429
    378430    $link = $wp_rewrite->get_page_permastruct();
    379431
    380     if ( ! empty( $link ) && ( ( isset( $post->post_status ) && ! $draft_or_pending ) || $sample ) ) {
     432    if ( ! empty( $link ) && ( ( isset( $post->post_status ) && ! $force_ugly_link ) || $sample ) ) {
    381433        if ( ! $leavename ) {
    382434            $link = str_replace( '%pagename%', get_page_uri( $post ), $link );
     
    418470    $link = false;
    419471
    420     $post   = get_post( $post );
    421     $parent = ( $post->post_parent > 0 && $post->post_parent != $post->ID ) ? get_post( $post->post_parent ) : false;
    422     if ( $parent && ! in_array( $parent->post_type, get_post_types(), true ) ) {
    423         $parent = false;
    424     }
    425 
    426     if ( $wp_rewrite->using_permalinks() && $parent ) {
     472    $post            = get_post( $post );
     473    $force_ugly_link = wp_force_ugly_post_permalink( $post );
     474    $parent_id       = $post->post_parent;
     475    $parent          = $parent_id ? get_post( $parent_id ) : false;
     476    $parent_valid    = true; // Default for no parent.
     477    if (
     478        $parent_id &&
     479        (
     480            $post->post_parent === $post->ID ||
     481            ! $parent ||
     482            ! is_post_type_viewable( get_post_type( $parent ) )
     483        )
     484    ) {
     485        // Post is either its own parent or parent post unavailable.
     486        $parent_valid = false;
     487    }
     488
     489    if ( $force_ugly_link || ! $parent_valid ) {
     490        $link = false;
     491    } elseif ( $wp_rewrite->using_permalinks() && $parent ) {
    427492        if ( 'page' === $parent->post_type ) {
    428493            $parentlink = _get_page_link( $post->post_parent ); // Ignores page_on_front.
Note: See TracChangeset for help on using the changeset viewer.