Skip to content

Commit e02f223

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 f6252b5 commit e02f223

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.
@@ -992,17 +992,24 @@ private static function get_files_gallery_block( $files ) {
992992
* Downloads the avatar image, optimizes it, and stores it in the actors directory.
993993
* Returns the local URL for the saved avatar.
994994
*
995-
* @param string $avatar_url The remote avatar URL.
996995
* @param int $actor_id The local actor post ID.
996+
* @param string $avatar_url The remote avatar URL.
997997
*
998998
* @return string|false The local avatar URL on success, false on failure.
999999
*/
1000-
public static function save_actor_avatar( $avatar_url, $actor_id ) {
1000+
public static function save_actor_avatar( $actor_id, $avatar_url ) {
1001+
// Validate actor_id is a positive integer to prevent path traversal.
1002+
$actor_id = (int) $actor_id;
1003+
if ( $actor_id <= 0 ) {
1004+
return false;
1005+
}
1006+
10011007
if ( empty( $avatar_url ) || ! \filter_var( $avatar_url, FILTER_VALIDATE_URL ) ) {
10021008
return false;
10031009
}
10041010

10051011
// Delete existing avatar files before saving new one.
1012+
// This prevents accumulating old avatar files since save_file creates unique filenames.
10061013
self::delete_actors_directory( $actor_id );
10071014

10081015
$attachment_data = array( 'url' => $avatar_url );
@@ -1021,6 +1028,12 @@ public static function save_actor_avatar( $avatar_url, $actor_id ) {
10211028
* @param int $actor_id The actor post ID.
10221029
*/
10231030
public static function delete_actors_directory( $actor_id ) {
1031+
// Validate actor_id is a positive integer to prevent path traversal.
1032+
$actor_id = (int) $actor_id;
1033+
if ( $actor_id <= 0 ) {
1034+
return;
1035+
}
1036+
10241037
// When used as hook callback, only process actor post types.
10251038
$post_type = \get_post_type( $actor_id );
10261039
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
@@ -1057,7 +1057,7 @@ public function test_save_actor_avatar_success() {
10571057
);
10581058

10591059
$avatar_url = 'https://example.com/avatar.jpg';
1060-
$result = Attachments::save_actor_avatar( $avatar_url, $actor_id );
1060+
$result = Attachments::save_actor_avatar( $actor_id, $avatar_url );
10611061

10621062
$this->assertIsString( $result );
10631063
$this->assertStringContainsString( 'wp-content/uploads/activitypub/actors/' . $actor_id, $result );
@@ -1077,7 +1077,7 @@ public function test_save_actor_avatar_empty_url() {
10771077
)
10781078
);
10791079

1080-
$result = Attachments::save_actor_avatar( '', $actor_id );
1080+
$result = Attachments::save_actor_avatar( $actor_id, '' );
10811081

10821082
$this->assertFalse( $result );
10831083
}
@@ -1094,7 +1094,7 @@ public function test_save_actor_avatar_invalid_url() {
10941094
)
10951095
);
10961096

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

10991099
$this->assertFalse( $result );
11001100
}
@@ -1112,7 +1112,7 @@ public function test_save_actor_avatar_download_error() {
11121112
);
11131113

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

11171117
$this->assertFalse( $result );
11181118
}
@@ -1131,11 +1131,11 @@ public function test_save_actor_avatar_replaces_existing() {
11311131
);
11321132

11331133
// Save first avatar.
1134-
$first_result = Attachments::save_actor_avatar( 'https://example.com/avatar1.jpg', $actor_id );
1134+
$first_result = Attachments::save_actor_avatar( $actor_id, 'https://example.com/avatar1.jpg' );
11351135
$this->assertIsString( $first_result );
11361136

11371137
// Save second avatar (should replace the first).
1138-
$second_result = Attachments::save_actor_avatar( 'https://example.com/avatar2.jpg', $actor_id );
1138+
$second_result = Attachments::save_actor_avatar( $actor_id, 'https://example.com/avatar2.jpg' );
11391139
$this->assertIsString( $second_result );
11401140

11411141
// URLs should be different (different source filenames).
@@ -1155,7 +1155,7 @@ public function test_delete_actors_directory() {
11551155
);
11561156

11571157
// Save an avatar first.
1158-
$avatar_url = Attachments::save_actor_avatar( 'https://example.com/avatar.jpg', $actor_id );
1158+
$avatar_url = Attachments::save_actor_avatar( $actor_id, 'https://example.com/avatar.jpg' );
11591159
$this->assertIsString( $avatar_url );
11601160

11611161
// Get the directory path.

0 commit comments

Comments
 (0)