Make WordPress Core

Changeset 34091


Ignore:
Timestamp:
09/12/2015 09:26:57 PM (9 years ago)
Author:
boonebgorges
Message:

Fail gracefully when checking mapped cap against unregistered post type.

Post type objects are reponsible for mapping their capabilities to core caps.
As a result, when the post type is no longer registered, the caps are no
longer mapped. This causes problems when a post is left in the database after
the post type is no longer present, and WP does an 'edit_post' or other cap
check against it: a PHP notice is thrown, and the cap check always fails.

As a more graceful fallback, we map all post-type-dependent caps onto
'edit_others_posts', which allows highly privileged users to be able to
access orphaned content (such as comments belonging to disabled post types),
while minimizing the possibility of unintended privilege escalation.

We also add a _doing_it_wrong() notice, so that developers and site
administrators are aware that the cap mapping is failing in the absence of
the registered post type.

Props mitchoyoshitaka, DrewAPicture, imath, codeelite, boonebgorges, nofearinc, SergeyBiryukov, jorbin, dlh.
Fixes #16956.

Location:
trunk
Files:
2 edited

Legend:

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

    r33988 r34091  
    5454
    5555        $post_type = get_post_type_object( $post->post_type );
     56        if ( ! $post_type ) {
     57            /* translators: 1: post type, 2: capability name */
     58            _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     59            $caps[] = 'edit_others_posts';
     60            break;
     61        }
    5662
    5763        if ( ! $post_type->map_meta_cap ) {
     
    102108
    103109        $post_type = get_post_type_object( $post->post_type );
     110        if ( ! $post_type ) {
     111            /* translators: 1: post type, 2: capability name */
     112            _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     113            $caps[] = 'edit_others_posts';
     114            break;
     115        }
    104116
    105117        if ( ! $post_type->map_meta_cap ) {
     
    144156
    145157        $post_type = get_post_type_object( $post->post_type );
     158        if ( ! $post_type ) {
     159            /* translators: 1: post type, 2: capability name */
     160            _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     161            $caps[] = 'edit_others_posts';
     162            break;
     163        }
    146164
    147165        if ( ! $post_type->map_meta_cap ) {
     
    170188        $post = get_post( $args[0] );
    171189        $post_type = get_post_type_object( $post->post_type );
     190        if ( ! $post_type ) {
     191            /* translators: 1: post type, 2: capability name */
     192            _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     193            $caps[] = 'edit_others_posts';
     194            break;
     195        }
    172196
    173197        $caps[] = $post_type->cap->publish_posts;
  • trunk/tests/phpunit/tests/user/capabilities.php

    r33988 r34091  
    995995        $this->assertFalse( current_user_can( 'edit_user', $super_admin->ID ) );
    996996    }
     997
     998    /**
     999     * @ticket 16956
     1000     */
     1001    function test_require_edit_others_posts_if_post_type_doesnt_exist() {
     1002        register_post_type( 'existed' );
     1003        $post_id = $this->factory->post->create( array( 'post_type' => 'existed' ) );
     1004        _unregister_post_type( 'existed' );
     1005
     1006        $subscriber_id = $this->factory->user->create( array( 'role' => 'subscriber' ) );
     1007        $editor_id = $this->factory->user->create( array( 'role' => 'editor' ) );
     1008
     1009        $this->setExpectedIncorrectUsage( 'map_meta_cap' );
     1010        foreach ( array( 'delete_post', 'edit_post', 'read_post', 'publish_post' ) as $cap ) {
     1011            wp_set_current_user( $subscriber_id );
     1012            $this->assertSame( array( 'edit_others_posts' ), map_meta_cap( $cap, $subscriber_id, $post_id ) );
     1013            $this->assertFalse( current_user_can( $cap, $post_id ) );
     1014
     1015            wp_set_current_user( $editor_id );
     1016            $this->assertSame( array( 'edit_others_posts' ), map_meta_cap( $cap, $editor_id, $post_id ) );
     1017            $this->assertTrue( current_user_can( $cap, $post_id ) );
     1018        }
     1019    }
    9971020}
Note: See TracChangeset for help on using the changeset viewer.