From 0aab2584a0a51474d3feebce82006ca992f3c613 Mon Sep 17 00:00:00 2001 From: Andrew Ozz Date: Thu, 16 Jul 2020 21:54:37 +0000 Subject: [PATCH] REST API: Prevent attachment ID/image source mismatch when editing an image. Fixes #50565. git-svn-id: https://develop.svn.wordpress.org/trunk@48498 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-attachments-controller.php | 12 +++- .../rest-api/rest-attachments-controller.php | 63 +++++++++++++++++-- tests/qunit/fixtures/wp-api-generated.js | 5 ++ 3 files changed, 74 insertions(+), 6 deletions(-) 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 e74ce6821f..8c4a87a646 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 @@ -421,7 +421,11 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { $image_file = wp_get_original_image_path( $attachment_id ); $image_meta = wp_get_attachment_metadata( $attachment_id ); - if ( ! $image_meta || ! $image_file ) { + if ( + ! $image_meta || + ! $image_file || + ! wp_image_file_matches_image_meta( $request['src'], $image_meta ) + ) { return new WP_Error( 'rest_unknown_attachment', __( 'Unable to get meta information for file.' ), @@ -1289,6 +1293,12 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 'minimum' => 0, 'maximum' => 100, ), + 'src' => array( + 'description' => __( 'URL to the edited image file.' ), + 'type' => 'string', + 'format' => 'uri', + 'required' => true, + ), ); } diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 0b52d4c651..c6d4730205 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -1798,9 +1798,10 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control * @ticket 44405 */ public function test_edit_image_returns_error_if_logged_out() { - $attachment = self::factory()->attachment->create(); + $attachment = self::factory()->attachment->create_upload_object( $this->test_file ); $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( array( 'src' => wp_get_attachment_image_url( $attachment, 'full' ) ) ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_cannot_edit_image', $response, 401 ); } @@ -1813,9 +1814,10 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control $user->add_cap( 'upload_files', false ); wp_set_current_user( $user->ID ); - $attachment = self::factory()->attachment->create( array( 'post_author' => $user->ID ) ); + $attachment = self::factory()->attachment->create_upload_object( $this->test_file ); $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( array( 'src' => wp_get_attachment_image_url( $attachment, 'full' ) ) ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_cannot_edit_image', $response, 403 ); } @@ -1825,9 +1827,10 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control */ public function test_edit_image_returns_error_if_cannot_edit() { wp_set_current_user( self::$uploader_id ); - $attachment = self::factory()->attachment->create(); + $attachment = self::factory()->attachment->create_upload_object( $this->test_file ); $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( array( 'src' => wp_get_attachment_image_url( $attachment, 'full' ) ) ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 ); } @@ -1840,6 +1843,7 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control $attachment = self::factory()->attachment->create(); $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( array( 'src' => '/wp-content/uploads/2020/07/canola.jpg' ) ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_unknown_attachment', $response, 404 ); } @@ -1858,6 +1862,7 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control ); $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( array( 'src' => wp_get_attachment_image_url( $attachment, 'full' ) ) ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_cannot_edit_file_type', $response, 400 ); } @@ -1870,6 +1875,7 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control $attachment = self::factory()->attachment->create_upload_object( $this->test_file ); $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( array( 'src' => wp_get_attachment_image_url( $attachment, 'full' ) ) ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_image_not_edited', $response, 400 ); } @@ -1884,8 +1890,13 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control $this->setup_mock_editor(); WP_Image_Editor_Mock::$edit_return['rotate'] = new WP_Error(); + $params = array( + 'rotation' => 60, + 'src' => wp_get_attachment_image_url( $attachment, 'full' ), + ); + $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); - $request->set_body_params( array( 'rotation' => 60 ) ); + $request->set_body_params( $params ); $response = rest_do_request( $request ); $this->assertErrorResponse( 'rest_image_rotation_failed', $response, 500 ); @@ -1915,6 +1926,8 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control 'y' => 10, 'width' => 10, 'height' => 5, + 'src' => wp_get_attachment_image_url( $attachment, 'full' ), + ) ); $response = rest_do_request( $request ); @@ -1934,8 +1947,13 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control wp_set_current_user( self::$superadmin_id ); $attachment = self::factory()->attachment->create_upload_object( $this->test_file ); + $params = array( + 'rotation' => 60, + 'src' => wp_get_attachment_image_url( $attachment, 'full' ), + ); + $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); - $request->set_body_params( array( 'rotation' => 60 ) ); + $request->set_body_params( $params ); $response = rest_do_request( $request ); $item = $response->get_data(); @@ -1948,6 +1966,41 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control $this->assertContains( 'canola', $item['media_details']['parent_image']['file'] ); } + /** + * @ticket 50565 + */ + public function test_edit_image_returns_error_if_mismatched_src() { + wp_set_current_user( self::$superadmin_id ); + $attachment_id_image1 = self::factory()->attachment->create_upload_object( $this->test_file ); + $attachment_id_image2 = self::factory()->attachment->create_upload_object( $this->test_file2 ); + $attachment_id_file = self::factory()->attachment->create(); + + // URL to the first uploaded image. + $image_src = wp_get_attachment_image_url( $attachment_id_image1, 'large' ); + + // Test: attachment ID points to a different, non-image attachment. + $request_1 = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment_id_file}/edit" ); + $request_1->set_body_params( array( 'src' => $image_src ) ); + + $response_1 = rest_do_request( $request_1 ); + $this->assertErrorResponse( 'rest_unknown_attachment', $response_1, 404 ); + + // Test: attachment ID points to a different image attachment. + $request_2 = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment_id_image2}/edit" ); + $request_2->set_body_params( array( 'src' => $image_src ) ); + + $response_2 = rest_do_request( $request_2 ); + $this->assertErrorResponse( 'rest_unknown_attachment', $response_2, 404 ); + + // Test: attachment src points to a sub-size of the image. + $request_3 = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment_id_image1}/edit" ); + $request_3->set_body_params( array( 'src' => wp_get_attachment_image_url( $attachment_id_image1, 'medium' ) ) ); + + $response_3 = rest_do_request( $request_3 ); + // 'rest_image_not_edited' as the file wasn't edited. + $this->assertErrorResponse( 'rest_image_not_edited', $response_3, 400 ); + } + /** * Sets up the mock image editor. * diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index ebe44ea667..252109aec7 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -2377,6 +2377,11 @@ mockedApiResponse.Schema = { "required": false, "description": "As a percentage of the image, the height to crop the image to.", "type": "number" + }, + "src": { + "required": true, + "description": "URL to the edited image file.", + "type": "string" } } }