diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 848b4e56d75a1..be24c18236d38 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -1968,6 +1968,107 @@ public function sideload_item_permissions_check( $request ) { return $this->edit_media_item_permissions_check( $request ); } + /** + * Validates that uploaded image dimensions are appropriate for the specified image size. + * + * @since 7.0.0 + * + * @param int $width Uploaded image width. + * @param int $height Uploaded image height. + * @param string $image_size The target image size name. + * @param int $attachment_id The attachment ID. + * @return true|WP_Error True if valid, WP_Error if invalid. + */ + private function validate_image_dimensions( int $width, int $height, string $image_size, int $attachment_id ) { + // All image sizes require positive dimensions. + if ( $width <= 0 || $height <= 0 ) { + return new WP_Error( + 'rest_upload_invalid_dimensions', + __( 'Uploaded image must have positive dimensions.' ), + array( 'status' => 400 ) + ); + } + + // 'original' size: should match original attachment dimensions. + if ( 'original' === $image_size ) { + $metadata = wp_get_attachment_metadata( $attachment_id, true ); + if ( is_array( $metadata ) && isset( $metadata['width'], $metadata['height'] ) ) { + $expected_width = (int) $metadata['width']; + $expected_height = (int) $metadata['height']; + + if ( $width !== $expected_width || $height !== $expected_height ) { + return new WP_Error( + 'rest_upload_dimension_mismatch', + sprintf( + /* translators: 1: Actual width, 2: actual height, 3: expected width, 4: expected height. */ + __( 'Uploaded image dimensions (%1$dx%2$d) do not match original image dimensions (%3$dx%4$d).' ), + $width, + $height, + $expected_width, + $expected_height + ), + array( 'status' => 400 ) + ); + } + } + return true; + } + + // 'full' size (PDF thumbnails) and 'scaled': no further constraints. + if ( 'full' === $image_size || 'scaled' === $image_size ) { + return true; + } + + // Regular image sizes: validate against registered size constraints. + $registered_sizes = wp_get_registered_image_subsizes(); + + if ( ! isset( $registered_sizes[ $image_size ] ) ) { + return new WP_Error( + 'rest_upload_unknown_size', + __( 'Unknown image size.' ), + array( 'status' => 400 ) + ); + } + + $size_data = $registered_sizes[ $image_size ]; + $max_width = (int) $size_data['width']; + $max_height = (int) $size_data['height']; + + // Validate dimensions don't exceed the registered size maximums. + // Allow 1px tolerance for rounding differences. + $tolerance = 1; + + if ( $max_width > 0 && $width > $max_width + $tolerance ) { + return new WP_Error( + 'rest_upload_dimension_mismatch', + sprintf( + /* translators: 1: Image size name, 2: maximum width, 3: actual width. */ + __( 'Uploaded image width (%3$d) exceeds maximum for "%1$s" size (%2$d).' ), + $image_size, + $max_width, + $width + ), + array( 'status' => 400 ) + ); + } + + if ( $max_height > 0 && $height > $max_height + $tolerance ) { + return new WP_Error( + 'rest_upload_dimension_mismatch', + sprintf( + /* translators: 1: Image size name, 2: maximum height, 3: actual height. */ + __( 'Uploaded image height (%3$d) exceeds maximum for "%1$s" size (%2$d).' ), + $image_size, + $max_height, + $height + ), + array( 'status' => 400 ) + ); + } + + return true; + } + /** * Side-loads a media file without creating a new attachment. * @@ -2047,6 +2148,18 @@ public function sideload_item( WP_REST_Request $request ) { $image_size = $request['image_size']; + $size = wp_getimagesize( $path ); + + // Validate dimensions match expected size. + if ( is_array( $size ) ) { + $validation = $this->validate_image_dimensions( $size[0], $size[1], $image_size, $attachment_id ); + if ( is_wp_error( $validation ) ) { + // Clean up the uploaded file. + wp_delete_file( $path ); + return $validation; + } + } + $metadata = wp_get_attachment_metadata( $attachment_id, true ); if ( ! $metadata ) { @@ -2100,8 +2213,6 @@ public function sideload_item( WP_REST_Request $request ) { } else { $metadata['sizes'] = $metadata['sizes'] ?? array(); - $size = wp_getimagesize( $path ); - $metadata['sizes'][ $image_size ] = array( 'width' => $size ? $size[0] : 0, 'height' => $size ? $size[1] : 0,