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 faeb01c93904a..fbead90390741 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 @@ -777,7 +777,7 @@ public function edit_media_item( $request ) { $new_attachment_post->post_parent = $new_attachment_post->post_parent ?? 0; // Insert the new attachment post. - $new_attachment_id = wp_insert_attachment( wp_slash( $new_attachment_post ), $saved['path'], 0, true ); + $new_attachment_id = wp_insert_attachment( wp_slash( (array) $new_attachment_post ), $saved['path'], 0, true ); if ( is_wp_error( $new_attachment_id ) ) { if ( 'db_update_error' === $new_attachment_id->get_error_code() ) { diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index bc1ca41b578ee..05029e0845d96 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -3097,4 +3097,58 @@ public function test_edit_image_vertical_flip_only() { // The controller converts the integer values to booleans: 0 !== (int) 1 = true. $this->assertSame( array( true, false ), WP_Image_Editor_Mock::$spy['flip'][0], 'Vertical flip of the image is not identical.' ); } + + /** + * Test that wp_slash() is properly applied when creating edited images. + * + * This test verifies that the object returned by prepare_item_for_database() + * is properly cast to an array before being passed to wp_slash(), ensuring + * that string values are properly escaped for database insertion. + * + * @ticket 64149 + * @requires function imagejpeg + */ + public function test_edit_image_wp_slash_with_object_cast() { + wp_set_current_user( self::$superadmin_id ); + $attachment = self::factory()->attachment->create_upload_object( self::$test_file ); + + // Create a mock to capture the data passed to wp_insert_attachment. + $captured_data = null; + + // Mock wp_insert_attachment to capture the data being passed. + add_filter( + 'wp_insert_attachment_data', + static function ( $data ) use ( &$captured_data ) { + $captured_data = $data; + return $data; + }, + 10, + 1 + ); + + $params = array( + 'rotation' => 60, + 'src' => wp_get_attachment_image_url( $attachment, 'full' ), + 'title' => 'Test Title with "quotes" and \'apostrophes\'', + 'caption' => 'Test Caption with "quotes" and \'apostrophes\'', + 'description' => 'Test Description with "quotes" and \'apostrophes\'', + ); + + $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" ); + $request->set_body_params( $params ); + $response = rest_do_request( $request ); + + $this->assertSame( 201, $response->get_status() ); + + // Verify that the data was properly slashed (escaped) + $this->assertNotNull( $captured_data, 'wp_insert_attachment was not called with data' ); + + // Check that quotes are properly escaped in the captured data. + $this->assertStringContainsString( 'Test Title with \"quotes\"', $captured_data['post_title'] ?? '', 'Title quotes not properly escaped' ); + $this->assertStringContainsString( 'Test Caption with \"quotes\"', $captured_data['post_excerpt'] ?? '', 'Caption quotes not properly escaped' ); + $this->assertStringContainsString( 'Test Description with \"quotes\"', $captured_data['post_content'] ?? '', 'Description quotes not properly escaped' ); + + // Verify that the data is an array (not an object). + $this->assertIsArray( $captured_data, 'Data passed to wp_insert_attachment should be an array' ); + } }