From 0bba95f0abebde6ab5f2027991735850c39f137f Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Thu, 16 Jul 2026 14:14:17 +0530 Subject: [PATCH 1/6] Conditionally terminate if `is_wp_error` --- src/wp-admin/includes/ajax-actions.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index a04de73bd64e4..74504e013238c 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -1660,7 +1660,9 @@ function wp_ajax_add_meta() { $meta_id = add_meta( $post_id ); - if ( ! $meta_id ) { + if ( is_wp_error( $meta_id ) ) { + wp_die( $meta_id->get_error_message() ); + } elseif ( ! $meta_id ) { wp_die( __( 'Please provide a custom field value.' ) ); } } else { @@ -1669,7 +1671,9 @@ function wp_ajax_add_meta() { } else { $meta_id = add_meta( $post_id ); - if ( ! $meta_id ) { + if ( is_wp_error( $meta_id ) ) { + wp_die( $meta_id->get_error_message() ); + } elseif ( ! $meta_id ) { wp_die( __( 'Please provide a custom field value.' ) ); } } @@ -1707,7 +1711,7 @@ function wp_ajax_add_meta() { ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) || ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) ) { - wp_die( -1 ); + wp_die( __( 'Sorry, you are not allowed to edit this custom field.' ) ); } if ( $meta->meta_value !== $value || $meta->meta_key !== $key ) { From fadaad07b49de31762ff381966e94ad7e92586e2 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Thu, 16 Jul 2026 14:14:20 +0530 Subject: [PATCH 2/6] Throw error when meta key protected or capability not satisfied --- src/wp-admin/includes/post.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 39d267b623037..b7fd3d4a5d6f6 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1001,9 +1001,12 @@ function write_post() { * Adds post meta data defined in the `$_POST` superglobal for a post with given ID. * * @since 1.2.0 + * @since 7.1.0 Returns a WP_Error object when the meta key is protected or the user + * does not have the capability to add post meta. * * @param int $post_id - * @return int|bool + * @return int|bool|WP_Error Meta ID on success, false if no key/value pair was provided, + * WP_Error if the meta key is protected or the user lacks capability. */ function add_meta( $post_id ) { $post_id = (int) $post_id; @@ -1029,7 +1032,10 @@ function add_meta( $post_id ) { } if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_id, $metakey ) ) { - return false; + return new WP_Error( + 'protected_meta', + __( 'Sorry, you are not allowed to add this custom field.' ) + ); } $metakey = wp_slash( $metakey ); From 9391a2cec530774ec24baeeb674355cd53599a00 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Thu, 16 Jul 2026 14:14:21 +0530 Subject: [PATCH 3/6] Add data provider for the test --- tests/phpunit/tests/admin/includesPost.php | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/phpunit/tests/admin/includesPost.php b/tests/phpunit/tests/admin/includesPost.php index d9d39d8da727d..6f8516c5e2ef4 100644 --- a/tests/phpunit/tests/admin/includesPost.php +++ b/tests/phpunit/tests/admin/includesPost.php @@ -1163,6 +1163,31 @@ public function test_add_meta_allows_empty_values() { $this->assertSame( '', get_post_meta( $p, 'testkey', true ) ); } + public function data_add_meta_authorization() { + return array( + 'has capability, unprotected metakey' => array( + 'metakey' => 'ordinary_key', + 'role' => 'administrator', + 'expected_error' => false, + ), + 'no capability, unprotected metakey' => array( + 'metakey' => 'ordinary_key', + 'role' => 'subscriber', + 'expected_error' => true, + ), + 'has capability, protected metakey' => array( + 'metakey' => '_protected_testkey', + 'role' => 'administrator', + 'expected_error' => true, + ), + 'no capability, protected metakey' => array( + 'metakey' => '_protected_testkey', + 'role' => 'subscriber', + 'expected_error' => true, + ), + ); + } + /** * Test the post type support in post_exists(). * From 3c599e93128433c3cc4acaf34029c3206aed8840 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Thu, 16 Jul 2026 14:14:24 +0530 Subject: [PATCH 4/6] Add test for meta addition per meta keys and capabilities --- tests/phpunit/tests/admin/includesPost.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/phpunit/tests/admin/includesPost.php b/tests/phpunit/tests/admin/includesPost.php index 6f8516c5e2ef4..9e27ce9bc5934 100644 --- a/tests/phpunit/tests/admin/includesPost.php +++ b/tests/phpunit/tests/admin/includesPost.php @@ -1188,6 +1188,28 @@ public function data_add_meta_authorization() { ); } + public function test_add_meta_authorization( string $metakey, string $role, bool $expected_error ) { + $post_id = self::factory()->post->create(); + $user_id = 'administrator' === $role ? self::$admin_id : self::factory()->user->create( array( 'role' => $role ) ); + + $_POST = array( + 'metakeyinput' => $metakey, + 'metavalue' => 'test_value', + ); + + wp_set_current_user( $user_id ); + + $result = add_meta( $post_id ); + + if ( $expected_error ) { + $this->assertWPError( $result ); + $this->assertSame( 'protected_meta', $result->get_error_code() ); + } else { + $this->assertIsInt( $result ); + $this->assertSame( 'test_value', get_post_meta( $post_id, $metakey, true ) ); + } + } + /** * Test the post type support in post_exists(). * From 6c38a00ad4621fff2223989c2c71251857061ec7 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Thu, 16 Jul 2026 14:14:26 +0530 Subject: [PATCH 5/6] Add doc blocks --- tests/phpunit/tests/admin/includesPost.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/phpunit/tests/admin/includesPost.php b/tests/phpunit/tests/admin/includesPost.php index 9e27ce9bc5934..3e7dc695badcb 100644 --- a/tests/phpunit/tests/admin/includesPost.php +++ b/tests/phpunit/tests/admin/includesPost.php @@ -1163,6 +1163,11 @@ public function test_add_meta_allows_empty_values() { $this->assertSame( '', get_post_meta( $p, 'testkey', true ) ); } + /** + * Data provider for test_add_meta_authorization. + * + * @return array[] + */ public function data_add_meta_authorization() { return array( 'has capability, unprotected metakey' => array( @@ -1188,6 +1193,13 @@ public function data_add_meta_authorization() { ); } + /** + * @ticket 32565 + * + * @covers ::add_meta + * + * @dataProvider data_add_meta_authorization + */ public function test_add_meta_authorization( string $metakey, string $role, bool $expected_error ) { $post_id = self::factory()->post->create(); $user_id = 'administrator' === $role ? self::$admin_id : self::factory()->user->create( array( 'role' => $role ) ); From 94f1889f3f9f12c08600d6a0074238b0f982f1d5 Mon Sep 17 00:00:00 2001 From: Himanshu Pathak Date: Thu, 16 Jul 2026 14:14:28 +0530 Subject: [PATCH 6/6] Fix linting --- tests/phpunit/tests/admin/includesPost.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/admin/includesPost.php b/tests/phpunit/tests/admin/includesPost.php index 3e7dc695badcb..1e6f10cd12640 100644 --- a/tests/phpunit/tests/admin/includesPost.php +++ b/tests/phpunit/tests/admin/includesPost.php @@ -1170,22 +1170,22 @@ public function test_add_meta_allows_empty_values() { */ public function data_add_meta_authorization() { return array( - 'has capability, unprotected metakey' => array( + 'has capability, unprotected metakey' => array( 'metakey' => 'ordinary_key', 'role' => 'administrator', 'expected_error' => false, ), - 'no capability, unprotected metakey' => array( + 'no capability, unprotected metakey' => array( 'metakey' => 'ordinary_key', 'role' => 'subscriber', 'expected_error' => true, ), - 'has capability, protected metakey' => array( + 'has capability, protected metakey' => array( 'metakey' => '_protected_testkey', 'role' => 'administrator', 'expected_error' => true, ), - 'no capability, protected metakey' => array( + 'no capability, protected metakey' => array( 'metakey' => '_protected_testkey', 'role' => 'subscriber', 'expected_error' => true,