diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 2e0f7a8a0..f34a4327f 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -96,23 +96,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do Map.put(data, "to", to) end - def fix_quote_url(%{"quoteUrl" => _quote_url} = data), do: data + def fix_quote_url(%{"quoteUrl" => quote_url} = data) do + case normalize_object_id(quote_url) do + quote_url when is_binary(quote_url) -> Map.put(data, "quoteUrl", quote_url) + _ -> Map.delete(data, "quoteUrl") + end + end # Fedibird # https://github.com/fedibird/mastodon/commit/dbd7ae6cf58a92ec67c512296b4daaea0d01e6ac def fix_quote_url(%{"quoteUri" => quote_url} = data) do - Map.put(data, "quoteUrl", quote_url) + case normalize_object_id(quote_url) do + quote_url when is_binary(quote_url) -> Map.put(data, "quoteUrl", quote_url) + _ -> data + end end # Old Fedibird (bug) # https://github.com/fedibird/mastodon/issues/9 def fix_quote_url(%{"quoteURL" => quote_url} = data) do - Map.put(data, "quoteUrl", quote_url) + case normalize_object_id(quote_url) do + quote_url when is_binary(quote_url) -> Map.put(data, "quoteUrl", quote_url) + _ -> data + end end # Misskey fallback def fix_quote_url(%{"_misskey_quote" => quote_url} = data) do - Map.put(data, "quoteUrl", quote_url) + case normalize_object_id(quote_url) do + quote_url when is_binary(quote_url) -> Map.put(data, "quoteUrl", quote_url) + _ -> data + end end def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do @@ -128,6 +142,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do def fix_quote_url(data), do: data + defp normalize_object_id(object_id) when is_binary(object_id), do: object_id + defp normalize_object_id(%{"id" => object_id}) when is_binary(object_id), do: object_id + defp normalize_object_id([object_id | _]) when is_binary(object_id), do: object_id + defp normalize_object_id(_), do: nil + # On Mastodon, `"likes"` attribute includes an inlined `Collection` with `totalItems`, # not a list of users. # https://github.com/mastodon/mastodon/pull/32007 diff --git a/test/fixtures/fedidev.fun/misskey_quote_list.json b/test/fixtures/fedidev.fun/misskey_quote_list.json new file mode 100644 index 000000000..b9ea08691 --- /dev/null +++ b/test/fixtures/fedidev.fun/misskey_quote_list.json @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams" + ], + "type": "Create", + "id": "https://fedidev.fun/activity/misskey-quote-list", + "actor": "https://fedidev.fun/users/alice", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "published": "2025-11-21T14:43:53Z", + "object": { + "type": "Note", + "id": "https://fedidev.fun/objects/note-misskey-quote-list", + "attributedTo": "https://fedidev.fun/users/alice", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "published": "2025-11-21T14:43:53Z", + "content": "_misskey_quote attribute as list", + "_misskey_quote": [ + "https://fedidev.fun/assets/note1.jsonap", + "https://fedidev.fun/assets/note2.jsonap" + ] + } +} diff --git a/test/fixtures/fedidev.fun/quote_uri_list.json b/test/fixtures/fedidev.fun/quote_uri_list.json new file mode 100644 index 000000000..9245077e6 --- /dev/null +++ b/test/fixtures/fedidev.fun/quote_uri_list.json @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams" + ], + "type": "Create", + "id": "https://fedidev.fun/activity/quote-uri-list", + "actor": "https://fedidev.fun/users/alice", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "published": "2025-11-21T14:43:52Z", + "object": { + "type": "Note", + "id": "https://fedidev.fun/objects/note-quote-uri-list", + "attributedTo": "https://fedidev.fun/users/alice", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "published": "2025-11-21T14:43:52Z", + "content": "quoteUri attribute as list", + "quoteUri": [ + "https://fedidev.fun/assets/note1.jsonap", + "https://fedidev.fun/assets/note2.jsonap" + ] + } +} diff --git a/test/fixtures/fedidev.fun/quote_url_list.json b/test/fixtures/fedidev.fun/quote_url_list.json new file mode 100644 index 000000000..eecd0dd35 --- /dev/null +++ b/test/fixtures/fedidev.fun/quote_url_list.json @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams" + ], + "type": "Create", + "id": "https://fedidev.fun/activity/quote-url-list", + "actor": "https://fedidev.fun/users/alice", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "published": "2025-11-21T14:43:52Z", + "object": { + "type": "Note", + "id": "https://fedidev.fun/objects/note-quote-url-list", + "attributedTo": "https://fedidev.fun/users/alice", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "published": "2025-11-21T14:43:52Z", + "content": "quoteUrl attribute as list", + "quoteUrl": [ + "https://fedidev.fun/assets/note1.jsonap", + "https://fedidev.fun/assets/note2.jsonap" + ] + } +} diff --git a/test/pleroma/web/activity_pub/transmogrifier/fedidev_fun_quote_url_list_test.exs b/test/pleroma/web/activity_pub/transmogrifier/fedidev_fun_quote_url_list_test.exs new file mode 100644 index 000000000..9607fabdd --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/fedidev_fun_quote_url_list_test.exs @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.FedidevFunQuoteUrlListTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + defp ensure_remote_actor!(ap_id) when is_binary(ap_id) do + case User.get_cached_by_ap_id(ap_id) do + %User{} -> + :ok + + _ -> + insert(:user, + local: false, + ap_id: ap_id, + follower_address: ap_id <> "/followers", + following_address: ap_id <> "/following", + featured_address: ap_id <> "/collections/featured", + inbox: ap_id <> "/inbox" + ) + + :ok + end + end + + defp ingest_fixture!(rel_path) do + data = rel_path |> File.read!() |> Jason.decode!() + ensure_remote_actor!(data["actor"]) + + assert {:ok, activity} = Transmogrifier.handle_incoming(data) + object = Object.normalize(activity.data["object"], fetch: false) + + {activity, object} + end + + test "normalizes quoteUri list to the first object ID" do + {_activity, object} = ingest_fixture!("test/fixtures/fedidev.fun/quote_uri_list.json") + assert object.data["quoteUrl"] == "https://fedidev.fun/assets/note1.jsonap" + end + + test "normalizes quoteUrl list to the first object ID" do + {_activity, object} = ingest_fixture!("test/fixtures/fedidev.fun/quote_url_list.json") + assert object.data["quoteUrl"] == "https://fedidev.fun/assets/note1.jsonap" + end + + test "normalizes _misskey_quote list to the first object ID" do + {_activity, object} = ingest_fixture!("test/fixtures/fedidev.fun/misskey_quote_list.json") + assert object.data["quoteUrl"] == "https://fedidev.fun/assets/note1.jsonap" + end +end