WordPress.org

Make WordPress Core


Ignore:
Timestamp:
07/21/2020 12:01:10 PM (16 months 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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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
Note: See TracChangeset for help on using the changeset viewer.