Make WordPress Core


Ignore:
Timestamp:
08/06/2026 09:30:58 AM (2 months ago)
Author:
youknowriad
Message:

Pings/Trackbacks: Auto-approve pingbacks and trackbacks from the same site.

Pings carry no email address, so they never satisfy the
comment_previously_approved option, which is enabled by default. Every ping
is held for moderation indefinitely as a result, including the ones a site
sends to itself when a new post links to an older one.

Approve a ping whose source URL resolves to a published post on this site.
Existing moderation checks still take precedence, and the new
auto_approve_ping filter controls the decision.

Props annezazu, avcascade, chriscct7, desrosj, dshanske, eurello, matt,
mohkatz, SergeyBiryukov, stevegrunwell.
Fixes #65016.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/comment/checkComment.php

    r56548 r63036  
    206206                $this->assertFalse( $results );
    207207        }
     208
     209        /**
     210         * @ticket 65016
     211         */
     212        public function test_should_return_true_for_a_pingback_from_this_site() {
     213                update_option( 'comment_previously_approved', '1' );
     214
     215                $source_url = get_permalink( self::factory()->post->create() );
     216
     217                $this->assertTrue( check_comment( 'Site Title', '', $source_url, 'Excerpt.', '192.168.0.1', '', 'pingback' ) );
     218        }
     219
     220        /**
     221         * Trackbacks are never approved automatically.
     222         *
     223         * A trackback's source URL, title, and excerpt are unverified request data. Were
     224         * they trusted, anyone could POST a trackback naming a local post as its source
     225         * and have arbitrary content approved without moderation.
     226         *
     227         * @ticket 65016
     228         */
     229        public function test_should_return_false_for_a_trackback_claiming_a_local_source() {
     230                update_option( 'comment_previously_approved', '1' );
     231
     232                $source_url = get_permalink( self::factory()->post->create() );
     233
     234                $this->assertFalse( check_comment( 'ForgedSite', '', $source_url, 'Spam.', '192.168.0.1', '', 'trackback' ) );
     235        }
     236
     237        /**
     238         * @ticket 65016
     239         *
     240         * @dataProvider data_ping_types
     241         *
     242         * @param string $comment_type The comment type.
     243         */
     244        public function test_should_return_false_for_a_ping_from_another_site( $comment_type ) {
     245                update_option( 'comment_previously_approved', '1' );
     246
     247                $this->assertFalse( check_comment( 'Site Title', '', 'http://example.com/a-post/', 'Excerpt.', '192.168.0.1', '', $comment_type ) );
     248        }
     249
     250        /**
     251         * A URL is only local when its host matches, not when it merely contains the home URL.
     252         *
     253         * @ticket 65016
     254         *
     255         * @dataProvider data_ping_types
     256         *
     257         * @param string $comment_type The comment type.
     258         */
     259        public function test_should_return_false_for_a_ping_from_a_url_spoofing_this_site( $comment_type ) {
     260                update_option( 'comment_previously_approved', '1' );
     261
     262                $post_id    = self::factory()->post->create();
     263                $source_url = 'http://example.com/?ref=' . rawurlencode( get_permalink( $post_id ) );
     264
     265                $this->assertFalse( check_comment( 'Site Title', '', $source_url, 'Excerpt.', '192.168.0.1', '', $comment_type ) );
     266        }
     267
     268        /**
     269         * Manually approving every comment must not be bypassed by a self-pingback.
     270         *
     271         * @ticket 65016
     272         */
     273        public function test_should_return_false_for_a_pingback_from_this_site_when_comment_moderation_is_enabled() {
     274                update_option( 'comment_moderation', '1' );
     275
     276                $source_url = get_permalink( self::factory()->post->create() );
     277
     278                $this->assertFalse( check_comment( 'Site Title', '', $source_url, 'Excerpt.', '192.168.0.1', '', 'pingback' ) );
     279        }
     280
     281        /**
     282         * The source post must be published, not a draft that happens to resolve.
     283         *
     284         * @ticket 65016
     285         */
     286        public function test_should_return_false_for_a_pingback_from_an_unpublished_post_on_this_site() {
     287                update_option( 'comment_previously_approved', '1' );
     288
     289                $post_id    = self::factory()->post->create( array( 'post_status' => 'draft' ) );
     290                $source_url = add_query_arg( 'p', $post_id, home_url( '/' ) );
     291
     292                $this->assertFalse( check_comment( 'Site Title', '', $source_url, 'Excerpt.', '192.168.0.1', '', 'pingback' ) );
     293        }
     294
     295        /**
     296         * Only pings are exempt. A regular comment linking to a local post is still moderated.
     297         *
     298         * @ticket 65016
     299         */
     300        public function test_should_return_false_for_a_comment_whose_author_url_is_a_post_on_this_site() {
     301                update_option( 'comment_previously_approved', '1' );
     302
     303                $source_url = get_permalink( self::factory()->post->create() );
     304
     305                $this->assertFalse( check_comment( 'Bob', 'bob@example.com', $source_url, 'A comment.', '192.168.0.1', '', 'comment' ) );
     306        }
     307
     308        /**
     309         * @ticket 65016
     310         */
     311        public function test_auto_approve_pingback_should_be_able_to_hold_a_pingback_from_this_site() {
     312                update_option( 'comment_previously_approved', '1' );
     313
     314                $source_url = get_permalink( self::factory()->post->create() );
     315
     316                add_filter( 'auto_approve_pingback', '__return_false' );
     317
     318                $this->assertFalse( check_comment( 'Site Title', '', $source_url, 'Excerpt.', '192.168.0.1', '', 'pingback' ) );
     319        }
     320
     321        /**
     322         * @ticket 65016
     323         */
     324        public function test_auto_approve_pingback_should_be_able_to_approve_a_pingback_from_another_site() {
     325                update_option( 'comment_previously_approved', '1' );
     326
     327                add_filter( 'auto_approve_pingback', '__return_true' );
     328
     329                $this->assertTrue( check_comment( 'Site Title', '', 'http://example.com/a-post/', 'Excerpt.', '192.168.0.1', '', 'pingback' ) );
     330        }
     331
     332        /**
     333         * @ticket 65016
     334         */
     335        public function test_auto_approve_pingback_should_receive_the_source_post_id() {
     336                update_option( 'comment_previously_approved', '1' );
     337
     338                $post_id    = self::factory()->post->create();
     339                $source_url = get_permalink( $post_id );
     340
     341                $observed = null;
     342                add_filter(
     343                        'auto_approve_pingback',
     344                        static function ( $approve, $source_id ) use ( &$observed ) {
     345                                $observed = $source_id;
     346                                return $approve;
     347                        },
     348                        10,
     349                        2
     350                );
     351
     352                check_comment( 'Site Title', '', $source_url, 'Excerpt.', '192.168.0.1', '', 'pingback' );
     353
     354                $this->assertSame( $post_id, $observed );
     355        }
     356
     357        /**
     358         * Data provider.
     359         *
     360         * @return array[]
     361         */
     362        public function data_ping_types() {
     363                return array(
     364                        'pingback'  => array( 'pingback' ),
     365                        'trackback' => array( 'trackback' ),
     366                );
     367        }
    208368}
Note: See TracChangeset for help on using the changeset viewer.