Skip to content

Commit b53ec6a

Browse files
committed
Address PR feedback for avatar caching
- Swap save_actor_avatar parameter order (actor_id first) - Increase MAX_AVATAR_DIMENSION to 512px for better quality - Add actor_id validation to prevent path traversal - Add is_array check to cache_avatar for flexibility - Return avatar_url from cache_avatar - Use short ternary for avatar_url assignment - Update tests to match new parameter order
1 parent 26e3bf2 commit b53ec6a

File tree

3 files changed

+37
-23
lines changed

3 files changed

+37
-23
lines changed

includes/class-attachments.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class Attachments {
4747
*
4848
* @var int
4949
*/
50-
const MAX_AVATAR_DIMENSION = 100;
50+
const MAX_AVATAR_DIMENSION = 512;
5151

5252
/**
5353
* Initialize the class and set up filters.
@@ -968,17 +968,24 @@ private static function get_files_gallery_block( $files ) {
968968
* Downloads the avatar image, optimizes it, and stores it in the actors directory.
969969
* Returns the local URL for the saved avatar.
970970
*
971-
* @param string $avatar_url The remote avatar URL.
972971
* @param int $actor_id The local actor post ID.
972+
* @param string $avatar_url The remote avatar URL.
973973
*
974974
* @return string|false The local avatar URL on success, false on failure.
975975
*/
976-
public static function save_actor_avatar( $avatar_url, $actor_id ) {
976+
public static function save_actor_avatar( $actor_id, $avatar_url ) {
977+
// Validate actor_id is a positive integer to prevent path traversal.
978+
$actor_id = (int) $actor_id;
979+
if ( $actor_id <= 0 ) {
980+
return false;
981+
}
982+
977983
if ( empty( $avatar_url ) || ! \filter_var( $avatar_url, FILTER_VALIDATE_URL ) ) {
978984
return false;
979985
}
980986

981987
// Delete existing avatar files before saving new one.
988+
// This prevents accumulating old avatar files since save_file creates unique filenames.
982989
self::delete_actors_directory( $actor_id );
983990

984991
$attachment_data = array( 'url' => $avatar_url );
@@ -997,6 +1004,12 @@ public static function save_actor_avatar( $avatar_url, $actor_id ) {
9971004
* @param int $actor_id The actor post ID.
9981005
*/
9991006
public static function delete_actors_directory( $actor_id ) {
1007+
// Validate actor_id is a positive integer to prevent path traversal.
1008+
$actor_id = (int) $actor_id;
1009+
if ( $actor_id <= 0 ) {
1010+
return;
1011+
}
1012+
10001013
// When used as hook callback, only process actor post types.
10011014
$post_type = \get_post_type( $actor_id );
10021015
if ( $post_type && Remote_Actors::POST_TYPE !== $post_type ) {

includes/collection/class-remote-actors.php

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -685,39 +685,40 @@ public static function get_avatar_url( $id ) {
685685
return $default_avatar_url;
686686
}
687687

688-
// Try to cache the avatar locally.
689-
$remote_avatar_url = object_to_uri( $actor_data['icon'] );
690-
$local_url = Attachments::save_actor_avatar( $remote_avatar_url, $id );
691-
692-
$avatar_url = $local_url ? $local_url : $remote_avatar_url;
693-
\update_post_meta( $id, '_activitypub_avatar_url', \esc_url_raw( $avatar_url ) );
694-
695-
return $avatar_url;
688+
return self::cache_avatar( $id, $actor_data );
696689
}
697690

698691
/**
699692
* Cache a remote actor's avatar locally.
700693
*
701694
* Downloads the avatar image, optimizes it (resize/WebP), and stores it locally.
702695
*
703-
* @param int $post_id The actor post ID.
704-
* @param Actor $actor The actor object.
696+
* @param int $post_id The actor post ID.
697+
* @param Actor|array|object $actor The actor object or data array.
698+
*
699+
* @return string|null The cached avatar URL, or null if no avatar.
705700
*/
706701
private static function cache_avatar( $post_id, $actor ) {
707-
$remote_avatar_url = object_to_uri( $actor->get_icon() );
702+
if ( \is_array( $actor ) || ( \is_object( $actor ) && ! $actor instanceof Actor ) ) {
703+
$remote_avatar_url = object_to_uri( $actor['icon'] ?? ( $actor->icon ?? null ) );
704+
} else {
705+
$remote_avatar_url = object_to_uri( $actor->get_icon() );
706+
}
708707

709708
if ( empty( $remote_avatar_url ) ) {
710709
// No avatar to save, clean up any existing avatar.
711710
Attachments::delete_actors_directory( $post_id );
712711
\delete_post_meta( $post_id, '_activitypub_avatar_url' );
713-
return;
712+
return null;
714713
}
715714

716715
// Download and save the avatar locally.
717-
$local_url = Attachments::save_actor_avatar( $remote_avatar_url, $post_id );
716+
$local_url = Attachments::save_actor_avatar( $post_id, $remote_avatar_url );
718717

719718
// Store the local URL if caching succeeded, otherwise store the remote URL.
720719
$avatar_url = $local_url ?: $remote_avatar_url;
721720
\update_post_meta( $post_id, '_activitypub_avatar_url', \esc_url_raw( $avatar_url ) );
721+
722+
return $avatar_url;
722723
}
723724
}

tests/phpunit/tests/includes/class-test-attachments.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ public function test_save_actor_avatar_success() {
684684
);
685685

686686
$avatar_url = 'https://example.com/avatar.jpg';
687-
$result = Attachments::save_actor_avatar( $avatar_url, $actor_id );
687+
$result = Attachments::save_actor_avatar( $actor_id, $avatar_url );
688688

689689
$this->assertIsString( $result );
690690
$this->assertStringContainsString( 'wp-content/uploads/activitypub/actors/' . $actor_id, $result );
@@ -704,7 +704,7 @@ public function test_save_actor_avatar_empty_url() {
704704
)
705705
);
706706

707-
$result = Attachments::save_actor_avatar( '', $actor_id );
707+
$result = Attachments::save_actor_avatar( $actor_id, '' );
708708

709709
$this->assertFalse( $result );
710710
}
@@ -721,7 +721,7 @@ public function test_save_actor_avatar_invalid_url() {
721721
)
722722
);
723723

724-
$result = Attachments::save_actor_avatar( 'not-a-valid-url', $actor_id );
724+
$result = Attachments::save_actor_avatar( $actor_id, 'not-a-valid-url' );
725725

726726
$this->assertFalse( $result );
727727
}
@@ -739,7 +739,7 @@ public function test_save_actor_avatar_download_error() {
739739
);
740740

741741
// Use the missing.jpg URL that our mock returns as an error.
742-
$result = Attachments::save_actor_avatar( 'https://example.com/missing.jpg', $actor_id );
742+
$result = Attachments::save_actor_avatar( $actor_id, 'https://example.com/missing.jpg' );
743743

744744
$this->assertFalse( $result );
745745
}
@@ -758,11 +758,11 @@ public function test_save_actor_avatar_replaces_existing() {
758758
);
759759

760760
// Save first avatar.
761-
$first_result = Attachments::save_actor_avatar( 'https://example.com/avatar1.jpg', $actor_id );
761+
$first_result = Attachments::save_actor_avatar( $actor_id, 'https://example.com/avatar1.jpg' );
762762
$this->assertIsString( $first_result );
763763

764764
// Save second avatar (should replace the first).
765-
$second_result = Attachments::save_actor_avatar( 'https://example.com/avatar2.jpg', $actor_id );
765+
$second_result = Attachments::save_actor_avatar( $actor_id, 'https://example.com/avatar2.jpg' );
766766
$this->assertIsString( $second_result );
767767

768768
// URLs should be different (different source filenames).
@@ -782,7 +782,7 @@ public function test_delete_actors_directory() {
782782
);
783783

784784
// Save an avatar first.
785-
$avatar_url = Attachments::save_actor_avatar( 'https://example.com/avatar.jpg', $actor_id );
785+
$avatar_url = Attachments::save_actor_avatar( $actor_id, 'https://example.com/avatar.jpg' );
786786
$this->assertIsString( $avatar_url );
787787

788788
// Get the directory path.

0 commit comments

Comments
 (0)