From b7c2513eb70db20a71b3dfdd74f99395daf10cd7 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Tue, 28 Jan 2025 12:19:00 -0600 Subject: [PATCH 1/3] Outbox: Account for invalid json --- includes/activity/class-base-object.php | 2 +- includes/class-dispatcher.php | 6 +++++ includes/collection/class-followers.php | 24 +++++-------------- includes/model/class-follower.php | 7 +++++- .../activity/class-test-base-object.php | 12 ++++++++++ .../collection/class-test-followers.php | 20 ++++++++++++++++ 6 files changed, 51 insertions(+), 20 deletions(-) diff --git a/includes/activity/class-base-object.php b/includes/activity/class-base-object.php index b2d645f69..2624104f1 100644 --- a/includes/activity/class-base-object.php +++ b/includes/activity/class-base-object.php @@ -588,7 +588,7 @@ public function add( $key, $value ) { * * @param string $json The JSON string. * - * @return Base_Object An Object built from the JSON string. + * @return Base_Object|WP_Error An Object built from the JSON string or WP_Error when it's not a JSON string. */ public static function init_from_json( $json ) { $array = \json_decode( $json, true ); diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php index a1d4bd0a9..97cb9fab0 100644 --- a/includes/class-dispatcher.php +++ b/includes/class-dispatcher.php @@ -68,6 +68,12 @@ public static function process_outbox( $id ) { $activity->set_object( \json_decode( $outbox_item->post_content, true ) ); $activity->set_actor( Actors::get_by_id( $outbox_item->post_author )->get_id() ); + // Pre-fill the Activity with data (for example cc and to). + $activity_object = Activity::init_from_json( $outbox_item->post_content ); + if ( ! is_wp_error( $activity_object ) ) { + $activity->set_object( $activity_object ); + } + // Use simple Object (only ID-URI) for Like and Announce. if ( in_array( $type, array( 'Like', 'Delete' ), true ) ) { $activity->set_object( $activity->get_object()->get_id() ); diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 77ade14e1..d668c4831 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -197,12 +197,8 @@ public static function get_followers_with_count( $user_id, $number = -1, $page = $args = wp_parse_args( $args, $defaults ); $query = new WP_Query( $args ); $total = $query->found_posts; - $followers = array_map( - function ( $post ) { - return Follower::init_from_cpt( $post ); - }, - $query->get_posts() - ); + $followers = array_map( array( Follower::class, 'init_from_cpt' ), $query->get_posts() ); + $followers = array_filter( $followers ); return compact( 'followers', 'total' ); } @@ -354,13 +350,9 @@ public static function get_outdated_followers( $number = 50, $older_than = 86400 ); $posts = new WP_Query( $args ); - $items = array(); - - foreach ( $posts->get_posts() as $follower ) { - $items[] = Follower::init_from_cpt( $follower ); - } + $items = array_map( array( Follower::class, 'init_from_cpt' ), $posts->get_posts() ); - return $items; + return array_filter( $items ); } /** @@ -403,13 +395,9 @@ public static function get_faulty_followers( $number = 20 ) { ); $posts = new WP_Query( $args ); - $items = array(); - - foreach ( $posts->get_posts() as $follower ) { - $items[] = Follower::init_from_cpt( $follower ); - } + $items = array_map( array( Follower::class, 'init_from_cpt' ), $posts->get_posts() ); - return $items; + return array_filter( $items ); } /** diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index bcf5d39dd..477a71886 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -331,11 +331,16 @@ public function get_shared_inbox() { * Convert a Custom-Post-Type input to an Activitypub\Model\Follower. * * @param \WP_Post $post The post object. - * @return \Activitypub\Activity\Base_Object|WP_Error + * @return \Activitypub\Activity\Base_Object|false The Follower object or false on failure. */ public static function init_from_cpt( $post ) { $actor_json = get_post_meta( $post->ID, '_activitypub_actor_json', true ); $object = self::init_from_json( $actor_json ); + + if ( is_wp_error( $object ) ) { + return false; + } + $object->set__id( $post->ID ); $object->set_id( $post->guid ); $object->set_name( $post->post_title ); diff --git a/tests/includes/activity/class-test-base-object.php b/tests/includes/activity/class-test-base-object.php index 261e2d87c..f8da047bc 100644 --- a/tests/includes/activity/class-test-base-object.php +++ b/tests/includes/activity/class-test-base-object.php @@ -58,4 +58,16 @@ public function data_magic_add() { array( array( 'value', 'value' ), array( 'value' ) ), ); } + + /** + * Test init_from_json method. + * + * @covers ::init_from_json + */ + public function test_init_from_json() { + $invalid_json = '{"@context":https:\/\/www.w3.org\/ns\/activitystreams",{"Hashtag":"as:Hashtag","sensitive":"as:sensitive"}],"id":"https:\/\/example.com\/2","type":"Note","content":"\u003Cp\u003EThis is another note\u003C\/p\u003E","contentMap":{"en":"\u003Cp\u003EThis is another note\u003C\/p\u003E"},"tag":[],"to":["https:\/\/www.w3.org\/ns\/activitystreams#Public"],"cc":[],"mediaType":"text\/html","sensitive":false}'; + $base_object = Base_Object::init_from_json( $invalid_json ); + + $this->assertInstanceOf( 'WP_Error', $base_object ); + } } diff --git a/tests/includes/collection/class-test-followers.php b/tests/includes/collection/class-test-followers.php index c2cd8656f..dbf2ca136 100644 --- a/tests/includes/collection/class-test-followers.php +++ b/tests/includes/collection/class-test-followers.php @@ -114,6 +114,26 @@ function ( $item ) { $this->assertEquals( array( 'http://sally.example.org', 'https://example.org/author/doe', 'https://example.com/author/jon' ), $db_followers ); } + /** + * Tests get_followers with corrupted json. + * + * @covers ::get_followers + */ + public function test_get_followers_without_errors() { + $followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'https://sally.example.org' ); + + foreach ( $followers as $follower ) { + Followers::add_follower( 1, $follower ); + } + + $follower = Followers::get_follower( 1, 'https://example.org/author/doe' ); + update_post_meta( $follower->get__id(), '_activitypub_actor_json', 'invalid json' ); + + $db_followers = Followers::get_followers( 1 ); + + $this->assertEquals( 2, \count( $db_followers ) ); + } + /** * Tests add_follower. * From a051f2b04f2b19a15ab51935e56846cbb596c56f Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Tue, 28 Jan 2025 12:24:44 -0600 Subject: [PATCH 2/3] http it is! --- tests/includes/collection/class-test-followers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/includes/collection/class-test-followers.php b/tests/includes/collection/class-test-followers.php index dbf2ca136..9b4a24335 100644 --- a/tests/includes/collection/class-test-followers.php +++ b/tests/includes/collection/class-test-followers.php @@ -120,7 +120,7 @@ function ( $item ) { * @covers ::get_followers */ public function test_get_followers_without_errors() { - $followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'https://sally.example.org' ); + $followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' ); foreach ( $followers as $follower ) { Followers::add_follower( 1, $follower ); From 664af226f8888c42d8ea2b637eaecc01549e94a8 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Wed, 29 Jan 2025 09:05:25 -0600 Subject: [PATCH 3/3] Remove unnecessary init --- includes/class-dispatcher.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php index 97cb9fab0..a1d4bd0a9 100644 --- a/includes/class-dispatcher.php +++ b/includes/class-dispatcher.php @@ -68,12 +68,6 @@ public static function process_outbox( $id ) { $activity->set_object( \json_decode( $outbox_item->post_content, true ) ); $activity->set_actor( Actors::get_by_id( $outbox_item->post_author )->get_id() ); - // Pre-fill the Activity with data (for example cc and to). - $activity_object = Activity::init_from_json( $outbox_item->post_content ); - if ( ! is_wp_error( $activity_object ) ) { - $activity->set_object( $activity_object ); - } - // Use simple Object (only ID-URI) for Like and Announce. if ( in_array( $type, array( 'Like', 'Delete' ), true ) ) { $activity->set_object( $activity->get_object()->get_id() );