diff --git a/changelog.d/preserve-public-cc.fix b/changelog.d/preserve-public-cc.fix new file mode 100644 index 000000000..1b20ce9ad --- /dev/null +++ b/changelog.d/preserve-public-cc.fix @@ -0,0 +1 @@ +Fix federation issue where Public visibility information in cc field was lost when sent to remote servers, causing posts to appear with inconsistent visibility across instances diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 0de3a0d43..f160f1e17 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -93,7 +93,20 @@ defmodule Pleroma.Web.ActivityPub.Publisher do {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) - cc = Map.get(params, :cc, []) + param_cc = Map.get(params, :cc, []) + + original_cc = Map.get(data, "cc", []) + + public_address = Pleroma.Constants.as_public() + + # Ensure unlisted posts don't lose the public address in the cc + # if the param_cc was set + cc = + if public_address in original_cc and public_address not in param_cc do + [public_address | param_cc] + else + param_cc + end json = data diff --git a/test/pleroma/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs index 99ed42877..b7ff0ed5f 100644 --- a/test/pleroma/web/activity_pub/publisher_test.exs +++ b/test/pleroma/web/activity_pub/publisher_test.exs @@ -520,4 +520,105 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do assert decoded["cc"] == [] end + + test "unlisted activities retain public address in cc" do + user = insert(:user) + + # simulate unlistd activity by only having + # public address in cc + activity = + insert(:note_activity, + user: user, + data_attrs: %{ + "cc" => [@as_public], + "to" => [user.follower_address] + } + ) + + assert @as_public in activity.data["cc"] + + prepared = + Publisher.prepare_one(%{ + inbox: "https://remote.instance/users/someone/inbox", + activity_id: activity.id + }) + + {:ok, decoded} = Jason.decode(prepared.json) + + assert @as_public in decoded["cc"] + + # maybe we also have another inbox in cc + # during Publishing + activity = + insert(:note_activity, + user: user, + data_attrs: %{ + "cc" => [@as_public], + "to" => [user.follower_address] + } + ) + + prepared = + Publisher.prepare_one(%{ + inbox: "https://remote.instance/users/someone/inbox", + activity_id: activity.id, + cc: ["https://remote.instance/users/someone_else/inbox"] + }) + + {:ok, decoded} = Jason.decode(prepared.json) + + assert decoded["cc"] == [@as_public, "https://remote.instance/users/someone_else/inbox"] + end + + test "public address in cc parameter is preserved" do + user = insert(:user) + + cc_with_public = [@as_public, "https://example.org/users/other"] + + activity = + insert(:note_activity, + user: user, + data_attrs: %{ + "cc" => cc_with_public, + "to" => [user.follower_address] + } + ) + + assert @as_public in activity.data["cc"] + + prepared = + Publisher.prepare_one(%{ + inbox: "https://remote.instance/users/someone/inbox", + activity_id: activity.id, + cc: cc_with_public + }) + + {:ok, decoded} = Jason.decode(prepared.json) + + assert cc_with_public == decoded["cc"] + end + + test "cc parameter is preserved" do + user = insert(:user) + + activity = + insert(:note_activity, + user: user, + data_attrs: %{ + "cc" => ["https://example.com/specific/user"], + "to" => [user.follower_address] + } + ) + + prepared = + Publisher.prepare_one(%{ + inbox: "https://remote.instance/users/someone/inbox", + activity_id: activity.id, + cc: ["https://example.com/specific/user"] + }) + + {:ok, decoded} = Jason.decode(prepared.json) + + assert decoded["cc"] == ["https://example.com/specific/user"] + end end