updated plugin ActivityPub version 9.1.0

This commit is contained in:
2026-07-28 15:03:10 +00:00
committed by Gitium
parent bf428f0e45
commit ff806e1811
217 changed files with 6098 additions and 3025 deletions

View File

@ -10,11 +10,11 @@ namespace Activitypub\Collection;
use Activitypub\Comment;
use Activitypub\Emoji;
use Activitypub\Webfinger;
use WP_Comment_Query;
use function Activitypub\get_remote_metadata_by_actor;
use function Activitypub\is_ap_post;
use function Activitypub\is_post_disabled;
use function Activitypub\is_same_host;
use function Activitypub\object_id_to_comment;
use function Activitypub\object_to_uri;
use function Activitypub\url_to_commentid;
@ -38,6 +38,17 @@ class Interactions {
* @return int|false|\WP_Error The comment ID or false or WP_Error on failure.
*/
public static function add_comment( $activity, $user_id = null ) {
/*
* A remote comment is stored under its object id (source_id); that id must be on
* the signature-verified actor's host. Otherwise a remote server could file a
* comment whose recorded id points at a different host, mis-recording its
* provenance and taking over that id (the update owner-check would then reject the
* genuine author). Local outbox replies ($user_id set) are trusted.
*/
if ( null === $user_id && ! is_same_host( $activity['actor'] ?? '', $activity['object'] ?? '' ) ) {
return false;
}
$comment_data = self::activity_to_comment( $activity, $user_id );
if ( ! $comment_data ) {
@ -120,6 +131,29 @@ class Interactions {
return false;
}
/*
* Only the comment's author may update it. The comment maps to the remote actor that
* created it via _activitypub_remote_actor_id; that actor post's guid is the
* (signature-bound) actor URI. The Update's actor must match it, otherwise a remote
* server could rewrite another actor's comment by sending an Update whose object.id
* points at it.
*
* Comments created before this mapping existed have no owner recorded; those are let
* through for backward compatibility (matching the Undo path) rather than becoming
* permanently un-editable. On mismatch, return a WP_Error rather than false: false would
* make the Update handler fall back to Create (which re-dispatches to Update for an
* existing comment and recurses), while the unchanged comment array would be read as a
* successful update and relayed onward. A WP_Error is handled but unsuccessful: no Create
* fallback, and the handled-update success flag stays false.
*/
$owner = \get_post( (int) \get_comment_meta( $comment_data['comment_ID'], '_activitypub_remote_actor_id', true ) );
if ( $owner instanceof \WP_Post && object_to_uri( $activity['actor'] ) !== $owner->guid ) {
return new \WP_Error(
'activitypub_update_forbidden',
\__( 'The Update actor does not own the target comment.', 'activitypub' )
);
}
// Found a local comment id.
$comment_data['comment_author'] = \sanitize_text_field( empty( $meta['name'] ) ? $meta['preferredUsername'] : $meta['name'] );
@ -141,6 +175,15 @@ class Interactions {
* @return array|string|int|\WP_Error|false Comment data or `false` on failure.
*/
public static function add_reaction( $activity ) {
/*
* The reaction is stored under its own id (source_id); that id must be on the
* signature-verified actor's host, so a remote server cannot file a reaction
* whose recorded id points at a different host and take over that id.
*/
if ( ! is_same_host( $activity['actor'] ?? '', $activity['id'] ?? '' ) ) {
return false;
}
$url = object_to_uri( $activity['object'] );
$comment_post_id = \url_to_postid( $url );
$parent_comment_id = url_to_commentid( $url );
@ -214,7 +257,7 @@ class Interactions {
),
);
$query = new WP_Comment_Query( $args );
$query = new \WP_Comment_Query( $args );
return $query->comments;
}
@ -244,7 +287,7 @@ class Interactions {
$meta = get_remote_metadata_by_actor( $actor );
// Get URL, because $actor seems to be the ID.
if ( $meta && ! is_wp_error( $meta ) && isset( $meta['url'] ) ) {
if ( $meta && ! \is_wp_error( $meta ) && isset( $meta['url'] ) ) {
$actor = object_to_uri( $meta['url'] );
}
@ -323,17 +366,17 @@ class Interactions {
}
// Add `p` and `br` to the list of allowed tags.
if ( ! array_key_exists( 'br', $allowed_tags ) ) {
if ( ! \array_key_exists( 'br', $allowed_tags ) ) {
$allowed_tags['br'] = array();
}
if ( ! array_key_exists( 'p', $allowed_tags ) ) {
if ( ! \array_key_exists( 'p', $allowed_tags ) ) {
$allowed_tags['p'] = array();
}
// Add `img` for custom emoji support with strict validation.
$emoji_html = Emoji::get_kses_allowed_html();
if ( ! array_key_exists( 'img', $allowed_tags ) ) {
if ( ! \array_key_exists( 'img', $allowed_tags ) ) {
$allowed_tags['img'] = $emoji_html['img'];
}
@ -371,7 +414,7 @@ class Interactions {
$actor = object_to_uri( $activity['actor'] ?? null );
$actor = get_remote_metadata_by_actor( $actor );
if ( ! $actor || is_wp_error( $actor ) ) {
if ( ! $actor || \is_wp_error( $actor ) ) {
return false;
}
@ -386,14 +429,14 @@ class Interactions {
return false;
}
$comment_author = $comment_author ?? __( 'Anonymous', 'activitypub' );
$comment_author = $comment_author ?? \__( 'Anonymous', 'activitypub' );
$comment_author_url = \esc_url_raw( object_to_uri( $actor['url'] ?? $actor['id'] ) );
$webfinger = Webfinger::uri_to_acct( $comment_author_url );
if ( is_wp_error( $webfinger ) ) {
if ( \is_wp_error( $webfinger ) ) {
$comment_author_email = '';
} else {
$comment_author_email = str_replace( 'acct:', '', $webfinger );
$comment_author_email = \str_replace( 'acct:', '', $webfinger );
}
if ( isset( $activity['object']['content'] ) ) {