From e0c3b67d4b9c707415f9b9f5f3b24cd1b4dfced7 Mon Sep 17 00:00:00 2001 From: Matthew Exon Date: Tue, 28 Jan 2025 21:55:20 +0800 Subject: [PATCH] Check response body for tombstone type (#1209) (#1222) * Check response body for tombstone type (#1209) * some small improvements * fix PHPCS --------- Co-authored-by: Matthew Exon Co-authored-by: Matthias Pfefferle --- CHANGELOG.md | 4 ++ includes/class-http.php | 8 ++- readme.txt | 1 + tests/includes/class-test-http.php | 85 ++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/includes/class-test-http.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 717120b4b..cb2a104fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Improved content negotiation and AUTHORIZED_FETCH support for third-party plugins +### Fixed + +* Handle deletes from remote servers that leave behind an accessible Tombstone object. + ## [4.7.3] - 2025-01-21 ### Fixed diff --git a/includes/class-http.php b/includes/class-http.php index 85edec316..1c8c885d1 100644 --- a/includes/class-http.php +++ b/includes/class-http.php @@ -192,13 +192,19 @@ public static function is_tombstone( $url ) { */ \do_action( 'activitypub_pre_http_is_tombstone', $url ); - $response = \wp_safe_remote_get( $url ); + $response = \wp_safe_remote_get( $url, array( 'headers' => array( 'Accept' => 'application/activity+json' ) ) ); $code = \wp_remote_retrieve_response_code( $response ); if ( in_array( (int) $code, array( 404, 410 ), true ) ) { return true; } + $data = \wp_remote_retrieve_body( $response ); + $data = \json_decode( $data, true ); + if ( $data && isset( $data['type'] ) && 'Tombstone' === $data['type'] ) { + return true; + } + return false; } diff --git a/readme.txt b/readme.txt index efaf265ad..f33b35bd8 100644 --- a/readme.txt +++ b/readme.txt @@ -134,6 +134,7 @@ For reasons of data protection, it is not possible to see the followers of other = Unreleased = * Changed: Improved content negotiation and AUTHORIZED_FETCH support for third-party plugins +* Fixed: Handle deletes from remote servers that leave behind an accessible Tombstone object. = 4.7.3 = diff --git a/tests/includes/class-test-http.php b/tests/includes/class-test-http.php new file mode 100644 index 000000000..5d3f57b36 --- /dev/null +++ b/tests/includes/class-test-http.php @@ -0,0 +1,85 @@ + is_tombstone returns true + * + * @covers ::is_tombstone + * + * @dataProvider data_is_tombstone + * + * @param array $request The request array. + * @param bool $result The expected result. + */ + public function test_is_tombstone( $request, $result ) { + $fake_request = function () use ( $request ) { + return $request; + }; + add_filter( 'pre_http_request', $fake_request, 10, 3 ); + $response = Http::is_tombstone( 'https://fake.test/object/123' ); + $this->assertEquals( $result, $response ); + remove_filter( 'pre_http_request', $fake_request, 10 ); + } + + /** + * Data provider for test_is_tombstone. + * + * @return array + */ + public function data_is_tombstone() { + return array( + array( array( 'response' => array( 'code' => 404 ) ), true ), + array( array( 'response' => array( 'code' => 410 ) ), true ), + array( + array( + 'response' => array( 'code' => 200 ), + 'body' => '', + ), + false, + ), + array( + array( + 'response' => array( 'code' => 200 ), + 'body' => '{}', + ), + false, + ), + array( + array( + 'response' => array( 'code' => 200 ), + 'body' => '{"type": "Note"}', + ), + false, + ), + array( + array( + 'response' => array( 'code' => 200 ), + 'body' => '{"type": "Tombstone"}', + ), + true, + ), + array( + array( + 'response' => array( 'code' => 200 ), + 'body' => '{"foo": "bar"}', + ), + false, + ), + ); + } +}