Make WordPress Core

Changeset 48526


Ignore:
Timestamp:
07/21/2020 12:01:10 PM (6 years ago)
Author:
TimothyBlynJacobs
Message:

REST API: Issue a _doing_it_wrong when registering a route without a permission callback.

The REST API treats routes without a permission_callback as public. Because this happens without any warning to the user, if the permission callback is unintentionally omitted or misspelled, the endpoint can end up being available to the public. Such a scenario has happened multiple times in the wild, and the results can be catostrophic when it occurs.

For REST API routes that are intended to be public, it is recommended to set the permission callback to the __return_true built in function.

Fixes #50075.
Props rmccue, sorenbronsted, whyisjake, SergeyBiryukov, TimothyBlynJacobs.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-oembed-controller.php

    r48135 r48526  
    3737                        array(
    3838                                array(
    39                                         'methods'  => WP_REST_Server::READABLE,
    40                                         'callback' => array( $this, 'get_item' ),
    41                                         'args'     => array(
     39                                        'methods'             => WP_REST_Server::READABLE,
     40                                        'callback'            => array( $this, 'get_item' ),
     41                                        'permission_callback' => '__return_true',
     42                                        'args'                => array(
    4243                                                'url'      => array(
    4344                                                        'description' => __( 'The URL of the resource for which to fetch oEmbed data.' ),
  • trunk/src/wp-includes/rest-api.php

    r48367 r48526  
    8989                $arg_group         = array_merge( $defaults, $arg_group );
    9090                $arg_group['args'] = array_merge( $common_args, $arg_group['args'] );
     91
     92                if ( ! isset( $arg_group['permission_callback'] ) ) {
     93                        _doing_it_wrong(
     94                                __FUNCTION__,
     95                                sprintf(
     96                                        /* translators: 1. The REST API route being registered. 2. The argument name. 3. The suggested function name. */
     97                                        __( 'The REST API route definition for %1$s is missing the required %2$s argument. For REST API routes that are intended to be public, use %3$s as the permission callback.', 'LION' ),
     98                                        '<code>' . $clean_namespace . '/' . trim( $route, '/' ) . '</code>',
     99                                        '<code>permission_callback</code>',
     100                                        '<code>__return_true</code>'
     101                                ),
     102                                '5.5.0'
     103                        );
     104                }
    91105        }
    92106
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php

    r47122 r48526  
    6161                                ),
    6262                                array(
    63                                         'methods'  => WP_REST_Server::READABLE,
    64                                         'callback' => array( $this, 'get_item' ),
    65                                         'args'     => array(
     63                                        'methods'             => WP_REST_Server::READABLE,
     64                                        'callback'            => array( $this, 'get_item' ),
     65                                        'permission_callback' => '__return_true',
     66                                        'args'                => array(
    6667                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
    6768                                        ),
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php

    r48195 r48526  
    117117                        array(
    118118                                array(
    119                                         'methods'  => WP_REST_Server::READABLE,
    120                                         'callback' => array( $this, 'get_current_item' ),
    121                                         'args'     => array(
     119                                        'methods'             => WP_REST_Server::READABLE,
     120                                        'permission_callback' => '__return_true',
     121                                        'callback'            => array( $this, 'get_current_item' ),
     122                                        'args'                => array(
    122123                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
    123124                                        ),
  • trunk/tests/phpunit/tests/rest-api.php

    r48365 r48526  
    8585                        '/test',
    8686                        array(
    87                                 'methods'  => array( 'GET' ),
    88                                 'callback' => '__return_null',
     87                                'methods'             => array( 'GET' ),
     88                                'callback'            => '__return_null',
     89                                'permission_callback' => '__return_true',
    8990                        )
    9091                );
     
    121122                        array(
    122123                                array(
    123                                         'methods'  => array( 'GET' ),
    124                                         'callback' => '__return_null',
    125                                 ),
    126                                 array(
    127                                         'methods'  => array( 'POST' ),
    128                                         'callback' => '__return_null',
     124                                        'methods'             => array( 'GET' ),
     125                                        'callback'            => '__return_null',
     126                                        'permission_callback' => '__return_true',
     127                                ),
     128                                array(
     129                                        'methods'             => array( 'POST' ),
     130                                        'callback'            => '__return_null',
     131                                        'permission_callback' => '__return_true',
    129132                                ),
    130133                        )
     
    161164                        '/test',
    162165                        array(
    163                                 'methods'  => array( 'GET' ),
    164                                 'callback' => '__return_null',
     166                                'methods'             => array( 'GET' ),
     167                                'callback'            => '__return_null',
     168                                'permission_callback' => '__return_true',
    165169                        )
    166170                );
     
    169173                        '/test',
    170174                        array(
    171                                 'methods'  => array( 'POST' ),
    172                                 'callback' => '__return_null',
     175                                'methods'             => array( 'POST' ),
     176                                'callback'            => '__return_null',
     177                                'permission_callback' => '__return_true',
    173178                        )
    174179                );
     
    188193                        '/test',
    189194                        array(
    190                                 'methods'      => array( 'GET' ),
    191                                 'callback'     => '__return_null',
    192                                 'should_exist' => false,
     195                                'methods'             => array( 'GET' ),
     196                                'callback'            => '__return_null',
     197                                'permission_callback' => '__return_true',
     198                                'should_exist'        => false,
    193199                        )
    194200                );
     
    197203                        '/test',
    198204                        array(
    199                                 'methods'      => array( 'POST' ),
    200                                 'callback'     => '__return_null',
    201                                 'should_exist' => true,
     205                                'methods'             => array( 'POST' ),
     206                                'callback'            => '__return_null',
     207                                'permission_callback' => '__return_true',
     208                                'should_exist'        => true,
    202209                        ),
    203210                        true
     
    224231                        '/test-empty-namespace',
    225232                        array(
    226                                 'methods'  => array( 'POST' ),
    227                                 'callback' => '__return_null',
     233                                'methods'             => array( 'POST' ),
     234                                'callback'            => '__return_null',
     235                                'permission_callback' => '__return_true',
    228236                        ),
    229237                        true
     
    243251                        '',
    244252                        array(
    245                                 'methods'  => array( 'POST' ),
    246                                 'callback' => '__return_null',
     253                                'methods'             => array( 'POST' ),
     254                                'callback'            => '__return_null',
     255                                'permission_callback' => '__return_true',
    247256                        ),
    248257                        true
     
    265274                        '/test',
    266275                        array(
    267                                 'methods'  => array( 'GET' ),
    268                                 'callback' => '__return_null',
     276                                'methods'             => array( 'GET' ),
     277                                'callback'            => '__return_null',
     278                                'permission_callback' => '__return_true',
    269279                        )
    270280                );
     
    283293                        '/test',
    284294                        array(
    285                                 'methods'  => 'GET',
    286                                 'callback' => '__return_null',
     295                                'methods'             => 'GET',
     296                                'callback'            => '__return_null',
     297                                'permission_callback' => '__return_true',
    287298                        )
    288299                );
     
    301312                        '/test',
    302313                        array(
    303                                 'methods'  => array( 'GET', 'POST' ),
    304                                 'callback' => '__return_null',
     314                                'methods'             => array( 'GET', 'POST' ),
     315                                'callback'            => '__return_null',
     316                                'permission_callback' => '__return_true',
    305317                        )
    306318                );
     
    325337                        '/test',
    326338                        array(
    327                                 'methods'  => 'GET,POST',
    328                                 'callback' => '__return_null',
     339                                'methods'             => 'GET,POST',
     340                                'callback'            => '__return_null',
     341                                'permission_callback' => '__return_true',
    329342                        )
    330343                );
     
    346359                        '/test',
    347360                        array(
    348                                 'methods'  => 'GET,POST',
    349                                 'callback' => '__return_null',
     361                                'methods'             => 'GET,POST',
     362                                'callback'            => '__return_null',
     363                                'permission_callback' => '__return_true',
    350364                        )
    351365                );
     
    368382                        '/test',
    369383                        array(
    370                                 'methods'  => 'GET,POST',
    371                                 'callback' => '__return_true',
     384                                'methods'             => 'GET,POST',
     385                                'callback'            => '__return_true',
     386                                'permission_callback' => '__return_true',
    372387                        )
    373388                );
     
    911926                        '/test',
    912927                        array(
    913                                 'methods'  => array( 'GET' ),
    914                                 'callback' => '__return_null',
     928                                'methods'             => array( 'GET' ),
     929                                'callback'            => '__return_null',
     930                                'permission_callback' => '__return_true',
    915931                        )
    916932                );
     
    9971013                        '/my-route',
    9981014                        array(
    999                                 'callback' => '__return_true',
     1015                                'callback'            => '__return_true',
     1016                                'permission_callback' => '__return_true',
    10001017                        )
    10011018                );
     
    10051022
    10061023                $this->assertTrue( rest_do_request( '/my-namespace/v1/my-route' )->get_data() );
     1024        }
     1025
     1026        /**
     1027         * @ticket 50075
     1028         */
     1029        public function test_register_route_with_missing_permission_callback_top_level_route() {
     1030                $this->setExpectedIncorrectUsage( 'register_rest_route' );
     1031
     1032                $registered = register_rest_route(
     1033                        'my-ns/v1',
     1034                        '/my-route',
     1035                        array(
     1036                                'callback' => '__return_true',
     1037                        )
     1038                );
     1039
     1040                $this->assertTrue( $registered );
     1041        }
     1042
     1043        /**
     1044         * @ticket 50075
     1045         */
     1046        public function test_register_route_with_missing_permission_callback_single_wrapped_route() {
     1047                $this->setExpectedIncorrectUsage( 'register_rest_route' );
     1048
     1049                $registered = register_rest_route(
     1050                        'my-ns/v1',
     1051                        '/my-route',
     1052                        array(
     1053                                array(
     1054                                        'callback' => '__return_true',
     1055                                ),
     1056                        )
     1057                );
     1058
     1059                $this->assertTrue( $registered );
     1060        }
     1061
     1062
     1063        /**
     1064         * @ticket 50075
     1065         */
     1066        public function test_register_route_with_missing_permission_callback_multiple_wrapped_route() {
     1067                $this->setExpectedIncorrectUsage( 'register_rest_route' );
     1068
     1069                $registered = register_rest_route(
     1070                        'my-ns/v1',
     1071                        '/my-route',
     1072                        array(
     1073                                array(
     1074                                        'callback' => '__return_true',
     1075                                ),
     1076                                array(
     1077                                        'callback'            => '__return_true',
     1078                                        'permission_callback' => '__return_true',
     1079                                ),
     1080                        )
     1081                );
     1082
     1083                $this->assertTrue( $registered );
    10071084        }
    10081085
  • trunk/tests/phpunit/tests/rest-api/rest-server.php

    r47351 r48526  
    6767                        '/test',
    6868                        array(
    69                                 'methods'  => array( 'GET' ),
    70                                 'callback' => '__return_null',
    71                                 'args'     => array(
     69                                'methods'             => array( 'GET' ),
     70                                'callback'            => '__return_null',
     71                                'permission_callback' => '__return_true',
     72                                'args'                => array(
    7273                                        'foo' => array(
    7374                                                'default' => 'bar',
     
    8990                        '/test',
    9091                        array(
    91                                 'methods'  => array( 'GET' ),
    92                                 'callback' => '__return_null',
    93                                 'args'     => array(
     92                                'methods'             => array( 'GET' ),
     93                                'callback'            => '__return_null',
     94                                'permission_callback' => '__return_true',
     95                                'args'                => array(
    9496                                        'foo' => array(
    9597                                                'default' => 'bar',
     
    111113                        '/test',
    112114                        array(
    113                                 'methods'  => array( 'GET' ),
    114                                 'callback' => '__return_null',
    115                                 'args'     => array(
     115                                'methods'             => array( 'GET' ),
     116                                'callback'            => '__return_null',
     117                                'permission_callback' => '__return_true',
     118                                'args'                => array(
    116119                                        'foo' => array(),
    117120                                ),
     
    132135                        '/test',
    133136                        array(
    134                                 'methods'  => array( 'GET' ),
    135                                 'callback' => '__return_null',
    136                                 'args'     => array(
     137                                'methods'             => array( 'GET' ),
     138                                'callback'            => '__return_null',
     139                                'permission_callback' => '__return_true',
     140                                'args'                => array(
    137141                                        'foo' => array(
    138142                                                'default' => 'bar',
     
    151155                        '/test',
    152156                        array(
    153                                 'methods'  => array( 'GET' ),
    154                                 'callback' => '__return_true',
     157                                'methods'             => array( 'GET' ),
     158                                'callback'            => '__return_true',
     159                                'permission_callback' => '__return_true',
    155160                        )
    156161                );
     
    172177                        array(
    173178                                array(
    174                                         'methods'  => array( 'HEAD' ),
    175                                         'callback' => '__return_true',
     179                                        'methods'             => array( 'HEAD' ),
     180                                        'callback'            => '__return_true',
     181                                        'permission_callback' => '__return_true',
    176182                                ),
    177183                                array(
     
    194200                        array(
    195201                                array(
    196                                         'methods'  => WP_REST_Server::READABLE,
    197                                         'callback' => '__return_false',
    198                                         'args'     => array(
     202                                        'methods'             => WP_REST_Server::READABLE,
     203                                        'callback'            => '__return_false',
     204                                        'permission_callback' => '__return_true',
     205                                        'args'                => array(
    199206                                                'data' => array(),
    200207                                        ),
     
    267274                        '/test',
    268275                        array(
    269                                 'methods'      => 'GET',
    270                                 'callback'     => '__return_null',
    271                                 'should_exist' => false,
     276                                'methods'             => 'GET',
     277                                'callback'            => '__return_null',
     278                                'permission_callback' => '__return_true',
     279                                'should_exist'        => false,
    272280                        )
    273281                );
     
    294302                        '/test',
    295303                        array(
    296                                 'methods'      => 'GET',
    297                                 'callback'     => '__return_null',
    298                                 'should_exist' => false,
     304                                'methods'             => 'GET',
     305                                'callback'            => '__return_null',
     306                                'permission_callback' => '__return_true',
     307                                'should_exist'        => false,
    299308                        )
    300309                );
     
    304313                        '/test',
    305314                        array(
    306                                 'methods'      => 'POST',
    307                                 'callback'     => '__return_null',
    308                                 'should_exist' => false,
     315                                'methods'             => 'POST',
     316                                'callback'            => '__return_null',
     317                                'permission_callback' => '__return_true',
     318                                'should_exist'        => false,
    309319                        )
    310320                );
     
    343353                        '/test',
    344354                        array(
    345                                 'methods'      => 'POST',
    346                                 'callback'     => '__return_null',
    347                                 'should_exist' => false,
     355                                'methods'             => 'POST',
     356                                'callback'            => '__return_null',
     357                                'permission_callback' => '__return_true',
     358                                'should_exist'        => false,
    348359                        )
    349360                );
     
    366377                        array(
    367378                                array(
    368                                         'methods'  => array( 'GET' ),
    369                                         'callback' => '__return_null',
     379                                        'methods'             => array( 'GET' ),
     380                                        'callback'            => '__return_null',
     381                                        'permission_callback' => '__return_true',
    370382                                ),
    371383                                array(
     
    13281340                        '/test',
    13291341                        array(
    1330                                 'methods'  => array( 'GET' ),
    1331                                 'callback' => '__return_null',
    1332                                 'args'     => array(
     1342                                'methods'             => array( 'GET' ),
     1343                                'callback'            => '__return_null',
     1344                                'permission_callback' => '__return_true',
     1345                                'args'                => array(
    13331346                                        'someinteger' => array(
    13341347                                                'validate_callback' => array( $this, '_validate_as_integer_123' ),
     
    13631376                        '/test',
    13641377                        array(
    1365                                 'methods'  => array( 'GET' ),
    1366                                 'callback' => function () {
     1378                                'methods'             => array( 'GET' ),
     1379                                'callback'            => function () {
    13671380                                        return new WP_REST_Response();
    13681381                                },
     1382                                'permission_callback' => '__return_true',
    13691383                        )
    13701384                );
     
    13841398                        '/test',
    13851399                        array(
    1386                                 'methods'  => array( 'GET' ),
    1387                                 'callback' => function () {
     1400                                'methods'             => array( 'GET' ),
     1401                                'callback'            => function () {
    13881402                                        return new WP_REST_Response( 'data', 204 );
    13891403                                },
     1404                                'permission_callback' => '__return_true',
    13901405                        )
    13911406                );
     
    14741489                        '/test',
    14751490                        array(
    1476                                 'methods'  => array( 'GET' ),
    1477                                 'callback' => function() {
     1491                                'methods'             => array( 'GET' ),
     1492                                'callback'            => function() {
    14781493                                        return new WP_REST_Response( 'data', 204 );
    14791494                                },
     1495                                'permission_callback' => '__return_true',
    14801496                        )
    14811497                );
     
    14841500                        '/test',
    14851501                        array(
    1486                                 'methods'  => array( 'GET' ),
    1487                                 'callback' => function() {
     1502                                'methods'             => array( 'GET' ),
     1503                                'callback'            => function() {
    14881504                                        return new WP_REST_Response( 'data', 204 );
    14891505                                },
     1506                                'permission_callback' => '__return_true',
    14901507                        )
    14911508                );
Note: See TracChangeset for help on using the changeset viewer.