Make WordPress Core


Ignore:
Timestamp:
04/02/2026 01:23:45 AM (4 months ago)
Author:
peterwilsoncc
Message:

REST API: Harden Real Time Collaboration endpoint.

Adds additional validation and permission checks the the Real Time Collaboration endpoint to ensure only input in the expected format is supported.

Props czarate, westonruter, joefusco.
Fixes #64890.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/collaboration/class-wp-http-polling-sync-server.php

    r61839 r62198  
    3939
    4040        /**
     41         * Maximum total size (in bytes) of the request body.
     42         *
     43         * @since 7.0.0
     44         * @var int
     45         */
     46        const MAX_BODY_SIZE = 16 * MB_IN_BYTES;
     47
     48        /**
     49         * Maximum number of rooms allowed per request.
     50         *
     51         * @since 7.0.0
     52         * @var int
     53         */
     54        const MAX_ROOMS_PER_REQUEST = 50;
     55
     56        /**
     57         * Maximum length of a single update data string.
     58         *
     59         * @since 7.0.0
     60         * @var int
     61         */
     62        const MAX_UPDATE_DATA_SIZE = MB_IN_BYTES;
     63
     64        /**
    4165         * Sync update type: compaction.
    4266         *
     
    97121                        'properties' => array(
    98122                                'data' => array(
    99                                         'type'     => 'string',
    100                                         'required' => true,
     123                                        'type'      => 'string',
     124                                        'required'  => true,
     125                                        'maxLength' => self::MAX_UPDATE_DATA_SIZE,
    101126                                ),
    102127                                'type' => array(
     
    150175                                'callback'            => array( $this, 'handle_request' ),
    151176                                'permission_callback' => array( $this, 'check_permissions' ),
     177                                'validate_callback'   => array( $this, 'validate_request' ),
    152178                                'args'                => array(
    153179                                        'rooms' => array(
     
    156182                                                        'type'       => 'object',
    157183                                                ),
     184                                                'maxItems' => self::MAX_ROOMS_PER_REQUEST,
    158185                                                'required' => true,
    159186                                                'type'     => 'array',
     
    225252
    226253        /**
     254         * Validates that the request body does not exceed the maximum allowed size.
     255         *
     256         * Runs as the route-level validate_callback, after per-arg schema
     257         * validation has already passed.
     258         *
     259         * @since 7.0.0
     260         *
     261         * @param WP_REST_Request $request The REST request.
     262         * @return true|WP_Error True if valid, WP_Error if the body is too large.
     263         */
     264        public function validate_request( WP_REST_Request $request ) {
     265                $body = $request->get_body();
     266                if ( is_string( $body ) && strlen( $body ) > self::MAX_BODY_SIZE ) {
     267                        return new WP_Error(
     268                                'rest_sync_body_too_large',
     269                                __( 'Request body is too large.' ),
     270                                array( 'status' => 413 )
     271                        );
     272                }
     273
     274                return true;
     275        }
     276
     277        /**
    227278         * Handles request: stores sync updates and awareness data, and returns
    228279         * updates the client is missing.
     
    279330         * @param string      $entity_kind The entity kind, e.g. 'postType', 'taxonomy', 'root'.
    280331         * @param string      $entity_name The entity name, e.g. 'post', 'category', 'site'.
    281          * @param string|null $object_id   The object ID / entity key for single entities, null for collections.
     332         * @param string|null $object_id   The numeric object ID / entity key for single entities, null for collections.
    282333         * @return bool True if user has permission, otherwise false.
    283334         */
    284335        private function can_user_sync_entity_type( string $entity_kind, string $entity_name, ?string $object_id ): bool {
    285                 // Handle single post type entities with a defined object ID.
    286                 if ( 'postType' === $entity_kind && is_numeric( $object_id ) ) {
    287                         return current_user_can( 'edit_post', (int) $object_id );
    288                 }
    289 
    290                 // Handle single taxonomy term entities with a defined object ID.
    291                 if ( 'taxonomy' === $entity_kind && is_numeric( $object_id ) ) {
    292                         $taxonomy = get_taxonomy( $entity_name );
    293                         return isset( $taxonomy->cap->assign_terms ) && current_user_can( $taxonomy->cap->assign_terms );
    294                 }
    295 
    296                 // Handle single comment entities with a defined object ID.
    297                 if ( 'root' === $entity_kind && 'comment' === $entity_name && is_numeric( $object_id ) ) {
    298                         return current_user_can( 'edit_comment', (int) $object_id );
     336                if ( is_string( $object_id ) ) {
     337                        if ( ! ctype_digit( $object_id ) ) {
     338                                return false;
     339                        }
     340                        $object_id = (int) $object_id;
     341                }
     342                if ( null !== $object_id && $object_id <= 0 ) {
     343                        // Object ID must be numeric if provided.
     344                        return false;
     345                }
     346
     347                // Validate permissions for the provided object ID.
     348                if ( is_int( $object_id ) ) {
     349                        // Handle single post type entities with a defined object ID.
     350                        if ( 'postType' === $entity_kind ) {
     351                                if ( get_post_type( $object_id ) !== $entity_name ) {
     352                                        // Post is not of the specified post type.
     353                                        return false;
     354                                }
     355                                return current_user_can( 'edit_post', $object_id );
     356                        }
     357
     358                        // Handle single taxonomy term entities with a defined object ID.
     359                        if ( 'taxonomy' === $entity_kind ) {
     360                                $term_exists = term_exists( $object_id, $entity_name );
     361                                if ( ! is_array( $term_exists ) || ! isset( $term_exists['term_id'] ) ) {
     362                                        // Either term doesn't exist OR term is not in specified taxonomy.
     363                                        return false;
     364                                }
     365
     366                                return current_user_can( 'edit_term', $object_id );
     367                        }
     368
     369                        // Handle single comment entities with a defined object ID.
     370                        if ( 'root' === $entity_kind && 'comment' === $entity_name ) {
     371                                return current_user_can( 'edit_comment', $object_id );
     372                        }
    299373                }
    300374
Note: See TracChangeset for help on using the changeset viewer.