From 0cd0d3adbe4e256a423ba6fbf2bf388c623a4ad4 Mon Sep 17 00:00:00 2001 From: Ramon Date: Sat, 25 Oct 2025 12:48:40 +1100 Subject: [PATCH 1/2] Fix: Ensure attachment data is cast to array before wp_slash in REST API This change modifies the `wp_insert_attachment` call to ensure that the attachment post data is properly cast to an array before being passed to `wp_slash()`, enhancing data integrity during database insertion. Additionally, a new PHPUnit test is added to verify that the data is correctly slashed and formatted as an array, ensuring proper escaping of string values. --- .../class-wp-rest-attachments-controller.php | 2 +- .../rest-api/rest-attachments-controller.php | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) 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..5d74151b5db68 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', + 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' ); + } } From 6b251342919d1e80d6364162752a996e1b8ed73a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 26 Oct 2025 09:27:27 -0700 Subject: [PATCH 2/2] Use static closure Co-authored-by: Mukesh Panchal --- tests/phpunit/tests/rest-api/rest-attachments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 5d74151b5db68..05029e0845d96 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -3118,7 +3118,7 @@ public function test_edit_image_wp_slash_with_object_cast() { // Mock wp_insert_attachment to capture the data being passed. add_filter( 'wp_insert_attachment_data', - function ( $data ) use ( &$captured_data ) { + static function ( $data ) use ( &$captured_data ) { $captured_data = $data; return $data; },