| @@ -30,9 +30,26 @@ | ||
| 30 | 30 | * @param int|int[]|null $user_ids The user ID(s). |
| 31 | 31 | */ |
| 32 | 32 | public static function handle_undo( $activity, $user_ids ) { |
| 33 | 33 | $success = false; |
| 34 | - $result = Inbox_Collection::undo( object_to_uri( $activity['object'] ) ); | |
| 34 | + | |
| 35 | + /* | |
| 36 | + * Resolve the sender so Inbox::undo() can verify ownership. A genuinely absent actor | |
| 37 | + * maps to null (no ownership check, for programmatic callers), but an actor that is | |
| 38 | + * present yet unparseable must be rejected rather than skipping the check — passing | |
| 39 | + * null there would re-open the undo-by-id attack. | |
| 40 | + */ | |
| 41 | + $actor = isset( $activity['actor'] ) ? object_to_uri( $activity['actor'] ) : null; | |
| 42 | + | |
| 43 | + if ( isset( $activity['actor'] ) && empty( $actor ) ) { | |
| 44 | + $result = new \WP_Error( | |
| 45 | + 'activitypub_undo_invalid_actor', | |
| 46 | + \__( 'The Undo activity has an invalid actor.', 'activitypub' ), | |
| 47 | + array( 'status' => 400 ) | |
| 48 | + ); | |
| 49 | + } else { | |
| 50 | + $result = Inbox_Collection::undo( object_to_uri( $activity['object'] ), $actor ); | |
| 51 | + } | |
| 35 | 52 | |
| 36 | 53 | if ( $result && ! \is_wp_error( $result ) ) { |
| 37 | 54 | $success = true; |
| 38 | 55 | } |