<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[53408] trunk: Users: Fail gracefully when checking mapped capabilities without providing the required object ID.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/53408">53408</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/53408","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>SergeyBiryukov</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2022-05-17 18:59:24 +0000 (Tue, 17 May 2022)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>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 <a href="https://core.trac.wordpress.org/changeset/34091">[34091]</a>, <a href="https://core.trac.wordpress.org/changeset/34113">[34113]</a>, <a href="https://core.trac.wordpress.org/changeset/47178">[47178]</a>.

Props jeherve, peterwilsoncc, henry.wright, johnbillion, mattheweppelsheimer, hellofromTonya, JeffPaul, azouamauriac, Ninos Ego, TobiasBg, wpsmith, GaryJ, nacin, johnstonphilip, azaozz, SergeyBiryukov.
Fixes <a href="https://core.trac.wordpress.org/ticket/44591">#44591</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludescapabilitiesphp">trunk/src/wp-includes/capabilities.php</a></li>
<li><a href="#trunktestsphpunittestsusercapabilitiesphp">trunk/tests/phpunit/tests/user/capabilities.php</a></li>
<li><a href="#trunktestsphpunittestsusermapMetaCapphp">trunk/tests/phpunit/tests/user/mapMetaCap.php</a></li>
<li><a href="#trunktestsphpunittestsuserphp">trunk/tests/phpunit/tests/user.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludescapabilitiesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/capabilities.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/capabilities.php    2022-05-17 16:36:26 UTC (rev 53407)
+++ trunk/src/wp-includes/capabilities.php      2022-05-17 18:59:24 UTC (rev 53408)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -73,6 +73,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        break;
</span><span class="cx" style="display: block; padding: 0 10px">                case 'delete_post':
</span><span class="cx" style="display: block; padding: 0 10px">                case 'delete_page':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $post = get_post( $args[0] );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $caps[] = 'do_not_allow';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -92,7 +111,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $post_type = get_post_type_object( $post->post_type );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: Post type, 2: Capability name. */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                _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' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $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,
+                                               '<code>' . $post->post_type . '</code>',
+                                               '<code>' . $cap . '</code>'
+                                       ),
+                                       '4.4.0'
+                               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $caps[] = 'edit_others_posts';
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -146,6 +176,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                // edit_others_posts.
</span><span class="cx" style="display: block; padding: 0 10px">                case 'edit_post':
</span><span class="cx" style="display: block; padding: 0 10px">                case 'edit_page':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $post = get_post( $args[0] );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $caps[] = 'do_not_allow';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -163,7 +212,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $post_type = get_post_type_object( $post->post_type );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: Post type, 2: Capability name. */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                _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' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $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,
+                                               '<code>' . $post->post_type . '</code>',
+                                               '<code>' . $cap . '</code>'
+                                       ),
+                                       '4.4.0'
+                               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $caps[] = 'edit_others_posts';
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -215,6 +275,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        break;
</span><span class="cx" style="display: block; padding: 0 10px">                case 'read_post':
</span><span class="cx" style="display: block; padding: 0 10px">                case 'read_page':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $post = get_post( $args[0] );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $caps[] = 'do_not_allow';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -232,7 +311,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $post_type = get_post_type_object( $post->post_type );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: Post type, 2: Capability name. */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                _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' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $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,
+                                               '<code>' . $post->post_type . '</code>',
+                                               '<code>' . $cap . '</code>'
+                                       ),
+                                       '4.4.0'
+                               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $caps[] = 'edit_others_posts';
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -249,7 +339,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $status_obj = get_post_status_object( get_post_status( $post ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $status_obj ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: Post status, 2: Capability name. */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                _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' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $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,
+                                               '<code>' . get_post_status( $post ) . '</code>',
+                                               '<code>' . $cap . '</code>'
+                                       ),
+                                       '5.4.0'
+                               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $caps[] = 'edit_others_posts';
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -268,6 +369,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                        break;
</span><span class="cx" style="display: block; padding: 0 10px">                case 'publish_post':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $post = get_post( $args[0] );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $caps[] = 'do_not_allow';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -277,7 +392,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $post_type = get_post_type_object( $post->post_type );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $post_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                /* translators: 1: Post type, 2: Capability name. */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                _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' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $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,
+                                               '<code>' . $post->post_type . '</code>',
+                                               '<code>' . $cap . '</code>'
+                                       ),
+                                       '4.4.0'
+                               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $caps[] = 'edit_others_posts';
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -297,8 +423,34 @@
</span><span class="cx" style="display: block; padding: 0 10px">                case 'delete_user_meta':
</span><span class="cx" style="display: block; padding: 0 10px">                case 'add_user_meta':
</span><span class="cx" style="display: block; padding: 0 10px">                        $object_type = explode( '_', $cap )[1];
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $object_id   = (int) $args[0];
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
+                       $object_id = (int) $args[0];
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $object_subtype = get_object_subtype( $object_type, $object_id );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( empty( $object_subtype ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -392,6 +544,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                        break;
</span><span class="cx" style="display: block; padding: 0 10px">                case 'edit_comment':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $comment = get_comment( $args[0] );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $comment ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $caps[] = 'do_not_allow';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -532,6 +698,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">                case 'edit_term':
</span><span class="cx" style="display: block; padding: 0 10px">                case 'delete_term':
</span><span class="cx" style="display: block; padding: 0 10px">                case 'assign_term':
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        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, '<code>' . $cap . '</code>' ),
+                                       '6.1.0'
+                               );
+
+                               $caps[] = 'do_not_allow';
+                               break;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $term_id = (int) $args[0];
</span><span class="cx" style="display: block; padding: 0 10px">                        $term    = get_term( $term_id );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $term || is_wp_error( $term ) ) {
</span></span></pre></div>
<a id="trunktestsphpunittestsusercapabilitiesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/user/capabilities.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/user/capabilities.php   2022-05-17 16:36:26 UTC (rev 53407)
+++ trunk/tests/phpunit/tests/user/capabilities.php     2022-05-17 18:59:24 UTC (rev 53408)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1595,6 +1595,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $editor = self::$users['editor'];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->setExpectedIncorrectUsage( 'map_meta_cap' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $caps as $cap ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        // `null` represents a non-existent term ID.
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->assertFalse( user_can( $editor->ID, $cap, null ) );
</span></span></pre></div>
<a id="trunktestsphpunittestsusermapMetaCapphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/user/mapMetaCap.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/user/mapMetaCap.php     2022-05-17 16:36:26 UTC (rev 53407)
+++ trunk/tests/phpunit/tests/user/mapMetaCap.php       2022-05-17 18:59:24 UTC (rev 53408)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3,6 +3,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * @group user
</span><span class="cx" style="display: block; padding: 0 10px">  * @group capabilities
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @covers ::map_meta_cap
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class Tests_User_MapMetaCap extends WP_UnitTestCase {
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -410,4 +411,51 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame( array( 'manage_options' ), $caps );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * @dataProvider data_meta_caps_throw_doing_it_wrong_without_required_argument_provided
+        * @ticket 44591
+        *
+        * @param string $cap The meta capability requiring an argument.
+        */
+       public function test_meta_caps_throw_doing_it_wrong_without_required_argument_provided( $cap ) {
+               $admin_user = self::$user_id;
+               $this->setExpectedIncorrectUsage( 'map_meta_cap' );
+               $this->assertContains( 'do_not_allow', map_meta_cap( $cap, $admin_user ) );
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array[] Test parameters {
+        *     @type string $cap The meta capability requiring an argument.
+        * }
+        */
+       public function data_meta_caps_throw_doing_it_wrong_without_required_argument_provided() {
+               return array(
+                       array( 'delete_post' ),
+                       array( 'delete_page' ),
+                       array( 'edit_post' ),
+                       array( 'edit_page' ),
+                       array( 'read_post' ),
+                       array( 'read_page' ),
+                       array( 'publish_post' ),
+                       array( 'edit_post_meta' ),
+                       array( 'delete_post_meta' ),
+                       array( 'add_post_meta' ),
+                       array( 'edit_comment_meta' ),
+                       array( 'delete_comment_meta' ),
+                       array( 'add_comment_meta' ),
+                       array( 'edit_term_meta' ),
+                       array( 'delete_term_meta' ),
+                       array( 'add_term_meta' ),
+                       array( 'edit_user_meta' ),
+                       array( 'delete_user_meta' ),
+                       array( 'add_user_meta' ),
+                       array( 'edit_comment' ),
+                       array( 'edit_term' ),
+                       array( 'delete_term' ),
+                       array( 'assign_term' ),
+               );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsphpunittestsuserphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/user.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/user.php        2022-05-17 16:36:26 UTC (rev 53407)
+++ trunk/tests/phpunit/tests/user.php  2022-05-17 18:59:24 UTC (rev 53408)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1974,7 +1974,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // _doing_wrong() should be called because the filter callback
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // _doing_it_wrong() should be called because the filter callback
</ins><span class="cx" style="display: block; padding: 0 10px">                 // adds a item with a 'name' that is the same as one generated by core.
</span><span class="cx" style="display: block; padding: 0 10px">                $this->setExpectedIncorrectUsage( 'wp_user_personal_data_exporter' );
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'wp_privacy_additional_user_profile_data', array( $this, 'export_additional_user_profile_data_with_dup_name' ) );
</span></span></pre>
</div>
</div>

</body>
</html>