From a72f9cff232c6afd7aff8ab41b21d82779d8a086 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 16 Oct 2025 15:03:54 +0200 Subject: [PATCH 001/106] Elixir 1.19: Fix typing violation on struct updates in Pleroma.Marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Pleroma.Marker is expected on struct update: %Pleroma.Marker{marker | user: user} but got type: dynamic() where "marker" was given the type: # type: dynamic() # from: lib/pleroma/marker.ex {:ok, marker} when defining the variable "marker", you must also pattern match on "%Pleroma.Marker{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 81 │ {:ok, marker} -> %__MODULE__{marker | user: user} │ ~ │ └─ lib/pleroma/marker.ex:81:24: Pleroma.Marker.get_marker/2 --- lib/pleroma/marker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex index 68b054e4d..4f645240e 100644 --- a/lib/pleroma/marker.ex +++ b/lib/pleroma/marker.ex @@ -78,7 +78,7 @@ defmodule Pleroma.Marker do defp get_marker(user, timeline) do case Repo.find_resource(get_query(user, timeline)) do - {:ok, marker} -> %__MODULE__{marker | user: user} + {:ok, %__MODULE__{} = marker} -> %__MODULE__{marker | user: user} _ -> %__MODULE__{timeline: timeline, user_id: user.id} end end From 0fc046a75e0499a068cbdee82f9c559407a7521b Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 16 Oct 2025 15:06:02 +0200 Subject: [PATCH 002/106] Elixir 1.19: Fix typing violation on struct updates in MFA.Changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Pleroma.MFA.Settings is expected on struct update: %Pleroma.MFA.Settings{settings | enabled: false} but got type: dynamic() where "settings" was given the type: # type: dynamic() # from: lib/pleroma/mfa/changeset.ex:11:14 settings = Pleroma.MFA.fetch_settings(Ecto.Changeset.apply_changes(changeset)) when defining the variable "settings", you must also pattern match on "%Pleroma.MFA.Settings{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 17 │ put_change(changeset, %Settings{settings | enabled: false}) │ ~ │ └─ lib/pleroma/mfa/changeset.ex:17:29: Pleroma.MFA.Changeset.disable/2 --- warning: a struct for Pleroma.MFA.Settings is expected on struct update: %Pleroma.MFA.Settings{ settings | totp: %Pleroma.MFA.Settings.TOTP{confirmed: false, delivery_type: "app", secret: nil} } but got type: dynamic() where "settings" was given the type: # type: dynamic() # from: lib/pleroma/mfa/changeset.ex:23:74 %Pleroma.User{multi_factor_authentication_settings: settings} = user when defining the variable "settings", you must also pattern match on "%Pleroma.MFA.Settings{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 25 │ |> put_change(%Settings{settings | totp: %Settings.TOTP{}}) │ ~ │ └─ lib/pleroma/mfa/changeset.ex:25:19: Pleroma.MFA.Changeset.disable_totp/1 --- lib/pleroma/mfa/changeset.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/mfa/changeset.ex b/lib/pleroma/mfa/changeset.ex index 3ec3cfe91..890cb2193 100644 --- a/lib/pleroma/mfa/changeset.ex +++ b/lib/pleroma/mfa/changeset.ex @@ -8,7 +8,7 @@ defmodule Pleroma.MFA.Changeset do alias Pleroma.User def disable(%Ecto.Changeset{} = changeset, force \\ false) do - settings = + %Settings{} = settings = changeset |> Ecto.Changeset.apply_changes() |> MFA.fetch_settings() @@ -20,20 +20,20 @@ defmodule Pleroma.MFA.Changeset do end end - def disable_totp(%User{multi_factor_authentication_settings: settings} = user) do + def disable_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do user |> put_change(%Settings{settings | totp: %Settings.TOTP{}}) end - def confirm_totp(%User{multi_factor_authentication_settings: settings} = user) do - totp_settings = %Settings.TOTP{settings.totp | confirmed: true} + def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do + totp_settings = %Settings.TOTP{%Settings.TOTP{} = settings.totp | confirmed: true} user |> put_change(%Settings{settings | totp: totp_settings, enabled: true}) end def setup_totp(%User{} = user, attrs) do - mfa_settings = MFA.fetch_settings(user) + %Settings{} = mfa_settings = MFA.fetch_settings(user) totp_settings = %Settings.TOTP{} @@ -46,7 +46,7 @@ defmodule Pleroma.MFA.Changeset do def cast_backup_codes(%User{} = user, codes) do user |> put_change(%Settings{ - user.multi_factor_authentication_settings + %Settings{} = user.multi_factor_authentication_settings | backup_codes: codes }) end From 0d3085937ca728c572f19798d21dce12dcf7b89e Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 16 Oct 2025 15:09:03 +0200 Subject: [PATCH 003/106] Elixir 1.19: Fix typing violation on struct updates in Pleroma.Upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Pleroma.Upload is expected on struct update: %Pleroma.Upload{ upload | path: case upload.path do x when x === false or x === nil -> <> x -> x end } but got type: dynamic() where "upload" was given the type: # type: dynamic() # from: lib/pleroma/upload.ex:95:24 {:ok, upload} <- prepare_upload(upload, opts) when defining the variable "upload", you must also pattern match on "%Pleroma.Upload{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 96 │ upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, │ ~ │ └─ lib/pleroma/upload.ex:96:19: Pleroma.Upload.store/2 --- lib/pleroma/upload.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 06d8005bc..350ff6cb3 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -93,7 +93,7 @@ defmodule Pleroma.Upload do def store(upload, opts \\ []) do opts = get_opts(opts) - with {:ok, upload} <- prepare_upload(upload, opts), + with {:ok, %__MODULE__{} = upload} <- prepare_upload(upload, opts), upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), description = get_description(upload), From 6f62cb0ed38c5d2bbdb6e304a287553bad8584f1 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 16 Oct 2025 15:09:51 +0200 Subject: [PATCH 004/106] Elixir 1.19: Fix typing violation on struct updates in Web.ApiSpec.Cast* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Plug.Conn is expected on struct update: %Plug.Conn{conn | query_params: query_params} but got type: dynamic() where "conn" was given the type: # type: dynamic() # from: lib/pleroma/web/api_spec/cast_and_validate.ex:109:43 conn when defining the variable "conn", you must also pattern match on "%Plug.Conn{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 133 │ conn = %Conn{conn | query_params: query_params} │ ~ │ └─ lib/pleroma/web/api_spec/cast_and_validate.ex:133:16: Pleroma.Web.ApiSpec.CastAndValidate.cast_and_validate/6 --- lib/pleroma/web/api_spec/cast_and_validate.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index 672d1c4a1..57ea8e9b3 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -106,7 +106,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts) end - defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do + defp cast_and_validate(spec, operation, %Conn{} = conn, content_type, false = _strict, cast_opts) do case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do {:ok, conn} -> {:ok, conn} From 7c79e4416f78b317d8a3bda4cacd23f3d955df46 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 16 Oct 2025 15:11:33 +0200 Subject: [PATCH 005/106] Elixir 1.19: Fix typing violation on struct updates in Web.ApiSpec.Rend* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for OpenApiSpex.Cast.Error is expected on struct update: %OpenApiSpex.Cast.Error{err | name: err.value} but got type: dynamic(%{..., name: nil, reason: :invalid_enum}) where "err" was given the type: # type: dynamic(%{..., name: nil, reason: :invalid_enum}) # from: lib/pleroma/web/api_spec/render_error.ex:20:45 %{name: nil, reason: :invalid_enum} = err when defining the variable "err", you must also pattern match on "%OpenApiSpex.Cast.Error{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 21 │ %OpenApiSpex.Cast.Error{err | name: err.value} │ ~ │ └─ lib/pleroma/web/api_spec/render_error.ex:21:11: Pleroma.Web.ApiSpec.RenderError.call/2 --- lib/pleroma/web/api_spec/render_error.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex index 3539af6e4..acf510774 100644 --- a/lib/pleroma/web/api_spec/render_error.ex +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -17,10 +17,10 @@ defmodule Pleroma.Web.ApiSpec.RenderError do def call(conn, errors) do errors = Enum.map(errors, fn - %{name: nil, reason: :invalid_enum} = err -> + %OpenApiSpex.Cast.Error{name: nil, reason: :invalid_enum} = err -> %OpenApiSpex.Cast.Error{err | name: err.value} - %{name: nil} = err -> + %OpenApiSpex.Cast.Error{name: nil} = err -> %OpenApiSpex.Cast.Error{err | name: List.last(err.path)} err -> From 6a926bf605b6e33c0fe9242fc0b2c1b8f68b655d Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 16 Oct 2025 15:12:41 +0200 Subject: [PATCH 006/106] Elixir 1.19: Fix typing violation on struct updates in CommonAPI.Activity* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Pleroma.Web.CommonAPI.ActivityDraft is expected on struct update: %Pleroma.Web.CommonAPI.ActivityDraft{draft | object: object} but got type: dynamic() where "draft" was given the type: # type: dynamic() # from: lib/pleroma/web/common_api/activity_draft.ex:91:22 draft when defining the variable "draft", you must also pattern match on "%Pleroma.Web.CommonAPI.ActivityDraft{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 102 │ %__MODULE__{draft | object: object} │ ~ │ └─ lib/pleroma/web/common_api/activity_draft.ex:102:5: Pleroma.Web.CommonAPI.ActivityDraft.listen_object/1 --- lib/pleroma/web/common_api/activity_draft.ex | 46 ++++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index c0b98508c..c5959276a 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -88,7 +88,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> validate() end - defp listen_object(draft) do + defp listen_object(%__MODULE__{} = draft) do object = draft.params |> Map.take([:album, :artist, :title, :length]) @@ -102,20 +102,20 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | object: object} end - defp put_params(draft, params) do + defp put_params(%__MODULE__{} = draft, params) do params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id]) %__MODULE__{draft | params: params} end - defp status(%{params: %{status: status}} = draft) do + defp status(%__MODULE__{params: %{status: status}} = draft) do %__MODULE__{draft | status: String.trim(status)} end - defp summary(%{params: params} = draft) do + defp summary(%__MODULE__{params: params} = draft) do %__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")} end - defp full_payload(%{status: status, summary: summary} = draft) do + defp full_payload(%__MODULE__{status: status, summary: summary} = draft) do full_payload = String.trim(status <> summary) case Utils.validate_character_limit(full_payload, draft.attachments) do @@ -124,7 +124,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end end - defp attachments(%{params: params} = draft) do + defp attachments(%__MODULE__{params: params} = draft) do attachments = Utils.attachments_from_ids(params, draft.user) draft = %__MODULE__{draft | attachments: attachments} @@ -134,9 +134,9 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end end - defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft + defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: ""}} = draft), do: draft - defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do + defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do # If a post was deleted all its activities (except the newly added Delete) are purged too, # thus lookup by Create db ID will yield nil just as if it never existed in the first place. # @@ -166,13 +166,13 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end end - defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do + defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do %__MODULE__{draft | in_reply_to: in_reply_to} end defp in_reply_to(draft), do: draft - defp quote_post(%{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do + defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do case Activity.get_by_id_with_object(id) do %Activity{} = activity -> %__MODULE__{draft | quote_post: activity} @@ -188,12 +188,12 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp quote_post(draft), do: draft - defp in_reply_to_conversation(draft) do + defp in_reply_to_conversation(%__MODULE__{} = draft) do in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id]) %__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation} end - defp visibility(%{params: params} = draft) do + defp visibility(%__MODULE__{params: params} = draft) do case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do {visibility, "direct"} when visibility != "direct" -> add_error(draft, dgettext("errors", "The message visibility must be direct")) @@ -226,14 +226,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp quoting_visibility(draft), do: draft - defp expires_at(draft) do + defp expires_at(%__MODULE__{} = draft) do case CommonAPI.check_expiry_date(draft.params[:expires_in]) do {:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at} {:error, message} -> add_error(draft, message) end end - defp poll(draft) do + defp poll(%__MODULE__{} = draft) do case Utils.make_poll_data(draft.params) do {:ok, {poll, poll_emoji}} -> %__MODULE__{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)} @@ -243,7 +243,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end end - defp content(%{mentions: mentions} = draft) do + defp content(%__MODULE__{mentions: mentions} = draft) do {content_html, mentioned_users, tags} = Utils.make_content_html(draft) mentioned_ap_ids = @@ -257,22 +257,22 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags} end - defp to_and_cc(draft) do + defp to_and_cc(%__MODULE__{} = draft) do {to, cc} = Utils.get_to_and_cc(draft) %__MODULE__{draft | to: to, cc: cc} end - defp context(draft) do + defp context(%__MODULE__{} = draft) do context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation) %__MODULE__{draft | context: context} end - defp sensitive(draft) do + defp sensitive(%__MODULE__{} = draft) do sensitive = draft.params[:sensitive] %__MODULE__{draft | sensitive: sensitive} end - defp language(draft) do + defp language(%__MODULE__{} = draft) do language = with language <- draft.params[:language], true <- good_locale_code?(language) do @@ -284,7 +284,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | language: language} end - defp object(draft) do + defp object(%__MODULE__{} = draft) do emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji) # Sometimes people create posts with subject containing emoji, @@ -328,12 +328,12 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | object: object} end - defp preview?(draft) do + defp preview?(%__MODULE__{} = draft) do preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview]) %__MODULE__{draft | preview?: preview?} end - defp changes(draft) do + defp changes(%__MODULE__{} = draft) do direct? = draft.visibility == "direct" additional = %{"cc" => draft.cc, "directMessage" => direct?} @@ -359,7 +359,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp with_valid(%{valid?: true} = draft, func), do: func.(draft) defp with_valid(draft, _func), do: draft - defp add_error(draft, message) do + defp add_error(%__MODULE__{} = draft, message) do %__MODULE__{draft | valid?: false, errors: [message | draft.errors]} end From 9040434886526e082bf675e46e8cadb3f64e7fe9 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 16:40:19 +0100 Subject: [PATCH 007/106] Elixir 1.19: Fix typing violations in ActivityPubTest --- test/pleroma/web/activity_pub/activity_pub_test.exs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 37ec87a9b..13146619a 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1524,8 +1524,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do %{test_file: test_file} end - test "strips / from filename", %{test_file: file} do - file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"} + test "strips / from filename", %{test_file: %Plug.Upload{} = file} do + file = %{file | filename: "../../../../../nested/bad.jpg"} {:ok, %Object{} = object} = ActivityPub.upload(file) [%{"href" => href}] = object.data["url"] assert Regex.match?(~r"/bad.jpg$", href) @@ -1786,10 +1786,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, list} = Pleroma.List.create(%{title: "foo"}, user) {:ok, list} = Pleroma.List.follow(list, member) - {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) + {:ok, %Activity{} = activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) activity = Repo.preload(activity, :bookmark) - activity = %Activity{activity | thread_muted?: !!activity.thread_muted?} + activity = %{activity | thread_muted?: !!activity.thread_muted?} assert ActivityPub.fetch_activities([], %{user: user}) == [activity] end @@ -1989,7 +1989,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert User.following?(follower, old_user) assert User.following?(follower_move_opted_out, old_user) - assert {:ok, activity} = ActivityPub.move(old_user, new_user) + assert {:ok, %Activity{} = activity} = ActivityPub.move(old_user, new_user) assert %Activity{ actor: ^old_ap_id, @@ -2021,7 +2021,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert User.following?(follower_move_opted_out, old_user) refute User.following?(follower_move_opted_out, new_user) - activity = %Activity{activity | object: nil} + activity = %{activity | object: nil} assert [%Notification{activity: ^activity}] = Notification.for_user(follower) From 349d858fdade27c7a016a55ac69a435dadb59d2c Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 16:42:26 +0100 Subject: [PATCH 008/106] Elixir 1.19: Fix typing violation in MediaControllerTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Plug.Upload is expected on struct update: %Plug.Upload{image | filename: "../../../../../nested/file.jpg"} but got type: dynamic() where "image" was given the type: # type: dynamic() # from: test/pleroma/web/mastodon_api/controllers/media_controller_test.exs:132:42 %{conn: conn, image: image} when defining the variable "image", you must also pattern match on "%Plug.Upload{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 133 │ image = %Plug.Upload{ │ ~ │ └─ test/pleroma/web/mastodon_api/controllers/media_controller_test.exs:133:15: Pleroma.Web.MastodonAPI.MediaControllerTest."test Upload media Do not allow nested filename"/1 --- .../web/mastodon_api/controllers/media_controller_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index ae86078d7..fc083fd0e 100644 --- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -129,8 +129,8 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do assert :ok == File.rm(Path.absname("test/tmp/large_binary.data")) end - test "Do not allow nested filename", %{conn: conn, image: image} do - image = %Plug.Upload{ + test "Do not allow nested filename", %{conn: conn, image: %Plug.Upload{} = image} do + image = %{ image | filename: "../../../../../nested/file.jpg" } From f3d9880f07e70f4e83e25e13aab537e2ea3c89ba Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 16:44:37 +0100 Subject: [PATCH 009/106] Elixir 1.19: Fix typing violation in RepoTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Pleroma.Web.OAuth.Token is expected on struct update: %Pleroma.Web.OAuth.Token{Pleroma.Factory.insert(:oauth_token) | user: user} but got type: dynamic() you must assign "Pleroma.Factory.insert(:oauth_token)" to variable and pattern match on "%Pleroma.Web.OAuth.Token{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 27 │ token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user} │ ~ │ └─ test/pleroma/repo_test.exs:27:15: Pleroma.RepoTest."test get_assoc/2 get assoc from preloaded data"/1 --- test/pleroma/repo_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/pleroma/repo_test.exs b/test/pleroma/repo_test.exs index 9c0f5d028..721175bda 100644 --- a/test/pleroma/repo_test.exs +++ b/test/pleroma/repo_test.exs @@ -24,7 +24,8 @@ defmodule Pleroma.RepoTest do describe "get_assoc/2" do test "get assoc from preloaded data" do user = %User{name: "Agent Smith"} - token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user} + %Pleroma.Web.OAuth.Token{} = token = insert(:oauth_token) + token = %{token | user: user} assert Repo.get_assoc(token, :user) == {:ok, user} end From d1ce6f9833036aacc585963046043f1a5c4f5b14 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 16:48:55 +0100 Subject: [PATCH 010/106] Elixir 1.19: Fix typing violation in MarkerTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: a struct for Pleroma.Marker is expected on struct update: %Pleroma.Marker{refresh_record(marker) | unread_count: 2} but got type: dynamic() where "marker" was given the type: # type: dynamic() # from: test/pleroma/marker_test.exs:35:14 marker = Pleroma.Factory.insert(:marker, user: user) you must assign "refresh_record(marker)" to variable and pattern match on "%Pleroma.Marker{}". hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of: user = some_function() %User{user | name: "John Doe"} it is enough to write: %User{} = user = some_function() %{user | name: "John Doe"} typing violation found at: │ 43 │ ) == [%Marker{refresh_record(marker) | unread_count: 2}] │ ~ │ └─ test/pleroma/marker_test.exs:43:20: Pleroma.MarkerTest."test get_markers/2 returns user markers"/1 --- test/pleroma/marker_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/pleroma/marker_test.exs b/test/pleroma/marker_test.exs index 819dde9be..7f573ac7a 100644 --- a/test/pleroma/marker_test.exs +++ b/test/pleroma/marker_test.exs @@ -36,11 +36,12 @@ defmodule Pleroma.MarkerTest do insert(:notification, user: user, activity: insert(:note_activity)) insert(:notification, user: user, activity: insert(:note_activity)) insert(:marker, timeline: "home", user: user) + %Marker{} = refreshed_marker = refresh_record(marker) assert Marker.get_markers( user, ["notifications"] - ) == [%Marker{refresh_record(marker) | unread_count: 2}] + ) == [%{refreshed_marker | unread_count: 2}] end end From 5777d8e8d3c33bc70bb3cda6b7bb72592a310e16 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 17:01:36 +0100 Subject: [PATCH 011/106] Elixir 1.19: Fix deprecation warning when invoking ParallelCompiler warning: you must pass return_diagnostics: true when invoking Kernel.ParallelCompiler functions (elixir 1.19.5) lib/kernel/parallel_compiler.ex:324: Kernel.ParallelCompiler.spawn_workers/3 (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/html.ex:13: Pleroma.HTML.compile_scrubbers/0 (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/application.ex:48: Pleroma.Application.start/2 (kernel 10.5) application_master.erl:299: :application_master.start_it_old/4 warning: you must pass return_diagnostics: true when invoking Kernel.ParallelCompiler functions (elixir 1.19.5) lib/kernel/parallel_compiler.ex:324: Kernel.ParallelCompiler.spawn_workers/3 (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/application.ex:121: Pleroma.Application.load_custom_modules/0 (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/application.ex:60: Pleroma.Application.start/2 (kernel 10.5) application_master.erl:299: :application_master.start_it_old/4 --- lib/pleroma/utils.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex index 73001c987..61d122a47 100644 --- a/lib/pleroma/utils.ex +++ b/lib/pleroma/utils.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Utils do dir |> File.ls!() |> Enum.map(&Path.join(dir, &1)) - |> Kernel.ParallelCompiler.compile() + |> Kernel.ParallelCompiler.compile(return_diagnostics: true) end @doc """ From 94ea3dffd1c49fadc6a80c34127ea59c7fd87311 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 17:05:31 +0100 Subject: [PATCH 012/106] Elixir 1.19: Fix ConfigDBTest regex tests It is not possible match regexes anymore as this worked by accident previously. Instead, at least check that the sources of the regex (the regex itself) match. Notice the +1 difference in the regex Reference below. 1) test to_elixir_types/1 complex keyword with sigil (Pleroma.ConfigDBTest) test/pleroma/config_db_test.exs:460 Assertion with == failed code: assert ConfigDB.to_elixir_types([ %{"tuple" => [":federated_timeline_removal", []]}, %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, %{"tuple" => [":replace", []]} ]) == [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []] left: [federated_timeline_removal: [], reject: [%Regex{opts: [], re_pattern: {:re_pattern, 0, 0, 0, #Reference<0.230935836.591265794.259515>}, source: "comp[lL][aA][iI][nN]er"}], replace: []] right: [federated_timeline_removal: [], reject: [%Regex{opts: [], re_pattern: {:re_pattern, 0, 0, 0, #Reference<0.230935836.591265794.259516>}, source: "comp[lL][aA][iI][nN]er"}], replace: []] stacktrace: test/pleroma/config_db_test.exs:461: (test) --- test/pleroma/config_db_test.exs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/pleroma/config_db_test.exs b/test/pleroma/config_db_test.exs index d68e4e6fa..59beba48e 100644 --- a/test/pleroma/config_db_test.exs +++ b/test/pleroma/config_db_test.exs @@ -273,24 +273,24 @@ defmodule Pleroma.ConfigDBTest do end test "sigil" do - assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/ + assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source == ~r/comp[lL][aA][iI][nN]er/.source end test "link sigil" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/ + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source == ~r/https:\/\/example.com/.source end test "link sigil with um modifiers" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") == - ~r/https:\/\/example.com/um + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um").source == + ~r/https:\/\/example.com/um.source end test "link sigil with i modifier" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source == ~r/https:\/\/example.com/i.source end test "link sigil with s modifier" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source == ~r/https:\/\/example.com/s.source end test "raise if valid delimiter not found" do @@ -460,11 +460,11 @@ defmodule Pleroma.ConfigDBTest do test "complex keyword with sigil" do assert ConfigDB.to_elixir_types([ %{"tuple" => [":federated_timeline_removal", []]}, - %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, + %{"tuple" => [":reject", [~r/comp[lL][aA][iI][nN]er/.source]]}, %{"tuple" => [":replace", []]} ]) == [ federated_timeline_removal: [], - reject: [~r/comp[lL][aA][iI][nN]er/], + reject: [~r/comp[lL][aA][iI][nN]er/.source], replace: [] ] end From cbacc0f146eaad98eb31574142a076658ae98f3f Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 17:13:10 +0100 Subject: [PATCH 013/106] Elixir 1.19: Fix MRFTest regex tests It is no longer possible to match regexes. Instead at least match that the sources of the regexes (regexes themselves) are the same. Notice the +1 Reference number below. 2) test subdomain_match/2 wildcard domains with one subdomain (Pleroma.Web.ActivityPub.MRFTest) test/pleroma/web/activity_pub/mrf_test.exs:36 Assertion with == failed code: assert regexes == [~r/^(.*\.)*unsafe.tld$/i] left: [%Regex{opts: [:caseless], re_pattern: {:re_pattern, 1, 0, 0, #Reference<0.378940835.3277193222.129648>}, source: "^(.*\\.)*unsafe.tld$"}] right: [%Regex{opts: [:caseless], re_pattern: {:re_pattern, 1, 0, 0, #Reference<0.378940835.3277193222.129649>}, source: "^(.*\\.)*unsafe.tld$"}] stacktrace: test/pleroma/web/activity_pub/mrf_test.exs:39: (test) --- test/pleroma/web/activity_pub/mrf_test.exs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 25548e3da..6656a01b8 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -11,17 +11,21 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do alias Pleroma.Web.ActivityPub.MRF test "subdomains_regex/1" do - assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [ - ~r/^unsafe.tld$/i, - ~r/^(.*\.)*unsafe.tld$/i + regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) + matchable_regexes = Enum.map(regexes, fn r -> r.source end) + + assert matchable_regexes == [ + ~r/^unsafe.tld$/i.source, + ~r/^(.*\.)*unsafe.tld$/i.source ] end describe "subdomain_match/2" do test "common domains" do regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"]) + matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i] + assert matchable_regexes == [~r/^unsafe.tld$/i.source, ~r/^unsafe2.tld$/i.source] assert MRF.subdomain_match?(regexes, "unsafe.tld") assert MRF.subdomain_match?(regexes, "unsafe2.tld") @@ -31,8 +35,9 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do test "wildcard domains with one subdomain" do regexes = MRF.subdomains_regex(["*.unsafe.tld"]) + matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert regexes == [~r/^(.*\.)*unsafe.tld$/i] + assert matchable_regexes == [~r/^(.*\.)*unsafe.tld$/i.source] assert MRF.subdomain_match?(regexes, "unsafe.tld") assert MRF.subdomain_match?(regexes, "sub.unsafe.tld") @@ -42,8 +47,9 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do test "wildcard domains with two subdomains" do regexes = MRF.subdomains_regex(["*.unsafe.tld"]) + matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert regexes == [~r/^(.*\.)*unsafe.tld$/i] + assert matchable_regexes == [~r/^(.*\.)*unsafe.tld$/i.source] assert MRF.subdomain_match?(regexes, "unsafe.tld") assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld") @@ -53,8 +59,9 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do test "matches are case-insensitive" do regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"]) + matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i] + assert matchable_regexes == [~r/^UnSafe.TLD$/i.source, ~r/^UnSAFE2.Tld$/i.source] assert MRF.subdomain_match?(regexes, "UNSAFE.TLD") assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD") From 3c7cd8b043237fb0ece9c314b73e335dfd28ffcb Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 17:28:06 +0100 Subject: [PATCH 014/106] Elixir 1.19: Fix Mastodon StatusControllerTest DateTime difference --- .../web/mastodon_api/controllers/status_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index 298e92366..8477ae03f 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -3068,7 +3068,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> json_response_and_validate_schema(:ok) {:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at) - assert DateTime.diff(expires_at, a_expires_at) == 0 + assert DateTime.diff(DateTime.truncate(expires_at, :second), DateTime.truncate(a_expires_at, :second)) == 0 %{conn: conn} = oauth_access(["read:statuses"]) From dd2f0a32d8a0ab918cf840b937158e5b6d9418b3 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Fri, 9 Jan 2026 20:16:30 +0100 Subject: [PATCH 015/106] lint --- lib/pleroma/mfa/changeset.ex | 7 ++++--- lib/pleroma/web/api_spec/cast_and_validate.ex | 9 ++++++++- lib/pleroma/web/common_api/activity_draft.ex | 10 +++++++--- test/pleroma/config_db_test.exs | 12 ++++++++---- test/pleroma/web/activity_pub/activity_pub_test.exs | 3 ++- .../controllers/status_controller_test.exs | 6 +++++- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/pleroma/mfa/changeset.ex b/lib/pleroma/mfa/changeset.ex index 890cb2193..2045c3a7c 100644 --- a/lib/pleroma/mfa/changeset.ex +++ b/lib/pleroma/mfa/changeset.ex @@ -8,7 +8,8 @@ defmodule Pleroma.MFA.Changeset do alias Pleroma.User def disable(%Ecto.Changeset{} = changeset, force \\ false) do - %Settings{} = settings = + %Settings{} = + settings = changeset |> Ecto.Changeset.apply_changes() |> MFA.fetch_settings() @@ -26,7 +27,7 @@ defmodule Pleroma.MFA.Changeset do end def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do - totp_settings = %Settings.TOTP{%Settings.TOTP{} = settings.totp | confirmed: true} + totp_settings = %Settings.TOTP{(%Settings.TOTP{} = settings.totp) | confirmed: true} user |> put_change(%Settings{settings | totp: totp_settings, enabled: true}) @@ -46,7 +47,7 @@ defmodule Pleroma.MFA.Changeset do def cast_backup_codes(%User{} = user, codes) do user |> put_change(%Settings{ - %Settings{} = user.multi_factor_authentication_settings + (%Settings{} = user.multi_factor_authentication_settings) | backup_codes: codes }) end diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index 57ea8e9b3..95bd4d9cf 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -106,7 +106,14 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts) end - defp cast_and_validate(spec, operation, %Conn{} = conn, content_type, false = _strict, cast_opts) do + defp cast_and_validate( + spec, + operation, + %Conn{} = conn, + content_type, + false = _strict, + cast_opts + ) do case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do {:ok, conn} -> {:ok, conn} diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index c5959276a..6c84ef656 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -136,7 +136,8 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: ""}} = draft), do: draft - defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do + defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft) + when is_binary(id) do # If a post was deleted all its activities (except the newly added Delete) are purged too, # thus lookup by Create db ID will yield nil just as if it never existed in the first place. # @@ -166,13 +167,16 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end end - defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do + defp in_reply_to( + %__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft + ) do %__MODULE__{draft | in_reply_to: in_reply_to} end defp in_reply_to(draft), do: draft - defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do + defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft) + when not_empty_string(id) do case Activity.get_by_id_with_object(id) do %Activity{} = activity -> %__MODULE__{draft | quote_post: activity} diff --git a/test/pleroma/config_db_test.exs b/test/pleroma/config_db_test.exs index 59beba48e..98b56a146 100644 --- a/test/pleroma/config_db_test.exs +++ b/test/pleroma/config_db_test.exs @@ -273,11 +273,13 @@ defmodule Pleroma.ConfigDBTest do end test "sigil" do - assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source == ~r/comp[lL][aA][iI][nN]er/.source + assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source == + ~r/comp[lL][aA][iI][nN]er/.source end test "link sigil" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source == ~r/https:\/\/example.com/.source + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source == + ~r/https:\/\/example.com/.source end test "link sigil with um modifiers" do @@ -286,11 +288,13 @@ defmodule Pleroma.ConfigDBTest do end test "link sigil with i modifier" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source == ~r/https:\/\/example.com/i.source + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source == + ~r/https:\/\/example.com/i.source end test "link sigil with s modifier" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source == ~r/https:\/\/example.com/s.source + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source == + ~r/https:\/\/example.com/s.source end test "raise if valid delimiter not found" do diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 13146619a..9aafc41a5 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1786,7 +1786,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, list} = Pleroma.List.create(%{title: "foo"}, user) {:ok, list} = Pleroma.List.follow(list, member) - {:ok, %Activity{} = activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) + {:ok, %Activity{} = activity} = + CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) activity = Repo.preload(activity, :bookmark) activity = %{activity | thread_muted?: !!activity.thread_muted?} diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index 8477ae03f..11e96a6ac 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -3068,7 +3068,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> json_response_and_validate_schema(:ok) {:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at) - assert DateTime.diff(DateTime.truncate(expires_at, :second), DateTime.truncate(a_expires_at, :second)) == 0 + + assert DateTime.diff( + DateTime.truncate(expires_at, :second), + DateTime.truncate(a_expires_at, :second) + ) == 0 %{conn: conn} = oauth_access(["read:statuses"]) From 926cad3a76972835d340a7cabb0e90b8c01f51eb Mon Sep 17 00:00:00 2001 From: Phantasm Date: Sat, 10 Jan 2026 16:42:09 +0100 Subject: [PATCH 016/106] Elixir 1.19: Refactor Logger configuration to what Elixir 1.15+ expects Elixir 1.19 started emitting warnings about our use of: config :logger, :console and config :logger, backends: [] which got split in 1.15 to :default_handler, :default_formatter, and LoggerBackends.add(backend) respectively. On Pleroma startup it is now necessary to add the additional logger backends at runtime (done with configure_logger() in application.ex). New config key :pleroma, :logger_backends: [] has replaced the older backends array. The logger_backends package is now used to facilitate usage of other logger backends besides the default one (:logger_std_h from OTP). Warnings were added to Pleroma startup to warn about the usage of :logger, backends and additional logic was introduced to make addition of ExSyslogger to admin's configuration less dumb. Ref: https://hexdocs.pm/logger/1.15.0/Logger.html#module-backends-and-backwards-compatibility --- config/config.exs | 9 +++++---- config/dev.exs | 2 +- config/prod.exs | 3 +-- config/test.exs | 6 +++--- docs/configuration/cheatsheet.md | 25 +++++++++++++------------ lib/pleroma/application.ex | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/config/config.exs b/config/config.exs index 5bf2c5c2e..08311146e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -132,11 +132,12 @@ config :pleroma, Pleroma.Web.Endpoint, "SameSite=Lax" ] -# Configures Elixir's Logger -config :logger, backends: [:console] +# config :logger, :console has been changed in 1.15+ and split into +# :default_handler and :default_formatter. It started emitting warnings in 1.19. +# See https://hexdocs.pm/logger/1.19.0/Logger.html#module-backends-and-backwards-compatibility +config :logger, :default_handler, level: :debug -config :logger, :console, - level: :debug, +config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:actor, :path, :type, :user] diff --git a/config/dev.exs b/config/dev.exs index 14cf4a6dc..e86bdb7fd 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -36,7 +36,7 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local # different ports. # Do not include timestamps in development logs -config :logger, Logger.Backends.Console, format: "$metadata[$level] $message\n" +config :logger, :default_formatter, format: "$metadata[$level] $message\n" # Set a higher stacktrace during development. Avoid configuring such # in production as building large stacktraces may be expensive. diff --git a/config/prod.exs b/config/prod.exs index 2d252bf02..1dfbbe937 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -20,8 +20,7 @@ config :pleroma, Pleroma.Web.Endpoint, config :phoenix, serve_endpoints: true # Do not print debug messages in production -config :logger, Logger.Backends.Console, level: :info -config :logger, :console, level: :info +config :logger, :default_handler, level: :info config :logger, :ex_syslogger, level: :info # ## SSL Support diff --git a/config/test.exs b/config/test.exs index 9652b7d4b..bac5e4df3 100644 --- a/config/test.exs +++ b/config/test.exs @@ -15,9 +15,9 @@ config :pleroma, Pleroma.Captcha, method: Pleroma.Captcha.Mock # Print only warnings and errors during test -config :logger, :console, - level: :warning, - format: "\n[$level] $message\n" +config :logger, :default_handler, level: :warning + +config :logger, :default_formatter, format: "\n[$level] $message\n" config :pleroma, :auth, oauth_consumer_strategies: [] diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 9efa1c8b3..2ab2652b8 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -858,12 +858,14 @@ Web Push Notifications configuration. You can use the mix task `mix web_push.gen * ``private_key``: VAPID private key ## :logger -* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog +* Logging to console/stdout is done by default, use `{ExSyslogger, :ex_syslogger}` to log to syslog An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed: ```elixir -config :logger, - backends: [{ExSyslogger, :ex_syslogger}] +config :pleroma, + logger_backends: [{ExSyslogger, :ex_syslogger}] + +config :logger, default_handler: false config :logger, :ex_syslogger, level: :warning @@ -871,8 +873,8 @@ config :logger, :ex_syslogger, Another example, keeping console output and adding the pid to syslog output: ```elixir -config :logger, - backends: [:console, {ExSyslogger, :ex_syslogger}] +config :pleroma, + logger_backends: [{ExSyslogger, :ex_syslogger}] config :logger, :ex_syslogger, level: :warning, @@ -883,23 +885,22 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s An example of logging info to local syslog, but debug to console: ```elixir -config :logger, - backends: [ {ExSyslogger, :ex_syslogger}, :console ], - level: :info +config :pleroma, + logger_backends: [ {ExSyslogger, :ex_syslogger}] config :logger, :ex_syslogger, level: :info, ident: "pleroma", format: "$metadata[$level] $message" -config :logger, :console, - level: :debug, +config :logger, :default_handler, + level: :debug + +config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:request_id] ``` - - ## Database options ### RUM indexing for full text search diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 8e1c5de0d..7355a501a 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -43,6 +43,7 @@ defmodule Pleroma.Application do # every time the application is restarted, so we disable module # conflicts at runtime Code.compiler_options(ignore_module_conflict: true) + configure_logger() Pleroma.Telemetry.Logger.attach() Config.Holder.save_default() Pleroma.HTML.compile_scrubbers() @@ -113,6 +114,37 @@ defmodule Pleroma.Application do Supervisor.start_link(children, opts) end + def configure_logger do + if Application.get_env(:logger, :backends) do + Logger.warning( + "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger_backends: [...]' instead." + ) + end + + Config.get([:logger_backends], []) + |> Enum.each(fn backend -> + backend = backend_to_logger(backend) + + case LoggerBackends.add(backend) do + {:ok, _} -> + Logger.debug("Successfully added logger backend: #{inspect(backend)}") + + {:error, reason} -> + Logger.error("Failed to add logger backend #{inspect(backend)}: #{inspect(reason)}") + end + end) + end + + defp backend_to_logger({:ex_syslogger = backend, name}) do + Logger.warning( + "Configuration {:#{backend}, :#{name}} is incorrect. Use {ExSyslogger, :#{name}} instead!" + ) + + {ExSyslogger, name} + end + + defp backend_to_logger(backend), do: backend + def load_custom_modules do dir = Config.get([:modules, :runtime_dir]) From 0393f0f92dcd68f7615b98139a35055d272e242a Mon Sep 17 00:00:00 2001 From: Phantasm Date: Mon, 12 Jan 2026 18:03:24 +0100 Subject: [PATCH 017/106] Elixir 1.19: Move Logger backends under a specific config key --- docs/configuration/cheatsheet.md | 12 ++++++------ lib/pleroma/application.ex | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 2ab2652b8..01d81f6ca 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -862,8 +862,8 @@ Web Push Notifications configuration. You can use the mix task `mix web_push.gen An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed: ```elixir -config :pleroma, - logger_backends: [{ExSyslogger, :ex_syslogger}] +config :pleroma, :logger, + backends: [{ExSyslogger, :ex_syslogger}] config :logger, default_handler: false @@ -873,8 +873,8 @@ config :logger, :ex_syslogger, Another example, keeping console output and adding the pid to syslog output: ```elixir -config :pleroma, - logger_backends: [{ExSyslogger, :ex_syslogger}] +config :pleroma, :logger, + backends: [{ExSyslogger, :ex_syslogger}] config :logger, :ex_syslogger, level: :warning, @@ -885,8 +885,8 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s An example of logging info to local syslog, but debug to console: ```elixir -config :pleroma, - logger_backends: [ {ExSyslogger, :ex_syslogger}] +config :pleroma, :logger, + backends: [ {ExSyslogger, :ex_syslogger}] config :logger, :ex_syslogger, level: :info, diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 7355a501a..ce5b4f8d6 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -117,11 +117,11 @@ defmodule Pleroma.Application do def configure_logger do if Application.get_env(:logger, :backends) do Logger.warning( - "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger_backends: [...]' instead." + "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." ) end - Config.get([:logger_backends], []) + Config.get([:logger, :backends], []) |> Enum.each(fn backend -> backend = backend_to_logger(backend) From 3790e6786cce376cbabf2588beed27d0daf9c55c Mon Sep 17 00:00:00 2001 From: Phantasm Date: Mon, 12 Jan 2026 18:21:46 +0100 Subject: [PATCH 018/106] Only warn about bad clear_config usage if warned usage is met --- test/support/helpers.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/support/helpers.ex b/test/support/helpers.ex index 7fa6c31a4..259180e8f 100644 --- a/test/support/helpers.ex +++ b/test/support/helpers.ex @@ -73,7 +73,8 @@ defmodule Pleroma.Tests.Helpers do defmacro clear_config(config_path, temp_setting) do # NOTE: `clear_config([section, key], value)` != `clear_config([section], key: value)` (!) # Displaying a warning to prevent unintentional clearing of all but one keys in section - if Keyword.keyword?(temp_setting) and length(temp_setting) == 1 do + if is_list(config_path) and length(config_path) == 1 and Keyword.keyword?(temp_setting) and + Keyword.keys(temp_setting) == [:key] do Logger.warning( "Please change `clear_config([section], key: value)` to `clear_config([section, key], value)`" ) From eee54a9226b1b4642bb2a71daa0921ba0022ce48 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Mon, 12 Jan 2026 18:24:11 +0100 Subject: [PATCH 019/106] Elixir 1.19: Add tests for new Logger configuration --- test/pleroma/application_test.exs | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/pleroma/application_test.exs diff --git a/test/pleroma/application_test.exs b/test/pleroma/application_test.exs new file mode 100644 index 000000000..bff44f4ff --- /dev/null +++ b/test/pleroma/application_test.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2026 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ApplicationTest do + use Pleroma.DataCase + + import ExUnit.CaptureLog + + # clear_config/2 can only manipulate env under :pleroma. + describe "config :logger, backends: []" do + setup do + Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) + + on_exit(fn -> + Application.delete_env(:logger, :backends) end) + end + + test "warns on deprecated syntax" do + assert capture_log(fn -> Pleroma.Application.configure_logger() end) =~ + "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." + end + end + + describe "config :pleroma, :logger, backends: [{:ex_syslogger, :ex_syslogger}]" do + setup do + clear_config([:logger, :backends], [{:ex_syslogger, :ex_syslogger}]) + end + + test "is handled" do + log = capture_log(fn -> Pleroma.Application.configure_logger() end) + + assert log =~ "Configuration {:ex_syslogger, :ex_syslogger} is incorrect. Use {ExSyslogger, :ex_syslogger} instead!" + assert log =~ "Successfully added logger backend: {ExSyslogger, :ex_syslogger}" + end + end +end From 3ebf4b105629b137f620de7b71167463ad3ca6be Mon Sep 17 00:00:00 2001 From: Phantasm Date: Mon, 12 Jan 2026 19:31:15 +0100 Subject: [PATCH 020/106] Elixir 1.19: Move deprecated logger syntax check to DeprecationWarnings --- lib/pleroma/application.ex | 6 ------ lib/pleroma/config/deprecation_warnings.ex | 17 ++++++++++++++++- test/pleroma/application_test.exs | 15 --------------- .../config/deprecation_warnings_test.exs | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index ce5b4f8d6..1e80c6249 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -115,12 +115,6 @@ defmodule Pleroma.Application do end def configure_logger do - if Application.get_env(:logger, :backends) do - Logger.warning( - "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." - ) - end - Config.get([:logger, :backends], []) |> Enum.each(fn backend -> backend = backend_to_logger(backend) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 58d164dc7..8dabd728f 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -218,7 +218,8 @@ defmodule Pleroma.Config.DeprecationWarnings do check_quarantined_instances_tuples(), check_transparency_exclusions_tuples(), check_simple_policy_tuples(), - check_exiftool_filter() + check_exiftool_filter(), + check_deprecated_logger_config() ] |> Enum.reduce(:ok, fn :ok, :ok -> :ok @@ -415,4 +416,18 @@ defmodule Pleroma.Config.DeprecationWarnings do :ok end end + + @spec check_deprecated_logger_config() :: :ok | :error + def check_deprecated_logger_config() do + if Application.get_env(:logger, :backends) do + Logger.warning( + "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. " <> + "Use 'config :pleroma, :logger, backends: [...]' instead." + ) + + :error + else + :ok + end + end end diff --git a/test/pleroma/application_test.exs b/test/pleroma/application_test.exs index bff44f4ff..afe628732 100644 --- a/test/pleroma/application_test.exs +++ b/test/pleroma/application_test.exs @@ -7,21 +7,6 @@ defmodule Pleroma.ApplicationTest do import ExUnit.CaptureLog - # clear_config/2 can only manipulate env under :pleroma. - describe "config :logger, backends: []" do - setup do - Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) - - on_exit(fn -> - Application.delete_env(:logger, :backends) end) - end - - test "warns on deprecated syntax" do - assert capture_log(fn -> Pleroma.Application.configure_logger() end) =~ - "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." - end - end - describe "config :pleroma, :logger, backends: [{:ex_syslogger, :ex_syslogger}]" do setup do clear_config([:logger, :backends], [{:ex_syslogger, :ex_syslogger}]) diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index fca2324ff..1491f4ea3 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -388,4 +388,19 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end) =~ "Your config is using the old namespace for the Shoutbox configuration." end + + describe "check_deprecated_logger_config" do + setup do + Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) + + on_exit(fn -> + Application.delete_env(:logger, :backends) end) + end + + test "warns on deprecated syntax" do + assert capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() end) =~ + "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." + end + end + end From 39409a784df0a726aee3a6e48769344d83cd81f2 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Mon, 12 Jan 2026 19:35:12 +0100 Subject: [PATCH 021/106] Elixir 1.19: Warn on adding :console to Logger backends Logger.Backend.Console still exists, but it is unused in our configuration. Instead logging to console is done from Logger's "default_handler". --- lib/pleroma/application.ex | 28 +++++++++++++++++++--------- test/pleroma/application_test.exs | 11 +++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 1e80c6249..0e8723251 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -117,27 +117,37 @@ defmodule Pleroma.Application do def configure_logger do Config.get([:logger, :backends], []) |> Enum.each(fn backend -> - backend = backend_to_logger(backend) + case backend_to_logger(backend) do + {:ok, logger} -> + add_logger(logger) - case LoggerBackends.add(backend) do - {:ok, _} -> - Logger.debug("Successfully added logger backend: #{inspect(backend)}") - - {:error, reason} -> - Logger.error("Failed to add logger backend #{inspect(backend)}: #{inspect(reason)}") + {:error, :console_backend} -> + Logger.warning(":console is no longer considered a backend and is enabled by default") end end) end + defp add_logger(backend) do + case LoggerBackends.add(backend) do + {:ok, _} -> + Logger.debug("Successfully added logger backend: #{inspect(backend)}") + + {:error, reason} -> + Logger.error("Failed to add logger backend #{inspect(backend)}: #{inspect(reason)}") + end + end + + defp backend_to_logger(:console), do: {:error, :console_backend} + defp backend_to_logger({:ex_syslogger = backend, name}) do Logger.warning( "Configuration {:#{backend}, :#{name}} is incorrect. Use {ExSyslogger, :#{name}} instead!" ) - {ExSyslogger, name} + {:ok, {ExSyslogger, name}} end - defp backend_to_logger(backend), do: backend + defp backend_to_logger(backend), do: {:ok, backend} def load_custom_modules do dir = Config.get([:modules, :runtime_dir]) diff --git a/test/pleroma/application_test.exs b/test/pleroma/application_test.exs index afe628732..48f87818f 100644 --- a/test/pleroma/application_test.exs +++ b/test/pleroma/application_test.exs @@ -19,4 +19,15 @@ defmodule Pleroma.ApplicationTest do assert log =~ "Successfully added logger backend: {ExSyslogger, :ex_syslogger}" end end + + describe "config :pleroma, :logger, :backends: [:console]" do + setup do + clear_config([:logger, :backends], [:console]) + end + + test "emits a warning" do + assert capture_log(fn -> Pleroma.Application.configure_logger() end) =~ + ":console is no longer considered a backend and is enabled by default" + end + end end From 6744a86fe3a70834aac0c248f5b1c4d0680278fe Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 00:36:15 +0100 Subject: [PATCH 022/106] Elixir 1.19: Disable logging with colors in test environment Can break some tests capture logs and then match on an expected text. Since color codes end up being in the captured log. --- config/test.exs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/test.exs b/config/test.exs index bac5e4df3..5b7f3b1ed 100644 --- a/config/test.exs +++ b/config/test.exs @@ -17,7 +17,11 @@ config :pleroma, Pleroma.Captcha, # Print only warnings and errors during test config :logger, :default_handler, level: :warning -config :logger, :default_formatter, format: "\n[$level] $message\n" +config :logger, :default_formatter, + format: "\n[$level] $message\n", + colors: [ + enabled: false + ] config :pleroma, :auth, oauth_consumer_strategies: [] From 17a563aeb41e4f2ec18788cf5a58a1a928044a6e Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 00:39:48 +0100 Subject: [PATCH 023/106] Elixir 1.19: Inject @compile autoload opt to MRF policies Descriptions of MRF policies are gathered at compile-time and use Pleroma.Docs.Generator.list_behaviour_implementations func, which depends on modules implementing the queried behaviour to be loaded. Otherwise descriptions for MRFs can be missing from the AdminAPI route. Note: @behaviour cannot be injected in the __using__ macro. It causes a loop in the compiler which leads to memory exhaustion. Hence why @behaviour is still defined separately in the MRFs. --- .../web/activity_pub/mrf/activity_expiration_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex | 6 +++--- lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex | 4 +++- .../web/activity_pub/mrf/anti_mention_spam_policy.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/drop_policy.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/emoji_policy.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex | 6 ++++-- lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex | 3 +++ .../web/activity_pub/mrf/force_bot_unlisted_policy.ex | 7 +++++-- lib/pleroma/web/activity_pub/mrf/force_mention.ex | 3 ++- .../web/activity_pub/mrf/force_mentions_in_content.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex | 7 ++++--- lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/keyword_policy.ex | 6 ++++-- .../web/activity_pub/mrf/media_proxy_warming_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/mention_policy.ex | 3 ++- lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/no_op_policy.ex | 2 ++ .../web/activity_pub/mrf/no_placeholder_text_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/normalize_markup.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex | 5 ++++- lib/pleroma/web/activity_pub/mrf/object_age_policy.ex | 6 ++++-- lib/pleroma/web/activity_pub/mrf/policy.ex | 8 ++++++++ lib/pleroma/web/activity_pub/mrf/quiet_reply.ex | 4 +++- .../web/activity_pub/mrf/quote_to_link_tag_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/reject_non_public.ex | 3 ++- lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex | 6 ++++-- lib/pleroma/web/activity_pub/mrf/subchain_policy.ex | 4 +++- lib/pleroma/web/activity_pub/mrf/tag_policy.ex | 7 +++++-- .../web/activity_pub/mrf/user_allow_list_policy.ex | 6 ++++-- lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex | 3 ++- 34 files changed, 105 insertions(+), 34 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex index 88f6ca028..41abe17c0 100644 --- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do @moduledoc "Adds expiration to all local Create activities" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def filter(activity) do activity = diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index 8ea61aec2..990ff31c9 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -3,11 +3,11 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do + @moduledoc "Prevent followbots from following with a bit of heuristic" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy alias Pleroma.User - @moduledoc "Prevent followbots from following with a bit of heuristic" - - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy # XXX: this should become User.normalize_by_ap_id() or similar, really. defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id) diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex index 2be6d8df4..5c3b66e37 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex @@ -3,9 +3,11 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy require Logger diff --git a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex index 1d76a307b..c6f5781b3 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex @@ -3,11 +3,13 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config alias Pleroma.User require Pleroma.Constants - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp user_has_posted?(%User{} = u), do: u.note_count > 0 diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index cf07db7f3..4aa1968f7 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -3,10 +3,12 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do - require Logger @moduledoc "Drop and log everything received" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Logger + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def filter(activity) do Logger.debug("REJECTING #{inspect(activity)}") diff --git a/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex index 1de5280d9..cc5fd49bf 100644 --- a/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex @@ -3,6 +3,8 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Pleroma.Constants alias Pleroma.Object.Updater @@ -10,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do @moduledoc "Reject or force-unlisted emojis with certain URLs or names" - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp config_remove_url do Pleroma.Config.get([:mrf_emoji, :remove_url], []) diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index f5983c8a7..6e43e746c 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -3,11 +3,13 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do - alias Pleroma.Object - @moduledoc "Ensure a re: is prepended on replies to a post with a Subject" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Object + + use Pleroma.Web.ActivityPub.MRF.Policy + @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) def history_awareness, do: :auto diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex index 480a03ef6..4612b2cc5 100644 --- a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex @@ -4,12 +4,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.CommonAPI require Logger + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def filter(activity) do with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]), diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex index 3b3251dc3..58d02f433 100644 --- a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex @@ -3,9 +3,12 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do - alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy @moduledoc "Remove bot posts from federated timeline" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + alias Pleroma.User + + use Pleroma.Web.ActivityPub.MRF.Policy require Pleroma.Constants diff --git a/lib/pleroma/web/activity_pub/mrf/force_mention.ex b/lib/pleroma/web/activity_pub/mrf/force_mention.ex index 4ea23540d..62aff36bc 100644 --- a/lib/pleroma/web/activity_pub/mrf/force_mention.ex +++ b/lib/pleroma/web/activity_pub/mrf/force_mention.ex @@ -3,13 +3,14 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ForceMention do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy require Pleroma.Constants alias Pleroma.Config alias Pleroma.Object alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp get_author(url) do with %Object{data: %{"actor" => actor}} <- Object.normalize(url, fetch: false), diff --git a/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex index caae365e5..e7c3f2d88 100644 --- a/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex +++ b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex @@ -3,13 +3,15 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Pleroma.Constants alias Pleroma.Formatter alias Pleroma.Object alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy @impl true def history_awareness, do: :auto diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex index 72f2274ed..a3fae3bae 100644 --- a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex @@ -3,6 +3,8 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Pleroma.Constants alias Pleroma.Config @@ -14,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists. """ - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy @impl true def history_awareness, do: :manual diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index 3a80d0a69..a84a7b0c8 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -3,13 +3,14 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do + @moduledoc "Block activities with too much mentions (configurable)" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.User require Pleroma.Constants - @moduledoc "Block activities with too much mentions (configurable)" - - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp delist_activity(activity, threshold) when threshold > 0 do follower_collection = User.get_cached_by_ap_id(activity["actor"]).follower_address diff --git a/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex b/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex index 51dcd3918..17879b4bd 100644 --- a/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do @moduledoc "Force a quote line into the message content." @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + defp build_inline_quote(template, url) do quote_line = String.replace(template, "{url}", "#{url}") diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 6ba6fd509..44b79596e 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -3,13 +3,15 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do + @moduledoc "Reject or Word-Replace activities with a keyword or regex" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Pleroma.Constants alias Pleroma.Web.ActivityPub.MRF.Utils - @moduledoc "Reject or Word-Replace activities with a keyword or regex" - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp string_matches?(string, pattern) when is_binary(pattern) do String.contains?(string, pattern) diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index 43c2c0449..07046a666 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do @moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.HTTP alias Pleroma.Web.MediaProxy diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex index f7bff121f..8e108a0ae 100644 --- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex @@ -4,9 +4,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do @moduledoc "Block activities which mention a user" - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def filter(%{"type" => "Create"} = activity) do reject_actors = Pleroma.Config.get([:mrf_mention, :actors], []) diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex index 08dd39878..8283fd504 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do @moduledoc "Filter local activities which have no content" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Web.Endpoint @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex index 64a5872bc..3adbb598a 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do @moduledoc "Does nothing (lets the messages go through unmodified)" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def filter(activity) do {:ok, activity} diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex index c6f239a5e..2a10c7e97 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do @moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def history_awareness, do: :auto diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index 91855ef84..de0f5b619 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -4,9 +4,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do @moduledoc "Scrub configured hypertext markup" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.HTML - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy @impl true def history_awareness, do: :auto diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index 52aaf05aa..7e3ec3de5 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -38,6 +38,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do - `unlist`: Unlist all detected NSFW content? Default: `false` - `reject`: Reject all detected NSFW content (takes precedence)? Default: `false` """ + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config alias Pleroma.Constants alias Pleroma.HTTP @@ -46,7 +48,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do require Logger require Pleroma.Constants - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + @policy :mrf_nsfw_api def build_request_url(url) do diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index 34905fc21..00f4cf84b 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -3,13 +3,15 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do + @moduledoc "Filter activities depending on their age" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config alias Pleroma.User require Pleroma.Constants - @moduledoc "Filter activities depending on their age" - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp check_date(%{"object" => %{"published" => published}} = activity) do with %DateTime{} = now <- DateTime.utc_now(), diff --git a/lib/pleroma/web/activity_pub/mrf/policy.ex b/lib/pleroma/web/activity_pub/mrf/policy.ex index 08bcac08a..b9a693c9e 100644 --- a/lib/pleroma/web/activity_pub/mrf/policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/policy.ex @@ -15,4 +15,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do } @callback history_awareness() :: :auto | :manual @optional_callbacks config_description: 0, history_awareness: 0, id_filter: 1 + + defmacro __using__(_) do + quote do + # Otherwise Pleroma.Docs.Generator.list_behaviour_implementations fails + # since the Elixir compiler doesn't automatically load the module since v1.19. + @compile {:autoload, true} + end + end end diff --git a/lib/pleroma/web/activity_pub/mrf/quiet_reply.ex b/lib/pleroma/web/activity_pub/mrf/quiet_reply.ex index b3eb0b390..b47e9edb9 100644 --- a/lib/pleroma/web/activity_pub/mrf/quiet_reply.ex +++ b/lib/pleroma/web/activity_pub/mrf/quiet_reply.ex @@ -6,11 +6,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuietReply do @moduledoc """ QuietReply alters the scope of activities from local users when replying by enforcing them to be "Unlisted" or "Quiet Public". This delivers the activity to all the expected recipients and instances, but it will not be published in the Federated / The Whole Known Network timelines. It will still be published to the Home timelines of the user's followers and visible to anyone who opens the thread. """ + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Pleroma.Constants alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy @impl true def history_awareness, do: :auto diff --git a/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex index 2a17b6761..e1ad17585 100644 --- a/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do @moduledoc "Force a Link tag for posts quoting another post. (may break outgoing federation of quote posts with older Pleroma versions)" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes require Pleroma.Constants diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index 9d4a7a405..7d113dbf6 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -4,11 +4,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do @moduledoc "Rejects non-public (followers-only, direct) activities" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy alias Pleroma.Config alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy require Pleroma.Constants diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex index 8422832dd..f9f8d2298 100644 --- a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -2,6 +2,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do @moduledoc "Drop remote reports if they don't contain enough information." @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index a97e8db7b..7045fe90f 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do @moduledoc "Filter activities depending on their origin instance" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config alias Pleroma.FollowingRelationship alias Pleroma.User diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex index 54f0e6bc1..f1e99aead 100644 --- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex @@ -3,12 +3,14 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do + @moduledoc "Detect new emojis by their shortcode and steals them" + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + require Logger alias Pleroma.Config - @moduledoc "Detect new emojis by their shortcode and steals them" - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], []) diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex index 97acca7e8..1fe7b00e9 100644 --- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex @@ -3,12 +3,14 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config alias Pleroma.Web.ActivityPub.MRF require Logger - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy defp lookup_subchain(actor) do with matches <- Config.get([:mrf_subchain, :match_actor]), diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex index c236a5a99..df5594808 100644 --- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex @@ -3,8 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do - alias Pleroma.User - @behaviour Pleroma.Web.ActivityPub.MRF.Policy @moduledoc """ Apply policies based on user tags @@ -18,6 +16,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do - `mrf_tag:disable-remote-subscription`: Reject non-local follow requests - `mrf_tag:disable-any-subscription`: Reject any follow requests """ + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + alias Pleroma.User + + use Pleroma.Web.ActivityPub.MRF.Policy require Pleroma.Constants diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index 10cc0e09d..a04ca90bd 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -3,11 +3,13 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do - alias Pleroma.Config - @moduledoc "Accept-list of users from specified instances" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + alias Pleroma.Config + + use Pleroma.Web.ActivityPub.MRF.Policy + defp filter_by_list(activity, []), do: {:ok, activity} defp filter_by_list(%{"actor" => actor} = activity, allow_list) do diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index 5671e4cf3..ecba3a2c6 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -4,9 +4,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do @moduledoc "Filter activities which belong to certain activity vocabularies" - @behaviour Pleroma.Web.ActivityPub.MRF.Policy + use Pleroma.Web.ActivityPub.MRF.Policy + @impl true def filter(%{"type" => "Undo", "object" => object} = activity) do with {:ok, _} <- filter(object) do From 2a23eacee899c69f55d8a3cb6ed8291b56c7187b Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 14:58:32 +0100 Subject: [PATCH 024/106] Elixir 1.19: Only match once on structs Second match is not needed and a simple Map update is recommended by the compiler --- lib/pleroma/marker.ex | 2 +- lib/pleroma/web/api_spec/render_error.ex | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex index 4f645240e..fab24d183 100644 --- a/lib/pleroma/marker.ex +++ b/lib/pleroma/marker.ex @@ -78,7 +78,7 @@ defmodule Pleroma.Marker do defp get_marker(user, timeline) do case Repo.find_resource(get_query(user, timeline)) do - {:ok, %__MODULE__{} = marker} -> %__MODULE__{marker | user: user} + {:ok, %__MODULE__{} = marker} -> %{marker | user: user} _ -> %__MODULE__{timeline: timeline, user_id: user.id} end end diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex index acf510774..2ba76f250 100644 --- a/lib/pleroma/web/api_spec/render_error.ex +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -18,10 +18,10 @@ defmodule Pleroma.Web.ApiSpec.RenderError do errors = Enum.map(errors, fn %OpenApiSpex.Cast.Error{name: nil, reason: :invalid_enum} = err -> - %OpenApiSpex.Cast.Error{err | name: err.value} + %{err | name: err.value} %OpenApiSpex.Cast.Error{name: nil} = err -> - %OpenApiSpex.Cast.Error{err | name: List.last(err.path)} + %{err | name: List.last(err.path)} err -> err From c455e0225581e3ebe43e9ef51a7ee393d8e87bdd Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 15:21:41 +0100 Subject: [PATCH 025/106] Elixir 1.19: Add non-test Elixir files in test/ to mix.exs filter This is a new filter introduced in 1.19. --- mix.exs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mix.exs b/mix.exs index dc9fa31f5..60064e846 100644 --- a/mix.exs +++ b/mix.exs @@ -15,6 +15,13 @@ defmodule Pleroma.Mixfile do aliases: aliases(), deps: deps(), test_coverage: [tool: :covertool, summary: true], + test_ignore_filters: [ + "test/credo/check/consistency/file_location.ex", + "test/fixtures/config/temp.exported_from_db.secret.exs", + "test/fixtures/config/temp.secret.exs", + "test/fixtures/modules/good_mrf.ex", + "test/fixtures/modules/runtime_module.ex" + ], # Docs name: "Pleroma", homepage_url: "https://pleroma.social/", From a4c929c527353f99cd11aeb9c08a44f212632f12 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 20:44:26 +0100 Subject: [PATCH 026/106] Lint --- lib/pleroma/config/deprecation_warnings.ex | 4 ++-- lib/pleroma/web/activity_pub/mrf/keyword_policy.ex | 1 - test/pleroma/application_test.exs | 6 ++++-- test/pleroma/config/deprecation_warnings_test.exs | 10 ++++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 8dabd728f..8f56a3771 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -418,11 +418,11 @@ defmodule Pleroma.Config.DeprecationWarnings do end @spec check_deprecated_logger_config() :: :ok | :error - def check_deprecated_logger_config() do + def check_deprecated_logger_config do if Application.get_env(:logger, :backends) do Logger.warning( "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. " <> - "Use 'config :pleroma, :logger, backends: [...]' instead." + "Use 'config :pleroma, :logger, backends: [...]' instead." ) :error diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 44b79596e..33f32e781 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do alias Pleroma.Web.ActivityPub.MRF.Utils - use Pleroma.Web.ActivityPub.MRF.Policy defp string_matches?(string, pattern) when is_binary(pattern) do diff --git a/test/pleroma/application_test.exs b/test/pleroma/application_test.exs index 48f87818f..b4a29fc9b 100644 --- a/test/pleroma/application_test.exs +++ b/test/pleroma/application_test.exs @@ -15,7 +15,9 @@ defmodule Pleroma.ApplicationTest do test "is handled" do log = capture_log(fn -> Pleroma.Application.configure_logger() end) - assert log =~ "Configuration {:ex_syslogger, :ex_syslogger} is incorrect. Use {ExSyslogger, :ex_syslogger} instead!" + assert log =~ + "Configuration {:ex_syslogger, :ex_syslogger} is incorrect. Use {ExSyslogger, :ex_syslogger} instead!" + assert log =~ "Successfully added logger backend: {ExSyslogger, :ex_syslogger}" end end @@ -27,7 +29,7 @@ defmodule Pleroma.ApplicationTest do test "emits a warning" do assert capture_log(fn -> Pleroma.Application.configure_logger() end) =~ - ":console is no longer considered a backend and is enabled by default" + ":console is no longer considered a backend and is enabled by default" end end end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 1491f4ea3..5383e2bce 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -394,13 +394,15 @@ defmodule Pleroma.Config.DeprecationWarningsTest do Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) on_exit(fn -> - Application.delete_env(:logger, :backends) end) + Application.delete_env(:logger, :backends) + end) end test "warns on deprecated syntax" do - assert capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() end) =~ - "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." + assert capture_log(fn -> + Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() + end) =~ + "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." end end - end From a4137243b62795b6299248ccf11f80ccda059d56 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 21:07:06 +0100 Subject: [PATCH 027/106] Elixir 1.19: Reword Logger backends deprecation warning --- lib/pleroma/config/deprecation_warnings.ex | 15 +++++++++++---- test/pleroma/config/deprecation_warnings_test.exs | 12 +++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 8f56a3771..8b8628862 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -420,10 +420,17 @@ defmodule Pleroma.Config.DeprecationWarnings do @spec check_deprecated_logger_config() :: :ok | :error def check_deprecated_logger_config do if Application.get_env(:logger, :backends) do - Logger.warning( - "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. " <> - "Use 'config :pleroma, :logger, backends: [...]' instead." - ) + Logger.warning(""" + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring backends of Elixir's logger. + `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use: + `config :pleroma, :logger, backends: [...]` + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: :console is no longer considered a backend and is used by default, you can disable it using: + `config :logger, :default_handler: false` + """) :error else diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 5383e2bce..a9a71be36 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -402,7 +402,17 @@ defmodule Pleroma.Config.DeprecationWarningsTest do assert capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() end) =~ - "'config :logger, backends: [...]' is deprecated syntax due to changes in Elixir. Use 'config :pleroma, :logger, backends: [...]' instead." + """ + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring backends of Elixir's logger. + `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use: + `config :pleroma, :logger, backends: [...]` + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: :console is no longer considered a backend and is used by default, you can disable it using: + `config :logger, :default_handler: false` + """ end end end From 6ffd25682d4a0e6cebe9b0c8789c76bb9e8a312d Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 21:18:08 +0100 Subject: [PATCH 028/106] Elixir 1.19: Add deprecation warning for :logger, :console configs --- lib/pleroma/config/deprecation_warnings.ex | 30 +++++++++++--- .../config/deprecation_warnings_test.exs | 41 +++++++++++++------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 8b8628862..f38fb7f2f 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -419,22 +419,42 @@ defmodule Pleroma.Config.DeprecationWarnings do @spec check_deprecated_logger_config() :: :ok | :error def check_deprecated_logger_config do - if Application.get_env(:logger, :backends) do + backend = if Application.get_env(:logger, :backends) do Logger.warning(""" !!!DEPRECATION WARNING!!! Your configuration is using deprecated syntax for configuring backends of Elixir's logger. `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. Please update your configuration at your earliest convenience to use: - `config :pleroma, :logger, backends: [...]` + `config :logger, backends: [...]` Pleroma's default configuration will be updated in the next release which WILL override your settings. - Note: :console is no longer considered a backend and is used by default, you can disable it using: + Note: `:console` is no longer considered a backend and is used by default, you can disable it using: `config :logger, :default_handler: false` """) - :error + true else - :ok + false end + + console = if Application.get_env(:logger, :console) do + Logger.warning(""" + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring logging to console. + `config :logger, :console` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use + `config :logger, default_handler` and `config :logger, :default_formatter`. + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: `:default_handler` is used only for the `level` setting. All other configurations go under + `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility + """) + + true + else + false + end + + if backend or console, do: :error, else: :ok end end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index a9a71be36..f5b9d76d3 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -392,27 +392,44 @@ defmodule Pleroma.Config.DeprecationWarningsTest do describe "check_deprecated_logger_config" do setup do Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) + Application.put_env(:logger, :console, level: :warning) on_exit(fn -> Application.delete_env(:logger, :backends) + Application.delete_env(:logger, :console) end) end test "warns on deprecated syntax" do - assert capture_log(fn -> + log = capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() - end) =~ - """ - !!!DEPRECATION WARNING!!! - Your configuration is using deprecated syntax for configuring backends of Elixir's logger. - `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. - Please update your configuration at your earliest convenience to use: - `config :pleroma, :logger, backends: [...]` - Pleroma's default configuration will be updated in the next release which WILL override your settings. + end) - Note: :console is no longer considered a backend and is used by default, you can disable it using: - `config :logger, :default_handler: false` - """ + assert log =~ + """ + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring backends of Elixir's logger. + `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use: + `config :logger, backends: [...]` + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: `:console` is no longer considered a backend and is used by default, you can disable it using: + `config :logger, :default_handler: false` + """ + + assert log =~ + """ + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring logging to console. + `config :logger, :console` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use + `config :logger, default_handler` and `config :logger, :default_formatter`. + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: `:default_handler` is used only for the `level` setting. All other configurations go under + `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility + """ end end end From a5bd4e0d9230fe9aaad4722681ed12aec0cb5301 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 21:31:07 +0100 Subject: [PATCH 029/106] Partially revert "Elixir 1.19: Refactor Logger configuration to what Elixir 1.15+ expects" This partially reverts commit fc35204cc1d718ba7815cc5e1b08746202b4f7dc. --- config/config.exs | 9 ++++----- config/dev.exs | 2 +- config/prod.exs | 3 ++- config/test.exs | 10 +++------- docs/configuration/cheatsheet.md | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/config/config.exs b/config/config.exs index 08311146e..5bf2c5c2e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -132,12 +132,11 @@ config :pleroma, Pleroma.Web.Endpoint, "SameSite=Lax" ] -# config :logger, :console has been changed in 1.15+ and split into -# :default_handler and :default_formatter. It started emitting warnings in 1.19. -# See https://hexdocs.pm/logger/1.19.0/Logger.html#module-backends-and-backwards-compatibility -config :logger, :default_handler, level: :debug +# Configures Elixir's Logger +config :logger, backends: [:console] -config :logger, :default_formatter, +config :logger, :console, + level: :debug, format: "\n$time $metadata[$level] $message\n", metadata: [:actor, :path, :type, :user] diff --git a/config/dev.exs b/config/dev.exs index e86bdb7fd..14cf4a6dc 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -36,7 +36,7 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local # different ports. # Do not include timestamps in development logs -config :logger, :default_formatter, format: "$metadata[$level] $message\n" +config :logger, Logger.Backends.Console, format: "$metadata[$level] $message\n" # Set a higher stacktrace during development. Avoid configuring such # in production as building large stacktraces may be expensive. diff --git a/config/prod.exs b/config/prod.exs index 1dfbbe937..2d252bf02 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -20,7 +20,8 @@ config :pleroma, Pleroma.Web.Endpoint, config :phoenix, serve_endpoints: true # Do not print debug messages in production -config :logger, :default_handler, level: :info +config :logger, Logger.Backends.Console, level: :info +config :logger, :console, level: :info config :logger, :ex_syslogger, level: :info # ## SSL Support diff --git a/config/test.exs b/config/test.exs index 5b7f3b1ed..9652b7d4b 100644 --- a/config/test.exs +++ b/config/test.exs @@ -15,13 +15,9 @@ config :pleroma, Pleroma.Captcha, method: Pleroma.Captcha.Mock # Print only warnings and errors during test -config :logger, :default_handler, level: :warning - -config :logger, :default_formatter, - format: "\n[$level] $message\n", - colors: [ - enabled: false - ] +config :logger, :console, + level: :warning, + format: "\n[$level] $message\n" config :pleroma, :auth, oauth_consumer_strategies: [] diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 01d81f6ca..b0a7087a7 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -886,7 +886,7 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s An example of logging info to local syslog, but debug to console: ```elixir config :pleroma, :logger, - backends: [ {ExSyslogger, :ex_syslogger}] + backends: [{ExSyslogger, :ex_syslogger}] config :logger, :ex_syslogger, level: :info, From aed0eb33331568b38c14b83d753c671b8336cb38 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 13 Jan 2026 23:28:57 +0100 Subject: [PATCH 030/106] Elixir 1.19: Warn on old config of :console Logger and conflicts --- lib/pleroma/config/deprecation_warnings.ex | 122 +++++++++++++----- .../config/deprecation_warnings_test.exs | 47 +++---- 2 files changed, 115 insertions(+), 54 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index f38fb7f2f..ee3965b64 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -419,42 +419,102 @@ defmodule Pleroma.Config.DeprecationWarnings do @spec check_deprecated_logger_config() :: :ok | :error def check_deprecated_logger_config do - backend = if Application.get_env(:logger, :backends) do - Logger.warning(""" - !!!DEPRECATION WARNING!!! - Your configuration is using deprecated syntax for configuring backends of Elixir's logger. - `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. - Please update your configuration at your earliest convenience to use: - `config :logger, backends: [...]` - Pleroma's default configuration will be updated in the next release which WILL override your settings. + mix_env = Pleroma.Config.get(:env) + config_file_name = "config/#{mix_env}.secret.exs" + config_file = File.read(config_file_name) - Note: `:console` is no longer considered a backend and is used by default, you can disable it using: - `config :logger, :default_handler: false` - """) + config_file = + case config_file do + {:ok, contents} -> + String.split(contents, "\n", trim: true) + |> Enum.reject(fn string -> String.starts_with?(string, "#") end) + |> Enum.join("\n") - true - else - false - end + _ -> + false + end - console = if Application.get_env(:logger, :console) do - Logger.warning(""" - !!!DEPRECATION WARNING!!! - Your configuration is using deprecated syntax for configuring logging to console. - `config :logger, :console` is deprecated syntax due to changes in Elixir. - Please update your configuration at your earliest convenience to use - `config :logger, default_handler` and `config :logger, :default_formatter`. - Pleroma's default configuration will be updated in the next release which WILL override your settings. + declared_deprecated_backend = + if is_binary(config_file) do + String.contains?(config_file, "config :logger, backends") or + String.contains?(config_file, "config :logger,\n backends") + else + nil + end - Note: `:default_handler` is used only for the `level` setting. All other configurations go under - `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility - """) + declared_deprecated_console = + if is_binary(config_file) do + String.contains?(config_file, "config :logger, :console") or + String.contains?(config_file, "config :logger,\n console") + else + nil + end - true - else - false - end + # We can't use Application.get_env/3, because by default default_formatter is populated + declared_new_console = + if is_binary(config_file) do + String.contains?(config_file, "config :logger, :default_handler") or + String.contains?(config_file, "config :logger,\n :default_handler") or + String.contains?(config_file, "config :logger, :default_formatter") or + String.contains?(config_file, "config :logger,\n :default_formatter") + else + nil + end - if backend or console, do: :error, else: :ok + use_new_backend_config = Pleroma.Config.get([:logger, :backends]) + + backend = + if declared_deprecated_backend do + Logger.warning(""" + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring backends of Elixir's logger. + `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use: + `config :logger, backends: [...]` + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: `:console` is no longer considered a backend and is used by default, you can disable it using: + `config :logger, :default_handler: false` + """) + + true + else + false + end + + console = + if declared_deprecated_console do + Logger.warning(""" + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring logging to console. + `config :logger, :console` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use + `config :logger, default_handler` and `config :logger, :default_formatter`. + Pleroma's default configuration will be updated in the next release which WILL override your settings. + + Note: `:default_handler` is used only for the `level` setting. All other configurations go under + `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility + """) + + true + else + false + end + + conflict = + if (!is_nil(use_new_backend_config) and declared_deprecated_backend) or + (declared_new_console and declared_deprecated_console) do + Logger.warning(""" + !!!CONFIGURATION CONFLICT!!! + Pleroma has detected that both the deprecated way of configuring Logger and the new way are defined in your configuration. + Delete the deprecated configuration syntax. + """) + + true + else + false + end + + if backend or console or conflict, do: :error, else: :ok end end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index f5b9d76d3..cf2c6b4d2 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -401,35 +401,36 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end test "warns on deprecated syntax" do - log = capture_log(fn -> - Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() - end) + log = + capture_log(fn -> + Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() + end) assert log =~ - """ - !!!DEPRECATION WARNING!!! - Your configuration is using deprecated syntax for configuring backends of Elixir's logger. - `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. - Please update your configuration at your earliest convenience to use: - `config :logger, backends: [...]` - Pleroma's default configuration will be updated in the next release which WILL override your settings. + """ + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring backends of Elixir's logger. + `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use: + `config :logger, backends: [...]` + Pleroma's default configuration will be updated in the next release which WILL override your settings. - Note: `:console` is no longer considered a backend and is used by default, you can disable it using: - `config :logger, :default_handler: false` - """ + Note: `:console` is no longer considered a backend and is used by default, you can disable it using: + `config :logger, :default_handler: false` + """ assert log =~ - """ - !!!DEPRECATION WARNING!!! - Your configuration is using deprecated syntax for configuring logging to console. - `config :logger, :console` is deprecated syntax due to changes in Elixir. - Please update your configuration at your earliest convenience to use - `config :logger, default_handler` and `config :logger, :default_formatter`. - Pleroma's default configuration will be updated in the next release which WILL override your settings. + """ + !!!DEPRECATION WARNING!!! + Your configuration is using deprecated syntax for configuring logging to console. + `config :logger, :console` is deprecated syntax due to changes in Elixir. + Please update your configuration at your earliest convenience to use + `config :logger, default_handler` and `config :logger, :default_formatter`. + Pleroma's default configuration will be updated in the next release which WILL override your settings. - Note: `:default_handler` is used only for the `level` setting. All other configurations go under - `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility - """ + Note: `:default_handler` is used only for the `level` setting. All other configurations go under + `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility + """ end end end From bd0c5aa748d226d109aeec7462ac9e6ba8d12f75 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 14 Jan 2026 11:09:57 +0100 Subject: [PATCH 031/106] Elixir 1.19: Move default configs to the new Logger configuration, again Trying to do a two-release migration proved to be very hacky and ugly. Instead this is preparation for moving defaults first and then merging the old configuration left in admin's config file at runtime while still emitting a warning. This hopefully will be a lot nicer and less complicated. --- config/config.exs | 5 ++--- config/dev.exs | 2 +- config/prod.exs | 3 +-- config/test.exs | 5 ++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/config/config.exs b/config/config.exs index 5bf2c5c2e..069c0c7c0 100644 --- a/config/config.exs +++ b/config/config.exs @@ -133,10 +133,9 @@ config :pleroma, Pleroma.Web.Endpoint, ] # Configures Elixir's Logger -config :logger, backends: [:console] +config :logger, :default_handler, level: :debug -config :logger, :console, - level: :debug, +config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:actor, :path, :type, :user] diff --git a/config/dev.exs b/config/dev.exs index 14cf4a6dc..e86bdb7fd 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -36,7 +36,7 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local # different ports. # Do not include timestamps in development logs -config :logger, Logger.Backends.Console, format: "$metadata[$level] $message\n" +config :logger, :default_formatter, format: "$metadata[$level] $message\n" # Set a higher stacktrace during development. Avoid configuring such # in production as building large stacktraces may be expensive. diff --git a/config/prod.exs b/config/prod.exs index 2d252bf02..1dfbbe937 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -20,8 +20,7 @@ config :pleroma, Pleroma.Web.Endpoint, config :phoenix, serve_endpoints: true # Do not print debug messages in production -config :logger, Logger.Backends.Console, level: :info -config :logger, :console, level: :info +config :logger, :default_handler, level: :info config :logger, :ex_syslogger, level: :info # ## SSL Support diff --git a/config/test.exs b/config/test.exs index 9652b7d4b..083759327 100644 --- a/config/test.exs +++ b/config/test.exs @@ -15,9 +15,8 @@ config :pleroma, Pleroma.Captcha, method: Pleroma.Captcha.Mock # Print only warnings and errors during test -config :logger, :console, - level: :warning, - format: "\n[$level] $message\n" +config :logger, :default_handler, level: :warning +config :logger, :default_formatter, format: "\n[$level] $message\n" config :pleroma, :auth, oauth_consumer_strategies: [] From 9450fba83d10fca74e013708bd12406520ef9788 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 14 Jan 2026 13:45:20 +0100 Subject: [PATCH 032/106] Elixir 1.19: Disable colors for logging in test env Color ANSI codes end up in the capture_log output and break existing tests. --- config/test.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/test.exs b/config/test.exs index 083759327..ed5283a26 100644 --- a/config/test.exs +++ b/config/test.exs @@ -16,7 +16,10 @@ config :pleroma, Pleroma.Captcha, # Print only warnings and errors during test config :logger, :default_handler, level: :warning -config :logger, :default_formatter, format: "\n[$level] $message\n" + +config :logger, :default_formatter, + format: "\n[$level] $message\n", + colors: [enabled: false] config :pleroma, :auth, oauth_consumer_strategies: [] From 0f262bb2f3238bf8d999d22e3a4a45bcbc7fc301 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 14 Jan 2026 13:49:31 +0100 Subject: [PATCH 033/106] Elixir 1.19: Replace ugly old logger config checks with config merging Side note: Going up in log levels doesn't work properly. - debug->info works - info->debug does not work --- lib/pleroma/application.ex | 3 + lib/pleroma/config/deprecation_warnings.ex | 88 ++++++------------- .../config/deprecation_warnings_test.exs | 20 ++++- 3 files changed, 51 insertions(+), 60 deletions(-) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 0e8723251..78073b430 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -132,6 +132,9 @@ defmodule Pleroma.Application do {:ok, _} -> Logger.debug("Successfully added logger backend: #{inspect(backend)}") + {:error, :already_present} -> + :ok + {:error, reason} -> Logger.error("Failed to add logger backend #{inspect(backend)}: #{inspect(reason)}") end diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index ee3965b64..4b6281e01 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -20,6 +20,9 @@ defmodule Pleroma.Config.DeprecationWarnings do "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} ] + @logger_config_knobs [:level, :translator_inspect_opts] + @logger_formatter_config_knows [:colors, :format, :metadata, :truncate, :utc_log] + def check_exiftool_filter do filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) @@ -417,54 +420,33 @@ defmodule Pleroma.Config.DeprecationWarnings do end end + defp merge_deprecated_logger_config(config) do + handler_config = Enum.filter(config, fn {k, _} -> k in @logger_config_knobs end) + formatter_config = Enum.filter(config, fn {k, _} -> k in @logger_formatter_config_knows end) + formatter = Logger.default_formatter(formatter_config) + + Logger.debug(""" + Reconfiguring console Logger with deprecated configuration syntax. + Handler configuration: + #{inspect(handler_config)} + + Formatter: + #{inspect(formatter)} + """) + + Logger.configure(handler_config) + :logger.update_handler_config(:default, :formatter, formatter) + end + @spec check_deprecated_logger_config() :: :ok | :error def check_deprecated_logger_config do - mix_env = Pleroma.Config.get(:env) - config_file_name = "config/#{mix_env}.secret.exs" - config_file = File.read(config_file_name) - - config_file = - case config_file do - {:ok, contents} -> - String.split(contents, "\n", trim: true) - |> Enum.reject(fn string -> String.starts_with?(string, "#") end) - |> Enum.join("\n") - - _ -> - false - end - - declared_deprecated_backend = - if is_binary(config_file) do - String.contains?(config_file, "config :logger, backends") or - String.contains?(config_file, "config :logger,\n backends") - else - nil - end - - declared_deprecated_console = - if is_binary(config_file) do - String.contains?(config_file, "config :logger, :console") or - String.contains?(config_file, "config :logger,\n console") - else - nil - end - - # We can't use Application.get_env/3, because by default default_formatter is populated - declared_new_console = - if is_binary(config_file) do - String.contains?(config_file, "config :logger, :default_handler") or - String.contains?(config_file, "config :logger,\n :default_handler") or - String.contains?(config_file, "config :logger, :default_formatter") or - String.contains?(config_file, "config :logger,\n :default_formatter") - else - nil - end - - use_new_backend_config = Pleroma.Config.get([:logger, :backends]) + backends_config = Application.get_env(:logger, :backends) + console_config = Application.get_env(:logger, :console) + # Note: No need to merge the old backends config since it still works. + # And new configuration will just add new Logger backends. backend = - if declared_deprecated_backend do + if backends_config do Logger.warning(""" !!!DEPRECATION WARNING!!! Your configuration is using deprecated syntax for configuring backends of Elixir's logger. @@ -483,7 +465,7 @@ defmodule Pleroma.Config.DeprecationWarnings do end console = - if declared_deprecated_console do + if console_config do Logger.warning(""" !!!DEPRECATION WARNING!!! Your configuration is using deprecated syntax for configuring logging to console. @@ -496,25 +478,13 @@ defmodule Pleroma.Config.DeprecationWarnings do `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility """) - true - else - false - end - - conflict = - if (!is_nil(use_new_backend_config) and declared_deprecated_backend) or - (declared_new_console and declared_deprecated_console) do - Logger.warning(""" - !!!CONFIGURATION CONFLICT!!! - Pleroma has detected that both the deprecated way of configuring Logger and the new way are defined in your configuration. - Delete the deprecated configuration syntax. - """) + merge_deprecated_logger_config(console_config) true else false end - if backend or console or conflict, do: :error, else: :ok + if backend or console, do: :error, else: :ok end end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index cf2c6b4d2..787f9d2fd 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -392,11 +392,12 @@ defmodule Pleroma.Config.DeprecationWarningsTest do describe "check_deprecated_logger_config" do setup do Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) - Application.put_env(:logger, :console, level: :warning) + Application.put_env(:logger, :console, level: :debug) on_exit(fn -> Application.delete_env(:logger, :backends) Application.delete_env(:logger, :console) + :logger.update_handler_config(:default, :formatter, Logger.default_formatter()) end) end @@ -432,5 +433,22 @@ defmodule Pleroma.Config.DeprecationWarningsTest do `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility """ end + + test "reconfigures logger" do + log = + capture_log(fn -> + Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() + end) + + # Formatter has inconsitent color formatting order + assert log =~ + """ + Reconfiguring console Logger with deprecated configuration syntax. + Handler configuration: + [level: :debug] + + Formatter: + """ + end end end From 02fc770e3638de20592fdaab115a041cda05f952 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 14 Jan 2026 15:02:51 +0100 Subject: [PATCH 034/106] Elixir 1.19: Improve DeprecationWarningsTest Logger reconfiguration test --- .../config/deprecation_warnings_test.exs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 787f9d2fd..a6ccf063c 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -11,6 +11,8 @@ defmodule Pleroma.Config.DeprecationWarningsTest do alias Pleroma.Config alias Pleroma.Config.DeprecationWarnings + require Logger + describe "filter exiftool" do test "gives warning when still used" do clear_config( @@ -392,11 +394,12 @@ defmodule Pleroma.Config.DeprecationWarningsTest do describe "check_deprecated_logger_config" do setup do Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) - Application.put_env(:logger, :console, level: :debug) + Application.put_env(:logger, :console, level: :info) on_exit(fn -> Application.delete_env(:logger, :backends) Application.delete_env(:logger, :console) + Logger.configure(level: :debug) :logger.update_handler_config(:default, :formatter, Logger.default_formatter()) end) end @@ -435,20 +438,18 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end test "reconfigures logger" do - log = + empty_log = capture_log(fn -> - Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config() + Logger.debug("This should not be logged") end) - # Formatter has inconsitent color formatting order - assert log =~ - """ - Reconfiguring console Logger with deprecated configuration syntax. - Handler configuration: - [level: :debug] + logged = + capture_log(fn -> + Logger.info("This should be logged") + end) - Formatter: - """ + assert empty_log =~ "" + assert logged =~ "This should be logged" end end end From 829a60ce9365ddc69655cc8e88f625dfa17c5e96 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 15 Jan 2026 16:03:44 +0100 Subject: [PATCH 035/106] Elixir 1.19 MRFTest: Replace matchable_regexes with regexes_match! func --- test/pleroma/web/activity_pub/mrf_test.exs | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 6656a01b8..401d4ebb3 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -10,22 +10,25 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do alias Pleroma.Web.ActivityPub.MRF + defp regexes_match!([],[]), do: true + + defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do + authority.source == checked.source and regexes_match!(authority_rest, checked_rest) + end + + defp regexes_match!(_, _), do: false + test "subdomains_regex/1" do regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) - matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert matchable_regexes == [ - ~r/^unsafe.tld$/i.source, - ~r/^(.*\.)*unsafe.tld$/i.source - ] + assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^(.*\.)*unsafe.tld$/i]) end describe "subdomain_match/2" do test "common domains" do regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"]) - matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert matchable_regexes == [~r/^unsafe.tld$/i.source, ~r/^unsafe2.tld$/i.source] + assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i]) assert MRF.subdomain_match?(regexes, "unsafe.tld") assert MRF.subdomain_match?(regexes, "unsafe2.tld") @@ -35,9 +38,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do test "wildcard domains with one subdomain" do regexes = MRF.subdomains_regex(["*.unsafe.tld"]) - matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert matchable_regexes == [~r/^(.*\.)*unsafe.tld$/i.source] + assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i]) assert MRF.subdomain_match?(regexes, "unsafe.tld") assert MRF.subdomain_match?(regexes, "sub.unsafe.tld") @@ -47,9 +49,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do test "wildcard domains with two subdomains" do regexes = MRF.subdomains_regex(["*.unsafe.tld"]) - matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert matchable_regexes == [~r/^(.*\.)*unsafe.tld$/i.source] + assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i]) assert MRF.subdomain_match?(regexes, "unsafe.tld") assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld") @@ -59,9 +60,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do test "matches are case-insensitive" do regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"]) - matchable_regexes = Enum.map(regexes, fn r -> r.source end) - assert matchable_regexes == [~r/^UnSafe.TLD$/i.source, ~r/^UnSAFE2.Tld$/i.source] + assert regexes_match!(regexes, [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i]) assert MRF.subdomain_match?(regexes, "UNSAFE.TLD") assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD") From ad7ab7741b1d426554765401c92857500f432c6c Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 15 Jan 2026 16:36:59 +0100 Subject: [PATCH 036/106] Elixir 1.19: Update dependencies in docs Elixir 1.15 support only OTP>=24 --- docs/installation/generic_dependencies.include | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include index 588a6c019..382f07a6c 100644 --- a/docs/installation/generic_dependencies.include +++ b/docs/installation/generic_dependencies.include @@ -1,8 +1,8 @@ ## Required dependencies * PostgreSQL >=11.0 -* Elixir >=1.15.0 <1.19 -* Erlang OTP >=23.0.0 (supported: <28) +* Elixir >=1.15.0, <1.20 +* Erlang OTP >=24.0.0 (supported: <29) * git * file / libmagic * gcc or clang From b75f8f7c9addbd73f88ac468473e23aef86b55fa Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 15 Jan 2026 16:56:01 +0100 Subject: [PATCH 037/106] Elixir 1.19: Added changelogs --- changelog.d/elixir-1-19.fix | 1 + changelog.d/logger-backends.change | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/elixir-1-19.fix create mode 100644 changelog.d/logger-backends.change diff --git a/changelog.d/elixir-1-19.fix b/changelog.d/elixir-1-19.fix new file mode 100644 index 000000000..53079b0f7 --- /dev/null +++ b/changelog.d/elixir-1-19.fix @@ -0,0 +1 @@ +Fixed Elixir 1.19 warnings diff --git a/changelog.d/logger-backends.change b/changelog.d/logger-backends.change new file mode 100644 index 000000000..5f044c2c7 --- /dev/null +++ b/changelog.d/logger-backends.change @@ -0,0 +1 @@ +Changed how Logger is configured due to changes in Elixir and added deprecation warnings for the old configuration format From 0ac1b65ba95b9bc3382494a84432bb8ecf088771 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 21 Jan 2026 18:39:15 +0100 Subject: [PATCH 038/106] Elixir 1.19: Fix improper deprecation warning wording --- lib/pleroma/config/deprecation_warnings.ex | 4 +--- test/pleroma/config/deprecation_warnings_test.exs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 4b6281e01..4f68c141c 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -452,8 +452,7 @@ defmodule Pleroma.Config.DeprecationWarnings do Your configuration is using deprecated syntax for configuring backends of Elixir's logger. `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. Please update your configuration at your earliest convenience to use: - `config :logger, backends: [...]` - Pleroma's default configuration will be updated in the next release which WILL override your settings. + `config :pleroma, :logger, backends: [...]` Note: `:console` is no longer considered a backend and is used by default, you can disable it using: `config :logger, :default_handler: false` @@ -472,7 +471,6 @@ defmodule Pleroma.Config.DeprecationWarnings do `config :logger, :console` is deprecated syntax due to changes in Elixir. Please update your configuration at your earliest convenience to use `config :logger, default_handler` and `config :logger, :default_formatter`. - Pleroma's default configuration will be updated in the next release which WILL override your settings. Note: `:default_handler` is used only for the `level` setting. All other configurations go under `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index a6ccf063c..221609da0 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -416,8 +416,7 @@ defmodule Pleroma.Config.DeprecationWarningsTest do Your configuration is using deprecated syntax for configuring backends of Elixir's logger. `config :logger, backends: [...]` is deprecated syntax due to changes in Elixir. Please update your configuration at your earliest convenience to use: - `config :logger, backends: [...]` - Pleroma's default configuration will be updated in the next release which WILL override your settings. + `config :pleroma, :logger, backends: [...]` Note: `:console` is no longer considered a backend and is used by default, you can disable it using: `config :logger, :default_handler: false` @@ -430,7 +429,6 @@ defmodule Pleroma.Config.DeprecationWarningsTest do `config :logger, :console` is deprecated syntax due to changes in Elixir. Please update your configuration at your earliest convenience to use `config :logger, default_handler` and `config :logger, :default_formatter`. - Pleroma's default configuration will be updated in the next release which WILL override your settings. Note: `:default_handler` is used only for the `level` setting. All other configurations go under `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility From 1743a837dd31e7756a7e3293a170234b2d94fc57 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 21 Jan 2026 20:02:26 +0100 Subject: [PATCH 039/106] Elixir 1.19: Set merged logger config correctly, remove log level test --- lib/pleroma/config/deprecation_warnings.ex | 29 +++++++++--------- .../config/deprecation_warnings_test.exs | 30 +++++-------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 4f68c141c..e77785483 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -20,8 +20,7 @@ defmodule Pleroma.Config.DeprecationWarnings do "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} ] - @logger_config_knobs [:level, :translator_inspect_opts] - @logger_formatter_config_knows [:colors, :format, :metadata, :truncate, :utc_log] + @logger_formatter_config_knobs [:colors, :format, :metadata, :truncate, :utc_log] def check_exiftool_filter do filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) @@ -421,21 +420,23 @@ defmodule Pleroma.Config.DeprecationWarnings do end defp merge_deprecated_logger_config(config) do - handler_config = Enum.filter(config, fn {k, _} -> k in @logger_config_knobs end) - formatter_config = Enum.filter(config, fn {k, _} -> k in @logger_formatter_config_knows end) + formatter_config = Enum.filter(config, fn {k, _} -> k in @logger_formatter_config_knobs end) formatter = Logger.default_formatter(formatter_config) + log_level = Keyword.get(config, :level, nil) - Logger.debug(""" - Reconfiguring console Logger with deprecated configuration syntax. - Handler configuration: - #{inspect(handler_config)} + if Config.get(:env) != :test do + Logger.debug(""" + Reconfiguring console Logger with deprecated configuration syntax. + Handler configuration: + log_level: #{inspect(log_level)} - Formatter: - #{inspect(formatter)} - """) + Formatter: + #{inspect(formatter)} + """) - Logger.configure(handler_config) - :logger.update_handler_config(:default, :formatter, formatter) + if log_level, do: :logger.update_handler_config(:default, :level, log_level) + :logger.update_handler_config(:default, :formatter, formatter) + end end @spec check_deprecated_logger_config() :: :ok | :error @@ -443,7 +444,7 @@ defmodule Pleroma.Config.DeprecationWarnings do backends_config = Application.get_env(:logger, :backends) console_config = Application.get_env(:logger, :console) - # Note: No need to merge the old backends config since it still works. + # NOTE: No need to merge the old backends config since it still works. # And new configuration will just add new Logger backends. backend = if backends_config do diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 221609da0..475a88c4d 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.DeprecationWarningsTest do - use ExUnit.Case + use ExUnit.Case, async: false use Pleroma.Tests.Helpers import ExUnit.CaptureLog @@ -393,14 +393,15 @@ defmodule Pleroma.Config.DeprecationWarningsTest do describe "check_deprecated_logger_config" do setup do - Application.put_env(:logger, :backends, [:console, {ExSyslogger, :ex_syslogger}]) - Application.put_env(:logger, :console, level: :info) + initial_console = Application.get_env(:logger, :console, nil) + initial_backends = Application.get_env(:logger, :backends, nil) + + Application.put_env(:logger, :console, level: :all) + Application.put_env(:logger, :backends, [:console]) on_exit(fn -> - Application.delete_env(:logger, :backends) - Application.delete_env(:logger, :console) - Logger.configure(level: :debug) - :logger.update_handler_config(:default, :formatter, Logger.default_formatter()) + if initial_console, do: Application.put_env(:logger, :console, initial_console), else: Application.delete_env(:logger, :console) + if initial_backends, do: Application.put_env(:logger, :backends, initial_backends), else: Application.delete_env(:logger, :backends) end) end @@ -434,20 +435,5 @@ defmodule Pleroma.Config.DeprecationWarningsTest do `:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility """ end - - test "reconfigures logger" do - empty_log = - capture_log(fn -> - Logger.debug("This should not be logged") - end) - - logged = - capture_log(fn -> - Logger.info("This should be logged") - end) - - assert empty_log =~ "" - assert logged =~ "This should be logged" - end end end From f44774d387c88ce6289ff691a4aa5662c0966964 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 21 Jan 2026 21:13:23 +0100 Subject: [PATCH 040/106] lint --- test/pleroma/config/deprecation_warnings_test.exs | 13 +++++++++++-- test/pleroma/web/activity_pub/mrf_test.exs | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 475a88c4d..72dd63100 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -400,8 +400,17 @@ defmodule Pleroma.Config.DeprecationWarningsTest do Application.put_env(:logger, :backends, [:console]) on_exit(fn -> - if initial_console, do: Application.put_env(:logger, :console, initial_console), else: Application.delete_env(:logger, :console) - if initial_backends, do: Application.put_env(:logger, :backends, initial_backends), else: Application.delete_env(:logger, :backends) + if initial_console do + Application.put_env(:logger, :console, initial_console) + else + Application.delete_env(:logger, :console) + end + + if initial_backends do + Application.put_env(:logger, :backends, initial_backends) + else + Application.delete_env(:logger, :backends) + end end) end diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 401d4ebb3..2d0f3b317 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -10,7 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do alias Pleroma.Web.ActivityPub.MRF - defp regexes_match!([],[]), do: true + defp regexes_match!([], []), do: true defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do authority.source == checked.source and regexes_match!(authority_rest, checked_rest) From a12f9777f857002318a6d6da17473bb6a52276b7 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 12 Feb 2026 23:46:08 +0100 Subject: [PATCH 041/106] Elixir 1.19: Bump Woodpecker test pipeline to 1.19 --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 4ceb1cab5..f6b06dc61 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -4,7 +4,7 @@ when: steps: test: - image: elixir:1.15-alpine + image: elixir:1.19-alpine environment: MIX_ENV: test DB_HOST: postgres From 01b139c6331977f5d622866127be9b4291cd4732 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Sun, 22 Feb 2026 21:50:28 +0100 Subject: [PATCH 042/106] Elixir 1.19: Add note to deprecation warnings test The Logger does not get reconfigured when running in the test env, the added and on_exit removed settings below should have no effect besides triggering the deprecation warnings. To make breakage even less possible, async is turned off for these tests. TransferTask test does almost the same with the Application env. --- test/pleroma/config/deprecation_warnings_test.exs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 72dd63100..b65b0e630 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -396,6 +396,9 @@ defmodule Pleroma.Config.DeprecationWarningsTest do initial_console = Application.get_env(:logger, :console, nil) initial_backends = Application.get_env(:logger, :backends, nil) + # NOTE: The Logger does not get reconfigured when running in the test env, + # the added and on_exit removed settings below should have no effect besides + # triggering the deprecation warnings Application.put_env(:logger, :console, level: :all) Application.put_env(:logger, :backends, [:console]) From 08ba0d46d77cbb19f218b464a1cbeb700227e6a8 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Sun, 22 Feb 2026 22:27:48 +0100 Subject: [PATCH 043/106] ActivityDraft: Add missing __MODULE__ matches and drop unneeded ones --- lib/pleroma/web/common_api/activity_draft.ex | 48 ++++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 6c84ef656..16489663a 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -99,34 +99,34 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> Map.put("cc", draft.cc) |> Map.put("actor", draft.user.ap_id) - %__MODULE__{draft | object: object} + %{draft | object: object} end defp put_params(%__MODULE__{} = draft, params) do params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id]) - %__MODULE__{draft | params: params} + %{draft | params: params} end defp status(%__MODULE__{params: %{status: status}} = draft) do - %__MODULE__{draft | status: String.trim(status)} + %{draft | status: String.trim(status)} end defp summary(%__MODULE__{params: params} = draft) do - %__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")} + %{draft | summary: Map.get(params, :spoiler_text, "")} end defp full_payload(%__MODULE__{status: status, summary: summary} = draft) do full_payload = String.trim(status <> summary) case Utils.validate_character_limit(full_payload, draft.attachments) do - :ok -> %__MODULE__{draft | full_payload: full_payload} + :ok -> %{draft | full_payload: full_payload} {:error, message} -> add_error(draft, message) end end defp attachments(%__MODULE__{params: params} = draft) do attachments = Utils.attachments_from_ids(params, draft.user) - draft = %__MODULE__{draft | attachments: attachments} + draft = %{draft | attachments: attachments} case Utils.validate_attachments_count(attachments) do :ok -> draft @@ -149,7 +149,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do with %Activity{} = activity <- Activity.get_by_id(id), true <- Visibility.visible_for_user?(activity, draft.user), {_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do - %__MODULE__{draft | in_reply_to: activity} + %{draft | in_reply_to: activity} else nil -> add_error(draft, dgettext("errors", "Cannot reply to a deleted status")) @@ -170,7 +170,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp in_reply_to( %__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft ) do - %__MODULE__{draft | in_reply_to: in_reply_to} + %{draft | in_reply_to: in_reply_to} end defp in_reply_to(draft), do: draft @@ -179,14 +179,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do when not_empty_string(id) do case Activity.get_by_id_with_object(id) do %Activity{} = activity -> - %__MODULE__{draft | quote_post: activity} + %{draft | quote_post: activity} _ -> draft end end - defp quote_post(%{params: %{quote_id: id}} = draft) when not_empty_string(id) do + defp quote_post(%__MODULE__{params: %{quote_id: id}} = draft) when not_empty_string(id) do quote_post(%{draft | params: Map.put(draft.params, :quoted_status_id, id)}) end @@ -194,7 +194,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp in_reply_to_conversation(%__MODULE__{} = draft) do in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id]) - %__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation} + %{draft | in_reply_to_conversation: in_reply_to_conversation} end defp visibility(%__MODULE__{params: params} = draft) do @@ -203,7 +203,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do add_error(draft, dgettext("errors", "The message visibility must be direct")) {visibility, _} -> - %__MODULE__{draft | visibility: visibility} + %{draft | visibility: visibility} end end @@ -219,7 +219,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do false end - defp quoting_visibility(%{quote_post: %Activity{}} = draft) do + defp quoting_visibility(%__MODULE__{quote_post: %Activity{}} = draft) do with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false), true <- can_quote?(draft, object, Visibility.get_visibility(object)) do draft @@ -232,7 +232,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp expires_at(%__MODULE__{} = draft) do case CommonAPI.check_expiry_date(draft.params[:expires_in]) do - {:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at} + {:ok, expires_at} -> %{draft | expires_at: expires_at} {:error, message} -> add_error(draft, message) end end @@ -240,7 +240,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp poll(%__MODULE__{} = draft) do case Utils.make_poll_data(draft.params) do {:ok, {poll, poll_emoji}} -> - %__MODULE__{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)} + %{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)} {:error, message} -> add_error(draft, message) @@ -258,22 +258,22 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> Kernel.++(mentioned_ap_ids) |> Utils.get_addressed_users(draft.params[:to]) - %__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags} + %{draft | content_html: content_html, mentions: mentions, tags: tags} end defp to_and_cc(%__MODULE__{} = draft) do {to, cc} = Utils.get_to_and_cc(draft) - %__MODULE__{draft | to: to, cc: cc} + %{draft | to: to, cc: cc} end defp context(%__MODULE__{} = draft) do context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation) - %__MODULE__{draft | context: context} + %{draft | context: context} end defp sensitive(%__MODULE__{} = draft) do sensitive = draft.params[:sensitive] - %__MODULE__{draft | sensitive: sensitive} + %{draft | sensitive: sensitive} end defp language(%__MODULE__{} = draft) do @@ -285,7 +285,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do _ -> LanguageDetector.detect(draft.content_html <> " " <> draft.summary) end - %__MODULE__{draft | language: language} + %{draft | language: language} end defp object(%__MODULE__{} = draft) do @@ -329,12 +329,12 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> Map.put("generator", draft.params[:generator]) |> Map.put("language", draft.language) - %__MODULE__{draft | object: object} + %{draft | object: object} end defp preview?(%__MODULE__{} = draft) do preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview]) - %__MODULE__{draft | preview?: preview?} + %{draft | preview?: preview?} end defp changes(%__MODULE__{} = draft) do @@ -357,14 +357,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do } |> Utils.maybe_add_list_data(draft.user, draft.visibility) - %__MODULE__{draft | changes: changes} + %{draft | changes: changes} end defp with_valid(%{valid?: true} = draft, func), do: func.(draft) defp with_valid(draft, _func), do: draft defp add_error(%__MODULE__{} = draft, message) do - %__MODULE__{draft | valid?: false, errors: [message | draft.errors]} + %{draft | valid?: false, errors: [message | draft.errors]} end defp validate(%{valid?: true} = draft), do: {:ok, draft} From 784a319f2eb9b98e6e034c38b462dd1e11390359 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Thu, 14 May 2026 00:22:24 +0200 Subject: [PATCH 044/106] Woodpecker CI: Bump unit testing pipeline to Elixir 1.19 --- ...t-testing-elixir-1.18.yaml => unit-testing-elixir-1.19.yaml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .woodpecker/{unit-testing-elixir-1.18.yaml => unit-testing-elixir-1.19.yaml} (97%) diff --git a/.woodpecker/unit-testing-elixir-1.18.yaml b/.woodpecker/unit-testing-elixir-1.19.yaml similarity index 97% rename from .woodpecker/unit-testing-elixir-1.18.yaml rename to .woodpecker/unit-testing-elixir-1.19.yaml index 9ad9eebc9..462e2ee5d 100644 --- a/.woodpecker/unit-testing-elixir-1.18.yaml +++ b/.woodpecker/unit-testing-elixir-1.19.yaml @@ -19,7 +19,7 @@ variables: steps: unit-testing-elixir-1.18: - image: elixir:1.18-otp-27-alpine + image: elixir:1.19-otp-28-alpine entrypoint: *script_file_entrypoint environment: MIX_ENV: test From 9782bcf979c12b06b7aefde14ed3708e74ff0657 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 16 Jun 2026 18:22:23 +0200 Subject: [PATCH 045/106] Elixir 1.19: Fix most MRFs being absent from AdminAPI suggestions. Elixir 1.19 started lazily loading modules, so modules only get loaded when referenced. Thus :code.all_loaded would no longer include all MRF policies which is used on startup to generate sugestions for AdminAPI. --- lib/pleroma/docs/generator.ex | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex index 93b19484f..70413837a 100644 --- a/lib/pleroma/docs/generator.ex +++ b/lib/pleroma/docs/generator.ex @@ -12,13 +12,27 @@ defmodule Pleroma.Docs.Generator do @spec list_behaviour_implementations(behaviour :: module()) :: [module()] def list_behaviour_implementations(behaviour) do - :code.all_loaded() - |> Enum.filter(fn {module, _} -> + # :code.all_loaded() no longer works on Elixir 1.19 and newer since modules are lazily loaded. + # We can get a complete list of modules by interating over Application.loaded_applications/0, + # but that includes every module which would get loaded below, which is very slow. + # Hardcode the Pleroma application instead, which is good enough for now. + application_modules = + case :application.get_key(:pleroma, :modules) do + {:ok, modules} -> modules + :undefined -> [] + end + + # At compile time or in tests some modules will be outside of the Pleroma application. + loaded_modules = Enum.map(:code.all_loaded(), fn {module, _} -> module end) + + (application_modules ++ loaded_modules) + |> Enum.uniq() + |> Enum.filter(fn module -> + Code.ensure_loaded(module) + # This shouldn't be needed as all modules are expected to have module_info/1, # but in test environments some transient modules `:elixir_compiler_XX` # are loaded for some reason (where XX is a random integer). - Code.ensure_loaded(module) - if function_exported?(module, :module_info, 1) do module.module_info(:attributes) |> Keyword.get_values(:behaviour) @@ -26,7 +40,6 @@ defmodule Pleroma.Docs.Generator do |> Enum.member?(behaviour) end end) - |> Enum.map(fn {module, _} -> module end) end @doc """ From a020b8a1901720238e8749d8487ddb218b82472b Mon Sep 17 00:00:00 2001 From: Phantasm Date: Mon, 29 Jun 2026 23:47:43 +0200 Subject: [PATCH 046/106] Elixir 1.19: Ensure default primary logger configg level is debug Setting the default handler and ex_syslogger handler levels does not necessarily have to set the primary logger log level, at least in the future. Without this, the primary log level might be lower than the configured console/syslog levels, which would cause some desired logs to not be logged. --- config/config.exs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/config.exs b/config/config.exs index 37a2590ca..80183a3d9 100644 --- a/config/config.exs +++ b/config/config.exs @@ -133,12 +133,17 @@ config :pleroma, Pleroma.Web.Endpoint, ] # Configures Elixir's Logger +# Primary config +config :logger, level: :debug + +# Console config config :logger, :default_handler, level: :debug config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:actor, :path, :type, :user] +# Syslog config config :logger, :ex_syslogger, level: :debug, ident: "pleroma", From 7d9175350d0f2ce715048ac1795ec4790d192c3b Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 30 Jun 2026 00:24:48 +0200 Subject: [PATCH 047/106] Elixir 1.19: Completely overwrite formatter for default handler :logger.update_handler_config/3 can merge configs, :logger.set_handler_config/3 instead resets the rest to defaults. --- lib/pleroma/config/deprecation_warnings.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index e77785483..8f8786ee7 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -435,7 +435,7 @@ defmodule Pleroma.Config.DeprecationWarnings do """) if log_level, do: :logger.update_handler_config(:default, :level, log_level) - :logger.update_handler_config(:default, :formatter, formatter) + :logger.set_handler_config(:default, :formatter, formatter) end end From f4b415c54e475f891cffe9363d4e19d58a062ad3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 22 May 2026 12:12:11 -0700 Subject: [PATCH 048/106] Improve RSS compliance The core feed reader issue was that RSS 2.0 is a simple text element and can only appear once per . By having multiple tags with rel attributes, parsers were getting confused and resolving entries to the wrong URL. Now each item has exactly one : the post URL. Everything else uses valid or dedicated extension elements where appropriate. --- changelog.d/rss.fix | 1 + lib/pleroma/web/feed/feed_view.ex | 151 +++++++++++++++++- .../templates/feed/feed/_activity.atom.eex | 24 +-- .../web/templates/feed/feed/_activity.rss.eex | 35 ++-- .../feed/feed/_tag_activity.atom.eex | 32 ++-- .../templates/feed/feed/_tag_activity.xml.eex | 22 ++- .../web/templates/feed/feed/tag.atom.eex | 6 +- .../web/templates/feed/feed/tag.rss.eex | 5 + .../web/templates/feed/feed/user.atom.eex | 6 +- .../web/templates/feed/feed/user.rss.eex | 12 +- test/pleroma/web/feed/tag_controller_test.exs | 2 +- 11 files changed, 212 insertions(+), 84 deletions(-) create mode 100644 changelog.d/rss.fix diff --git a/changelog.d/rss.fix b/changelog.d/rss.fix new file mode 100644 index 000000000..a3455698a --- /dev/null +++ b/changelog.d/rss.fix @@ -0,0 +1 @@ +Fix RSS feeds to be W3C compliant and render better in RSS readers diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index e1ee33d62..a433b8403 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -97,6 +97,64 @@ defmodule Pleroma.Web.Feed.FeedView do end end + def rss_content_encoded(data) do + data + |> feed_content_encoded() + |> String.replace("]]>", "]]>") + end + + def atom_content_encoded(data), do: feed_content_encoded(data) + + defp feed_content_encoded(data) do + base_content = + case activity_content(data) do + "" -> + data["summary"] || data["type"] || + "" + |> escape() + + content -> + content + end + + attachments_html = + (data["attachment"] || []) + |> Enum.map(&rss_attachment_preview/1) + |> Enum.reject(&(&1 == "")) + |> Enum.join("

") + + [base_content, attachments_html] + |> Enum.reject(&(&1 == "")) + |> Enum.join("

") + end + + defp rss_attachment_preview(attachment) do + href = attachment_href(attachment) + + if is_binary(href) and href != "" do + media_type = attachment_type(attachment) || "application/octet-stream" + escaped_href = escape(href) + name = attachment["name"] || "Attachment" + escaped_name = escape(name) + + cond do + String.starts_with?(media_type, "image/") -> + ~s(

#{escaped_name}

) + + String.starts_with?(media_type, "video/") -> + ~s(

#{escaped_name}

) + + String.starts_with?(media_type, "audio/") -> + ~s(

#{escaped_name}

) + + true -> + ~s(

#{escaped_name}

) + end + else + "" + end + end + def activity_content(%{"content" => content}) do content |> String.replace(~r/[\n\r]/, "") @@ -106,16 +164,99 @@ defmodule Pleroma.Web.Feed.FeedView do def activity_context(activity), do: escape(activity.data["context"]) + def feed_self_url(conn), do: Phoenix.Controller.current_url(conn) + + def activity_link(activity, data) do + cond do + activity.local -> data["id"] || data["url"] + true -> data["external_url"] || data["id"] || data["url"] + end + end + def attachment_href(attachment) do - attachment["url"] - |> hd() + attachment + |> attachment_url_data() |> Map.get("href") end def attachment_type(attachment) do - attachment["url"] - |> hd() - |> Map.get("mediaType") + attachment + |> attachment_url_data() + |> Map.get("mediaType") || attachment["mediaType"] + end + + def attachment_size(attachment) do + attachment + |> attachment_url_data() + |> Map.get("size", 0) + end + + def attachment_size_positive(attachment) do + case attachment_size(attachment) do + size when is_integer(size) and size > 0 -> + size + + size when is_binary(size) -> + case Integer.parse(size) do + {parsed, ""} when parsed > 0 -> parsed + _ -> nil + end + + _ -> + nil + end + end + + def attachment_medium(attachment) do + case attachment_type(attachment) do + "image/" <> _rest -> "image" + "video/" <> _rest -> "video" + "audio/" <> _rest -> "audio" + _ -> "document" + end + end + + def attachment_previewable?(attachment) do + attachment_medium(attachment) in ["image", "video", "audio"] + end + + def attachment_description(attachment) do + attachment["name"] || "" + end + + def media_content_xml(attachment) do + href = escape(attachment_href(attachment) || "") + type = escape(attachment_type(attachment) || "application/octet-stream") + medium = escape(attachment_medium(attachment)) + + file_size_attr = + case attachment_size_positive(attachment) do + size when is_integer(size) -> ~s( fileSize="#{size}") + _ -> "" + end + + description_xml = + case attachment_description(attachment) do + "" -> + "" + + description -> + ~s(\n #{escape(description)}) + end + + """ + + nonadult#{description_xml} + + """ + end + + defp attachment_url_data(attachment) do + case attachment["url"] do + [first | _] when is_map(first) -> first + %{} = map -> map + _ -> %{} + end end def get_href(id) do diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index b774f7984..f87d97cb4 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -3,7 +3,7 @@ http://activitystrea.ms/schema/1.0/post <%= @data["id"] %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> - <%= activity_description(@data) %> + <%= escape(atom_content_encoded(@data)) %> <%= to_rfc3339(@data["published"]) %> <%= to_rfc3339(@data["published"]) %> @@ -15,35 +15,23 @@ <%= escape(@data["summary"]) %> <% end %> - <%= if @activity.local do %> - - - <% else %> - - <% end %> + + <%= for tag <- Pleroma.Object.hashtags(@object) do %> <% end %> <%= for attachment <- @data["attachment"] || [] do %> - + <%= unless attachment_previewable?(attachment) do %> + + <% end %> <% end %> <%= if @data["inReplyTo"] do %> <% end %> - <%= for id <- @activity.recipients do %> - <%= if id == Pleroma.Constants.as_public() do %> - - <% else %> - <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %> - - <% end %> - <% end %> - <% end %> - <%= for {emoji, file} <- @data["emoji"] || %{} do %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex index 7de98f736..a8c2aa342 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex @@ -1,45 +1,32 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post - <%= @data["id"] %> + <%= @data["id"] %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= activity_description(@data) %> + ]]> <%= to_rfc2822(@data["published"]) %> <%= activity_context(@activity) %> - <%= if @activity.local do %> - <%= @data["id"] %> - <% else %> - <%= @data["external_url"] %> - <% end %> - - <%= activity_context(@activity) %> + <%= activity_link(@activity, @data) %> <%= for tag <- Pleroma.Object.hashtags(@object) do %> - + <%= if tag && tag != "" do %> + <%= tag %> + <% end %> <% end %> <%= for attachment <- @data["attachment"] || [] do %> - + <%= size = attachment_size_positive(attachment) %> + <%= unless attachment_previewable?(attachment) do %> + + <% end %> + <%= media_content_xml(attachment) %> <% end %> <%= if @data["inReplyTo"] do %> <% end %> - - <%= for id <- @activity.recipients do %> - <%= if id == Pleroma.Constants.as_public() do %> - http://activityschema.org/collection/public - <% else %> - <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %> - <%= id %> - <% end %> - <% end %> - <% end %> - - <%= for {emoji, file} <- @data["emoji"] || %{} do %> - <%= file %> - <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index 03c222975..ae762f9ed 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -6,7 +6,7 @@ <%= @data["id"] %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> - <%= activity_description(@data) %> + <%= escape(atom_content_encoded(@data)) %> <%= to_rfc3339(@data["published"]) %> <%= to_rfc3339(@data["published"]) %> @@ -15,34 +15,22 @@ <%= if @data["summary"] != "" do %> - <%= @data["summary"] %> + <%= escape(@data["summary"]) %> <% end %> - <%= if @activity.local do %> - - - <% else %> - - <% end %> - - <%= for id <- @activity.recipients do %> - <%= if id == Pleroma.Constants.as_public() do %> - - <% else %> - <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %> - - <% end %> - <% end %> - <% end %> + + <%= for tag <- Pleroma.Object.hashtags(@object) do %> <% end %> + <%= for attachment <- @data["attachment"] || [] do %> + <%= unless attachment_previewable?(attachment) do %> + + <% end %> + <% end %> + <%= for {emoji, file} <- @data["emoji"] || %{} do %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex index 1b8c34b87..6ae3c5a55 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -1,14 +1,22 @@ - <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> - - - <%= activity_context(@activity) %> - <%= activity_context(@activity) %> + <%= activity_link(@activity, @data) %> + <%= activity_link(@activity, @data) %> <%= to_rfc2822(@data["published"]) %> - + <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= activity_description(@data) %> + ]]> + <%= for attachment <- @data["attachment"] || [] do %> - + <%= size = attachment_size_positive(attachment) %> + <%= unless attachment_previewable?(attachment) do %> + + <% end %> + <%= media_content_xml(attachment) %> <% end %> + <%= for tag <- Pleroma.Object.hashtags(@object) do %> + <%= if tag && tag != "" do %> + <%= tag %> + <% end %> + <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index 3449c97ff..1646aeec7 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -9,10 +9,12 @@ <%= Routes.tag_feed_url(@conn, :feed, @tag) <> ".atom" %> #<%= @tag %> - <%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %> + <%= with subtitle when subtitle not in [nil, ""] <- Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) do %> + <%= subtitle %> + <% end %> <%= feed_logo() %> <%= most_recent_update(@activities) %> - " type="application/atom+xml"/> + <%= for activity <- @activities do %> <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex index a87f9bf50..618b89ba3 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -1,6 +1,8 @@ @@ -8,7 +10,10 @@ <%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %> <%= "#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss" %> <%= feed_logo() %> + <%= feed_logo() %> 2b90d9 + <%= most_recent_update(@activities) || to_rfc2822(NaiveDateTime.utc_now()) %> + <%= Pleroma.Application.named_version() %> <%= for activity <- @activities do %> <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/user.atom.eex b/lib/pleroma/web/templates/feed/feed/user.atom.eex index c2c77cfed..cc6883727 100644 --- a/lib/pleroma/web/templates/feed/feed/user.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/user.atom.eex @@ -8,10 +8,12 @@ <%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %> <%= @user.nickname <> "'s timeline" %> - <%= escape(@user.bio) %> + <%= if @user.bio && @user.bio != "" do %> + <%= escape(@user.bio) %> + <% end %> <%= most_recent_update(@activities, @user, :atom) %> <%= logo(@user) %> - " type="application/atom+xml"/> + <%= render Phoenix.Controller.view_module(@conn), "_author.atom", assigns %> diff --git a/lib/pleroma/web/templates/feed/feed/user.rss.eex b/lib/pleroma/web/templates/feed/feed/user.rss.eex index b907a7e57..e2150d8b6 100644 --- a/lib/pleroma/web/templates/feed/feed/user.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/user.rss.eex @@ -1,26 +1,32 @@ <%= @user.nickname <> "'s timeline" %> - <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %> - " + + <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %> <%= escape(@user.bio) %> <%= logo(@user) %> <%= @user.nickname <> "'s timeline" %> <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %> + <%= most_recent_update(@activities, @user, :rss) %> + <%= logo(@user) %> + <%= Pleroma.Application.named_version() %> <%= render Phoenix.Controller.view_module(@conn), "_author.rss", assigns %> <%= if last_activity(@activities) do %> - <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}" %> + - <%= activity_link(@activity, @data) %> + <%= escape(activity_link(@activity, @data) || "") %> <%= for tag <- Pleroma.Object.hashtags(@object) do %> <%= if tag && tag != "" do %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex index 09945605c..a8796353f 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -1,6 +1,6 @@ - <%= activity_link(@activity, @data) %> - <%= activity_link(@activity, @data) %> + <%= escape(activity_link(@activity, @data) || "") %> + <%= escape(activity_link(@activity, @data) || "") %> <%= to_rfc2822(@data["published"]) %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= activity_description(@data) %> diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index 9e8874455..eef524cc4 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -167,6 +167,8 @@ defmodule Pleroma.Web.Feed.TagControllerTest do ~c"yeah #PleromaArt" ] + assert xpath(xml, ~x"//channel/item/guid/@isPermaLink"sl) == ["true", "true"] + assert xpath(xml, ~x"//channel/item/pubDate/text()"sl) == [ FeedView.to_rfc2822(activity2.data["published"]), FeedView.to_rfc2822(activity1.data["published"]) @@ -232,6 +234,37 @@ defmodule Pleroma.Web.Feed.TagControllerTest do refute response =~ "" end + test "escapes and round-trips RSS item permalinks", %{conn: conn} do + user = insert(:user, local: false) + permalink = "https://remote.example/post?first=one&second=two" + + note = + insert(:note, + user: user, + object_local: false, + data: %{ + "content" => "remote #rsslink", + "external_url" => permalink, + "summary" => "", + "tag" => ["rsslink"] + } + ) + + insert(:note_activity, user: user, note: note, local: false, object_local: false) + + response = + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "rsslink.rss")) + |> response(200) + + assert response =~ "https://remote.example/post?first=one&second=two" + + xml = parse(response) + assert xpath(xml, ~x"//channel/item/guid/text()"s) == permalink + assert xpath(xml, ~x"//channel/item/link/text()"s) == permalink + end + describe "private instance" do setup do: clear_config([:instance, :public], false) From 2d667e1d2a2cfecea99cdcd660460882b2dfbf20 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2026 20:44:58 +0400 Subject: [PATCH 103/106] style(feed): format enclosure fixtures --- test/pleroma/web/feed/tag_controller_test.exs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index eef524cc4..a23c25274 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -30,8 +30,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do %{ "url" => [ %{ - "href" => - "https://peertube.moe/video.mp4?first=one&second=two", + "href" => "https://peertube.moe/video.mp4?first=one&second=two", "mediaType" => "video/mp4", "size" => "123", "type" => "Link" @@ -115,8 +114,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do %{ "url" => [ %{ - "href" => - "https://peertube.moe/video.mp4?first=one&second=two", + "href" => "https://peertube.moe/video.mp4?first=one&second=two", "mediaType" => "video/mp4", "size" => "123", "type" => "Link" From 5f71532442c7ad45c34663a6b3cf08f081a35132 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2026 20:59:13 +0400 Subject: [PATCH 104/106] fix(feed): tighten enclosure metadata --- lib/pleroma/web/feed/feed_view.ex | 10 ++++++---- .../web/templates/feed/feed/_activity.atom.eex | 2 +- .../web/templates/feed/feed/_activity.rss.eex | 9 ++++----- .../web/templates/feed/feed/_tag_activity.atom.eex | 2 +- .../web/templates/feed/feed/_tag_activity.xml.eex | 9 ++++----- test/pleroma/web/feed/feed_view_test.exs | 13 +++++++++++++ test/pleroma/web/feed/user_controller_test.exs | 9 ++++++--- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index dcbb8404f..fc29db2b7 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -131,7 +131,7 @@ defmodule Pleroma.Web.Feed.FeedView do end attachments_html = - if data["sensitive"] do + if sensitive?(data) do "" else (data["attachment"] || []) @@ -181,6 +181,8 @@ defmodule Pleroma.Web.Feed.FeedView do def activity_context(activity), do: escape(activity.data["context"]) + def sensitive?(data), do: data["sensitive"] in [true, "true"] + def feed_self_url(conn) do conn |> Phoenix.Controller.current_url() @@ -237,15 +239,15 @@ defmodule Pleroma.Web.Feed.FeedView do end end - def attachment_previewable?(attachment) do - attachment_medium(attachment) in ["image", "video", "audio"] + def rss_enclosure_attachment(attachments) do + Enum.find(attachments || [], &(not is_nil(attachment_size_positive(&1)))) end def attachment_description(attachment) do attachment["name"] || "" end - def media_content_xml(_attachment, true), do: "" + def media_content_xml(_attachment, sensitive) when sensitive in [true, "true"], do: "" def media_content_xml(attachment, _sensitive) do href = escape(attachment_href(attachment) || "") diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index bc67e12b3..91ef947f7 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -21,7 +21,7 @@ <% end %> - <%= unless @data["sensitive"] do %> + <%= unless sensitive?(@data) do %> <%= for attachment <- @data["attachment"] || [] do %> <%= if size = attachment_size_positive(attachment) do %> " type="<%= escape(attachment_type(attachment) || "application/octet-stream") %>" length="<%= size %>"/> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex index 393501dad..4d71a45c7 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex @@ -18,15 +18,14 @@ <% end %> <% end %> - <%= unless @data["sensitive"] do %> - <%= with [attachment | _] <- @data["attachment"] || [] do %> - <%= size = attachment_size_positive(attachment) %> - " type="<%= escape(attachment_type(attachment) || "application/octet-stream") %>" length="<%= size || 0 %>" /> + <%= unless sensitive?(@data) do %> + <%= with attachment when not is_nil(attachment) <- rss_enclosure_attachment(@data["attachment"]) do %> + " type="<%= escape(attachment_type(attachment) || "application/octet-stream") %>" length="<%= attachment_size_positive(attachment) %>" /> <% end %> <% end %> <%= for attachment <- @data["attachment"] || [] do %> - <%= media_content_xml(attachment, @data["sensitive"]) %> + <%= media_content_xml(attachment, sensitive?(@data)) %> <% end %> <%= if @data["inReplyTo"] do %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index ce5d62de6..44c4806cf 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -24,7 +24,7 @@ <% end %> - <%= unless @data["sensitive"] do %> + <%= unless sensitive?(@data) do %> <%= for attachment <- @data["attachment"] || [] do %> <%= if size = attachment_size_positive(attachment) do %> " type="<%= escape(attachment_type(attachment) || "application/octet-stream") %>" length="<%= size %>"/> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex index a8796353f..64e4c1d81 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -6,15 +6,14 @@ <%= activity_description(@data) %> ]]> - <%= unless @data["sensitive"] do %> - <%= with [attachment | _] <- @data["attachment"] || [] do %> - <%= size = attachment_size_positive(attachment) %> - " type="<%= escape(attachment_type(attachment) || "application/octet-stream") %>" length="<%= size || 0 %>"/> + <%= unless sensitive?(@data) do %> + <%= with attachment when not is_nil(attachment) <- rss_enclosure_attachment(@data["attachment"]) do %> + " type="<%= escape(attachment_type(attachment) || "application/octet-stream") %>" length="<%= attachment_size_positive(attachment) %>"/> <% end %> <% end %> <%= for attachment <- @data["attachment"] || [] do %> - <%= media_content_xml(attachment, @data["sensitive"]) %> + <%= media_content_xml(attachment, sensitive?(@data)) %> <% end %> <%= for tag <- Pleroma.Object.hashtags(@object) do %> diff --git a/test/pleroma/web/feed/feed_view_test.exs b/test/pleroma/web/feed/feed_view_test.exs index 45ed3c3ff..786b6df07 100644 --- a/test/pleroma/web/feed/feed_view_test.exs +++ b/test/pleroma/web/feed/feed_view_test.exs @@ -35,6 +35,7 @@ defmodule Pleroma.Web.Feed.FeedViewTest do assert FeedView.atom_content_encoded(data) == "content warning" assert FeedView.rss_content_encoded(data) == "content warning" assert FeedView.media_content_xml(attachment, true) == "" + assert FeedView.media_content_xml(attachment, "true") == "" media_content = FeedView.media_content_xml(attachment, false) assert media_content =~ "spoiler" @@ -43,4 +44,16 @@ defmodule Pleroma.Web.Feed.FeedViewTest do assert FeedView.media_content_xml(Map.delete(attachment, "name"), false) == ~s(\n) end + + test "does not treat a legacy false string as sensitive" do + data = %{ + "attachment" => [ + %{"url" => [%{"href" => "https://example.com/image.png", "mediaType" => "image/png"}]} + ], + "content" => "visible", + "sensitive" => "false" + } + + assert FeedView.atom_content_encoded(data) =~ " response(200) |> parse() - assert xpath(rss_xml, ~x"//item/enclosure/@url"sl) == ["https://example.com/file.bin"] - assert xpath(rss_xml, ~x"//item/enclosure/@type"sl) == ["application/octet-stream"] - assert xpath(rss_xml, ~x"//item/enclosure/@length"sl) == ["0"] + assert xpath(rss_xml, ~x"//item/enclosure/@url"sl) == [] + assert xpath(rss_xml, ~x"//item/media:content/@url"sl) == ["https://example.com/file.bin"] + + assert xpath(rss_xml, ~x"//item/media:content/@type"sl) == [ + "application/octet-stream" + ] end test "returns 404 for a missing feed", %{conn: conn} do From c5230d98c5bbb4ba70516f825fec4d1fb60d836d Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2026 21:04:35 +0400 Subject: [PATCH 105/106] fix(feed): complete Atom XML metadata --- lib/pleroma/web/feed/feed_view.ex | 2 +- .../templates/feed/feed/_activity.atom.eex | 2 +- .../feed/feed/_tag_activity.atom.eex | 2 +- .../web/templates/feed/feed/tag.atom.eex | 2 +- test/pleroma/web/feed/tag_controller_test.exs | 43 +++++++++++++++++++ .../pleroma/web/feed/user_controller_test.exs | 28 ++++++++++++ 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index fc29db2b7..b409b1923 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -43,7 +43,7 @@ defmodule Pleroma.Web.Feed.FeedView do def most_recent_update(activities, :rss) do activities |> Enum.map(fn activity -> - case Object.normalize(activity, fetch: false) do + case activity.object do %Object{updated_at: updated_at} -> updated_at _ -> activity.updated_at end diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index 91ef947f7..f94ba0793 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -1,7 +1,7 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post - <%= @data["id"] %> + <%= escape(@data["id"] || "") %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= escape(atom_content_encoded(@data)) %> <%= to_rfc3339(@data["published"]) %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index 44c4806cf..6c74c2ab7 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -4,7 +4,7 @@ <%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %> - <%= @data["id"] %> + <%= escape(@data["id"] || "") %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= escape(atom_content_encoded(@data)) %> <%= to_rfc3339(@data["published"]) %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index 1646aeec7..fb04544d3 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -13,7 +13,7 @@ <%= subtitle %> <% end %> <%= feed_logo() %> - <%= most_recent_update(@activities) %> + <%= most_recent_update(@activities) || to_rfc3339(NaiveDateTime.utc_now()) %> <%= for activity <- @activities do %> diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index a23c25274..35e29ed8c 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -232,6 +232,49 @@ defmodule Pleroma.Web.Feed.TagControllerTest do refute response =~ "" end + test "provides a valid updated timestamp for an empty Atom feed", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/atom+xml") + |> get(tag_feed_path(conn, :feed, "empty.atom")) + |> response(200) + + updated = response |> parse() |> xpath(~x"//feed/updated/text()"s) + assert {:ok, _datetime, 0} = DateTime.from_iso8601(updated) + end + + test "escapes Atom IDs and tag feed self links", %{conn: conn} do + user = insert(:user, local: false) + object_id = "https://remote.example/object?first=one&second=two" + + note = + insert(:note, + user: user, + object_local: false, + data: %{ + "content" => "remote #atomlink", + "external_url" => object_id, + "id" => object_id, + "summary" => "", + "tag" => ["atomlink"] + } + ) + + insert(:note_activity, user: user, note: note, local: false, object_local: false) + + response = + conn + |> put_req_header("accept", "application/atom+xml") + |> get("/tags/atomlink.atom?third=three&fourth=four") + |> response(200) + + assert response =~ "https://remote.example/object?first=one&second=two" + assert response =~ "third=three&fourth=four" + + xml = parse(response) + assert xpath(xml, ~x"//feed/entry/id/text()"s) == object_id + end + test "escapes and round-trips RSS item permalinks", %{conn: conn} do user = insert(:user, local: false) permalink = "https://remote.example/post?first=one&second=two" diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index b78ddb63a..458f5ec8d 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -150,6 +150,34 @@ defmodule Pleroma.Web.Feed.UserControllerTest do end end + test "keeps fallback summary markup inert in rendered Atom content", %{ + conn: conn, + object: object, + user: user + } do + data = + object.data + |> Map.put("content", "") + |> Map.put("summary", "") + + object + |> Ecto.Changeset.change(data: data) + |> Pleroma.Repo.update!() + + response = + conn + |> get("/users/#{user.nickname}/feed.atom") + |> response(200) + + assert response =~ "&lt;script&gt;alert(&#39;feed&#39;)&lt;/script&gt;" + + content_values = response |> parse() |> xpath(~x"//entry/content/text()"sl) + + assert Enum.any?(content_values, fn content -> + String.starts_with?(content, "<script>alert('feed')</script>") + end) + end + test "does not expose sensitive attachments as enclosures", %{ conn: conn, object: object, From 5d8b5a73d2d23dac3b79ff74431bca1b02a7fe70 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2026 21:05:16 +0400 Subject: [PATCH 106/106] style(feed): format Atom fallback assertion --- test/pleroma/web/feed/user_controller_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index 458f5ec8d..d12904ec0 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -169,7 +169,8 @@ defmodule Pleroma.Web.Feed.UserControllerTest do |> get("/users/#{user.nickname}/feed.atom") |> response(200) - assert response =~ "&lt;script&gt;alert(&#39;feed&#39;)&lt;/script&gt;" + assert response =~ + "&lt;script&gt;alert(&#39;feed&#39;)&lt;/script&gt;" content_values = response |> parse() |> xpath(~x"//entry/content/text()"sl)