diff --git a/.github/changelog/fix-3579-auto-approved-comments b/.github/changelog/fix-3579-auto-approved-comments new file mode 100644 index 0000000000..fda27db2fe --- /dev/null +++ b/.github/changelog/fix-3579-auto-approved-comments @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed normal replies from distant accounts sometimes being auto-approved just because that account had previously liked or reposted a post. diff --git a/includes/class-comment.php b/includes/class-comment.php index 0c406e7938..4d9a1e016c 100644 --- a/includes/class-comment.php +++ b/includes/class-comment.php @@ -908,8 +908,9 @@ public static function pre_comment_approved( $approved, $comment_data ) { $author = $comment_data['comment_author']; $author_url = $comment_data['comment_author_url']; + // Only previously approved normal comments count, not approved likes or reposts. // phpcs:ignore - $ok_to_comment = $wpdb->get_var( $wpdb->prepare( "SELECT comment_approved FROM $wpdb->comments WHERE comment_author = %s AND comment_author_url = %s and comment_approved = '1' LIMIT 1", $author, $author_url ) ); + $ok_to_comment = $wpdb->get_var( $wpdb->prepare( "SELECT comment_approved FROM $wpdb->comments WHERE comment_author = %s AND comment_author_url = %s AND comment_approved = '1' AND comment_type = 'comment' LIMIT 1", $author, $author_url ) ); if ( 1 === (int) $ok_to_comment ) { return 1; diff --git a/tests/phpunit/tests/includes/class-test-comment.php b/tests/phpunit/tests/includes/class-test-comment.php index 41503a319e..a0aa9f8986 100644 --- a/tests/phpunit/tests/includes/class-test-comment.php +++ b/tests/phpunit/tests/includes/class-test-comment.php @@ -280,6 +280,69 @@ public function test_pre_comment_approved() { \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 ); } + /** + * Test that an approved like does not count as a previously approved comment. + * + * A previously approved reaction (like, repost, quote) should not cause a + * later normal reply from the same actor to be auto-approved. + * + * @covers ::pre_comment_approved + */ + public function test_pre_comment_approved_ignores_reactions() { + // Disable flood control. + \remove_action( 'check_comment_flood', 'check_comment_flood_db' ); + + $previous = \get_option( 'comment_previously_approved' ); + \update_option( 'comment_previously_approved', '1' ); + + $post_id = \wp_insert_post( + array( + 'post_title' => 'Test Post', + 'post_content' => 'This is a test post.', + 'post_status' => 'publish', + 'post_author' => 1, + ) + ); + + // Approved like from a remote actor. This must not count as a previously approved comment. + \wp_insert_comment( + array( + 'comment_type' => 'like', + 'comment_approved' => '1', + 'comment_content' => 'Liked this!', + 'comment_author' => 'Approved', + 'comment_author_url' => 'https://example.com/@approved', + 'comment_post_ID' => $post_id, + 'comment_author_email' => '', + 'comment_meta' => array( + 'protocol' => 'activitypub', + ), + ) + ); + + // A normal reply from the same actor must still be held for moderation. + $reply_id = \wp_new_comment( + array( + 'comment_type' => 'comment', + 'comment_content' => 'This reply should not be auto-approved.', + 'comment_author' => 'Approved', + 'comment_author_url' => 'https://example.com/@approved', + 'comment_post_ID' => $post_id, + 'comment_author_email' => '', + 'comment_meta' => array( + 'protocol' => 'activitypub', + ), + ) + ); + + $reply = \get_comment( $reply_id ); + $this->assertEquals( '0', $reply->comment_approved ); + + // Restore the option and flood control. + \update_option( 'comment_previously_approved', $previous ); + \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 ); + } + /** * Test pre_wp_update_comment_count_now. *