From 2972db4ba55fbe0bdcd76f2e41231c7077bc4aed Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Tue, 17 May 2022 19:01:20 +0000 Subject: [PATCH] Users: Fail gracefully when checking mapped capabilities without providing the required object ID. This avoids an `Undefined array key 0` PHP warning for `current_user_can()` capability checks that require a specific object to check against but an object ID was not passed. A `_doing_it_wrong()` notice is also added, so that developers and site administrators are aware that the capability mapping is failing in the absence of the required object ID. The list of mapped capabilities that require an object ID: * `delete_post` / `delete_page` * `edit_post` / `edit_page` * `read_post` / `read_page` * `publish_post` * `edit_(post|comment|term|user)_meta` / `delete_*_meta` / `add_*_meta` * `edit_comment` * `edit_term` / `delete_term` / `assign_term` Follow-up to [34091], [34113], [47178]. Props jeherve, peterwilsoncc, henry.wright, johnbillion, mattheweppelsheimer, hellofromTonya, JeffPaul, azouamauriac, Ninos Ego, TobiasBg, wpsmith, GaryJ, nacin, johnstonphilip, azaozz, SergeyBiryukov. Fixes #44591. Built from https://develop.svn.wordpress.org/trunk@53408 git-svn-id: http://core.svn.wordpress.org/trunk@52997 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/capabilities.php | 192 +++++++++++++++++++++++++++++++++-- wp-includes/version.php | 2 +- 2 files changed, 187 insertions(+), 7 deletions(-) diff --git a/wp-includes/capabilities.php b/wp-includes/capabilities.php index 2d5cd9a958..5eea17197a 100644 --- a/wp-includes/capabilities.php +++ b/wp-includes/capabilities.php @@ -73,6 +73,25 @@ function map_meta_cap( $cap, $user_id, ...$args ) { break; case 'delete_post': case 'delete_page': + if ( ! isset( $args[0] ) ) { + if ( 'delete_post' === $cap ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific post.' ); + } else { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific page.' ); + } + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + $post = get_post( $args[0] ); if ( ! $post ) { $caps[] = 'do_not_allow'; @@ -92,7 +111,18 @@ function map_meta_cap( $cap, $user_id, ...$args ) { $post_type = get_post_type_object( $post->post_type ); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */ - _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' ); + $message = __( '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.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( + $message, + '' . $post->post_type . '', + '' . $cap . '' + ), + '4.4.0' + ); + $caps[] = 'edit_others_posts'; break; } @@ -146,6 +176,25 @@ function map_meta_cap( $cap, $user_id, ...$args ) { // edit_others_posts. case 'edit_post': case 'edit_page': + if ( ! isset( $args[0] ) ) { + if ( 'edit_post' === $cap ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific post.' ); + } else { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific page.' ); + } + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + $post = get_post( $args[0] ); if ( ! $post ) { $caps[] = 'do_not_allow'; @@ -163,7 +212,18 @@ function map_meta_cap( $cap, $user_id, ...$args ) { $post_type = get_post_type_object( $post->post_type ); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */ - _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' ); + $message = __( '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.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( + $message, + '' . $post->post_type . '', + '' . $cap . '' + ), + '4.4.0' + ); + $caps[] = 'edit_others_posts'; break; } @@ -215,6 +275,25 @@ function map_meta_cap( $cap, $user_id, ...$args ) { break; case 'read_post': case 'read_page': + if ( ! isset( $args[0] ) ) { + if ( 'read_post' === $cap ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific post.' ); + } else { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific page.' ); + } + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + $post = get_post( $args[0] ); if ( ! $post ) { $caps[] = 'do_not_allow'; @@ -232,7 +311,18 @@ function map_meta_cap( $cap, $user_id, ...$args ) { $post_type = get_post_type_object( $post->post_type ); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */ - _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' ); + $message = __( '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.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( + $message, + '' . $post->post_type . '', + '' . $cap . '' + ), + '4.4.0' + ); + $caps[] = 'edit_others_posts'; break; } @@ -249,7 +339,18 @@ function map_meta_cap( $cap, $user_id, ...$args ) { $status_obj = get_post_status_object( get_post_status( $post ) ); if ( ! $status_obj ) { /* translators: 1: Post status, 2: Capability name. */ - _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), get_post_status( $post ), $cap ), '5.4.0' ); + $message = __( 'The post status %1$s is not registered, so it may not be reliable to check the capability %2$s against a post with that status.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( + $message, + '' . get_post_status( $post ) . '', + '' . $cap . '' + ), + '5.4.0' + ); + $caps[] = 'edit_others_posts'; break; } @@ -268,6 +369,20 @@ function map_meta_cap( $cap, $user_id, ...$args ) { } break; case 'publish_post': + if ( ! isset( $args[0] ) ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific post.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + $post = get_post( $args[0] ); if ( ! $post ) { $caps[] = 'do_not_allow'; @@ -277,7 +392,18 @@ function map_meta_cap( $cap, $user_id, ...$args ) { $post_type = get_post_type_object( $post->post_type ); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */ - _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' ); + $message = __( '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.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( + $message, + '' . $post->post_type . '', + '' . $cap . '' + ), + '4.4.0' + ); + $caps[] = 'edit_others_posts'; break; } @@ -297,7 +423,33 @@ function map_meta_cap( $cap, $user_id, ...$args ) { case 'delete_user_meta': case 'add_user_meta': $object_type = explode( '_', $cap )[1]; - $object_id = (int) $args[0]; + + if ( ! isset( $args[0] ) ) { + if ( 'post' === $object_type ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific post.' ); + } elseif ( 'comment' === $object_type ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific comment.' ); + } elseif ( 'term' === $object_type ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific term.' ); + } else { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific user.' ); + } + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + + $object_id = (int) $args[0]; $object_subtype = get_object_subtype( $object_type, $object_id ); @@ -392,6 +544,20 @@ function map_meta_cap( $cap, $user_id, ...$args ) { } break; case 'edit_comment': + if ( ! isset( $args[0] ) ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific comment.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + $comment = get_comment( $args[0] ); if ( ! $comment ) { $caps[] = 'do_not_allow'; @@ -532,6 +698,20 @@ function map_meta_cap( $cap, $user_id, ...$args ) { case 'edit_term': case 'delete_term': case 'assign_term': + if ( ! isset( $args[0] ) ) { + /* translators: %s: Capability name. */ + $message = __( 'When checking for the %s capability, you must always check it against a specific term.' ); + + _doing_it_wrong( + __FUNCTION__, + sprintf( $message, '' . $cap . '' ), + '6.1.0' + ); + + $caps[] = 'do_not_allow'; + break; + } + $term_id = (int) $args[0]; $term = get_term( $term_id ); if ( ! $term || is_wp_error( $term ) ) { diff --git a/wp-includes/version.php b/wp-includes/version.php index 5f01ab0704..871c346f9b 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.1-alpha-53404'; +$wp_version = '6.1-alpha-53408'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.