From 6ed9d681b902f6d5be3c2e37f5bc2704501bb5f5 Mon Sep 17 00:00:00 2001 From: Mint Date: Fri, 7 Nov 2025 14:53:16 +0300 Subject: [PATCH 1/3] Transmogrifier: convert "as:Public" to full w3 URL --- changelog.d/transmogrifier-aspublic.fix | 1 + .../object_validators/common_fixes.ex | 6 ++++ .../web/activity_pub/transmogrifier.ex | 20 +++++++++++ test/fixtures/bovine-bogus-public-note.json | 34 +++++++++++++++++++ .../web/activity_pub/transmogrifier_test.exs | 23 +++++++++++++ 5 files changed, 84 insertions(+) create mode 100644 changelog.d/transmogrifier-aspublic.fix create mode 100644 test/fixtures/bovine-bogus-public-note.json diff --git a/changelog.d/transmogrifier-aspublic.fix b/changelog.d/transmogrifier-aspublic.fix new file mode 100644 index 000000000..36610cbed --- /dev/null +++ b/changelog.d/transmogrifier-aspublic.fix @@ -0,0 +1 @@ +Transmogrifier: convert "as:Public" to full w3 URL 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 f0f3fef90..7cee4c8e8 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -20,6 +20,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do require Pleroma.Constants def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do + # calling this here since we need to fix as:Public address before ObjectID cast throws it out + message = + message + |> Transmogrifier.fix_addressing_list(field) + |> Transmogrifier.fix_addressing_public(field) + {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) data = diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 00339fad9..c96badb11 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -104,6 +104,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end + @doc """ + Bovine compatibility + https://codeberg.org/bovine/bovine/issues/53 + """ + def fix_addressing_public(map, field) do + Map.put( + map, + field, + Enum.map(Map.get(map, field), fn + "Public" -> Pleroma.Constants.as_public() + "as:Public" -> Pleroma.Constants.as_public() + x -> x + end) + ) + end + # if directMessage flag is set to true, leave the addressing alone def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection), do: object @@ -161,6 +177,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_addressing_list("cc") |> fix_addressing_list("bto") |> fix_addressing_list("bcc") + |> fix_addressing_public("to") + |> fix_addressing_public("cc") + |> fix_addressing_public("bto") + |> fix_addressing_public("bcc") |> fix_explicit_addressing(follower_collection) |> fix_implicit_addressing(follower_collection) end diff --git a/test/fixtures/bovine-bogus-public-note.json b/test/fixtures/bovine-bogus-public-note.json new file mode 100644 index 000000000..032bf49dc --- /dev/null +++ b/test/fixtures/bovine-bogus-public-note.json @@ -0,0 +1,34 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/multikey/v1", + { + "Hashtag": "as:Hashtag" + } + ], + "attributedTo": "https://mymath.rocks/endpoints/SYn3cl_N4HAPfPHgo2x37XunLEmhV9LnxCggcYwyec0", + "cc": [ + "https://mymath.rocks/endpoints/30zoCe7haKBEFolH4rbAmKj-t9_bG0c2X2kMQkJk5qY", + "https://mastodon.social/users/nikclayton" + ], + "content": "
\n

I note that mymath.rocks does not provide this information.

\n
\n

I'm a big believer in "Do as I say, not as I did".

\n

I could give a long list of technical reasons, which boil down to: nodeinfo is pretty nonsensical with the way I write stuff.

\n

I think the above statement also addresses a main failure of the Fediverse. People, e.g. me, would love to fix stuff. Unfortunately, we lack the focus to address a lot of issues, e.g. nodeinfo sucks. So stuff gets done in a broken way.

\n

I think the main challenge the Fediverse has faced, and failed at, is avoiding the above situation. To continue the example, there is no way for somebody to say: Let's fix nodeinfo and people will follow their ideas.

\n", + "id": "https://mymath.rocks/objects/2b89e564-30cf-4eeb-97ca-7e638a154026", + "inReplyTo": "https://mastodon.social/users/nikclayton/statuses/115496665258618127", + "likes": "https://mymath.rocks/objects/2b89e564-30cf-4eeb-97ca-7e638a154026/likes", + "published": "2025-11-06T08:21:17.790Z", + "replies": "https://mymath.rocks/objects/2b89e564-30cf-4eeb-97ca-7e638a154026/replies", + "shares": "https://mymath.rocks/objects/2b89e564-30cf-4eeb-97ca-7e638a154026/shares", + "source": { + "content": "> I note that mymath.rocks does not provide this information.\n\nI'm a big believer in \"Do as I say, not as I __did__\".\n\nI could give a long list of technical reasons, which boil down to: nodeinfo is pretty nonsensical with the way I write stuff.\n\nI think the above statement also addresses a main failure of the Fediverse. People, e.g. me, would love to fix stuff. Unfortunately, we lack the focus to address a lot of issues, e.g. nodeinfo sucks. So stuff gets done in a broken way.\n\nI think the main challenge the Fediverse has faced, and failed at, is avoiding the above situation. To continue the example, there is no way for somebody to say: Let's fix nodeinfo and people will follow their ideas.", + "mediaType": "text/markdown" + }, + "tag": { + "href": "https://mastodon.social/users/nikclayton", + "name": "https://mastodon.social/users/nikclayton", + "type": "Mention" + }, + "to": "as:Public", + "type": "Note" +} diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index e34a101b2..24f545118 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -123,6 +123,29 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert activity.data["context"] == object.data["context"] end + test "it fixes the public scope addressing" do + insert(:user, + ap_id: "https://mymath.rocks/endpoints/SYn3cl_N4HAPfPHgo2x37XunLEmhV9LnxCggcYwyec0" + ) + + object = + "test/fixtures/bovine-bogus-public-note.json" + |> File.read!() + |> Jason.decode!() + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "actor" => "https://mymath.rocks/endpoints/SYn3cl_N4HAPfPHgo2x37XunLEmhV9LnxCggcYwyec0", + "object" => object + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + object = Object.normalize(activity, fetch: false) + assert "https://www.w3.org/ns/activitystreams#Public" in object.data["to"] + end + test "it keeps link tags" do insert(:user, ap_id: "https://example.org/users/alice") From 4496dc81c4e6bccf88eb4f0397e8a7c65a8cf17a Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 21 Dec 2025 15:19:05 +0400 Subject: [PATCH 2/3] TransmogrifierTest, CreateGenericValidatorTest: Add regression tests for addressing. --- .../create_generic_validator_test.exs | 33 +++++++++++++++++++ .../web/activity_pub/transmogrifier_test.exs | 1 + 2 files changed, 34 insertions(+) diff --git a/test/pleroma/web/activity_pub/object_validators/create_generic_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/create_generic_validator_test.exs index e771260c9..e6231dbcd 100644 --- a/test/pleroma/web/activity_pub/object_validators/create_generic_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/create_generic_validator_test.exs @@ -59,4 +59,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidatorTest do assert validated.valid? assert {:context, note["context"]} in validated.changes end + + test "a Create/Note without addressing falls back to the Note's recipients" do + user = insert(:user) + + note = %{ + "id" => Utils.generate_object_id(), + "type" => "Note", + "actor" => user.ap_id, + "to" => [user.follower_address], + "cc" => [], + "content" => "Hello world", + "context" => Utils.generate_context_id() + } + + note_activity = %{ + "id" => Utils.generate_activity_id(), + "type" => "Create", + "actor" => note["actor"], + "object" => note, + "published" => DateTime.utc_now() |> DateTime.to_iso8601(), + "context" => Utils.generate_context_id() + } + + # Build metadata + {:ok, object_data} = ObjectValidator.cast_and_apply(note_activity["object"]) + meta = [object_data: ObjectValidator.stringify_keys(object_data)] + + validated = CreateGenericValidator.cast_and_validate(note_activity, meta) + + assert validated.valid? + assert Ecto.Changeset.get_field(validated, :to) == note["to"] + assert Ecto.Changeset.get_field(validated, :cc) == note["cc"] + end end diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 24f545118..6f7b09de6 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -143,6 +143,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert {:ok, activity} = Transmogrifier.handle_incoming(message) object = Object.normalize(activity, fetch: false) + assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"] assert "https://www.w3.org/ns/activitystreams#Public" in object.data["to"] end From ec58b6a4cc40be5f69d21c8262c26504c11a9b60 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 21 Dec 2025 15:19:38 +0400 Subject: [PATCH 3/3] CommonFixes, Transmogrifier: Fix tests. --- .../web/activity_pub/object_validators/common_fixes.ex | 10 ++++++---- lib/pleroma/web/activity_pub/transmogrifier.ex | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) 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 7cee4c8e8..2e0f7a8a0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -20,13 +20,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do require Pleroma.Constants def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do - # calling this here since we need to fix as:Public address before ObjectID cast throws it out - message = - message + # Fix as:Public/Public before ObjectID casting drops it, but keep `field_fallback` + # semantics (only used when the field is missing). + recipients = + %{field => message[field] || field_fallback} |> Transmogrifier.fix_addressing_list(field) |> Transmogrifier.fix_addressing_public(field) + |> Map.fetch!(field) - {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) + {:ok, data} = ObjectValidators.Recipients.cast(recipients) data = Enum.reject(data, fn x -> diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index c96badb11..a358f3925 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -109,10 +109,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do https://codeberg.org/bovine/bovine/issues/53 """ def fix_addressing_public(map, field) do + addrs = Map.get(map, field, []) |> List.wrap() + Map.put( map, field, - Enum.map(Map.get(map, field), fn + Enum.map(addrs, fn "Public" -> Pleroma.Constants.as_public() "as:Public" -> Pleroma.Constants.as_public() x -> x