From f50422c3802cd2316cfa6312d5a88c018c722843 Mon Sep 17 00:00:00 2001 From: tusooa Date: Tue, 28 Feb 2023 11:48:34 -0500 Subject: Move emoji_policy.ex to the right place --- lib/pleroma/web/activity_pub/emoji_policy.ex | 269 ----------------------- lib/pleroma/web/activity_pub/mrf/emoji_policy.ex | 269 +++++++++++++++++++++++ 2 files changed, 269 insertions(+), 269 deletions(-) delete mode 100644 lib/pleroma/web/activity_pub/emoji_policy.ex create mode 100644 lib/pleroma/web/activity_pub/mrf/emoji_policy.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/emoji_policy.ex b/lib/pleroma/web/activity_pub/emoji_policy.ex deleted file mode 100644 index df1566ec3..000000000 --- a/lib/pleroma/web/activity_pub/emoji_policy.ex +++ /dev/null @@ -1,269 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2023 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do - require Pleroma.Constants - - alias Pleroma.Object.Updater - - @moduledoc "Reject or force-unlisted emojis with certain URLs or names" - - @behaviour Pleroma.Web.ActivityPub.MRF.Policy - - defp config_remove_url do - Pleroma.Config.get([:mrf_emoji, :remove_url], []) - end - - defp config_remove_shortcode do - Pleroma.Config.get([:mrf_emoji, :remove_shortcode], []) - end - - defp config_unlist_url do - Pleroma.Config.get([:mrf_emoji, :federated_timeline_removal_url], []) - end - - defp config_unlist_shortcode do - Pleroma.Config.get([:mrf_emoji, :federated_timeline_removal_shortcode], []) - end - - @impl Pleroma.Web.ActivityPub.MRF.Policy - def history_awareness, do: :manual - - @impl Pleroma.Web.ActivityPub.MRF.Policy - def filter(%{"type" => type, "object" => %{} = object} = message) - when type in ["Create", "Update"] do - with {:ok, object} <- - Updater.do_with_history(object, fn object -> - {:ok, process_remove(object, :url, config_remove_url())} - end), - {:ok, object} <- - Updater.do_with_history(object, fn object -> - {:ok, process_remove(object, :shortcode, config_remove_shortcode())} - end), - activity <- Map.put(message, "object", object), - activity <- maybe_delist(activity) do - {:ok, activity} - end - end - - @impl Pleroma.Web.ActivityPub.MRF.Policy - def filter(%{"type" => type} = object) when type in Pleroma.Constants.actor_types() do - with object <- process_remove(object, :url, config_remove_url()), - object <- process_remove(object, :shortcode, config_remove_shortcode()) do - {:ok, object} - end - end - - @impl Pleroma.Web.ActivityPub.MRF.Policy - def filter(message) do - {:ok, message} - end - - defp match_string?(string, pattern) when is_binary(pattern) do - string == pattern - end - - defp match_string?(string, %Regex{} = pattern) do - String.match?(string, pattern) - end - - defp match_any?(string, patterns) do - Enum.any?(patterns, &match_string?(string, &1)) - end - - defp url_from_tag(%{"icon" => %{"url" => url}}), do: url - defp url_from_tag(_), do: nil - - defp url_from_emoji({_name, url}), do: url - - defp shortcode_from_tag(%{"name" => name}) when is_binary(name), do: String.trim(name, ":") - defp shortcode_from_tag(_), do: nil - - defp shortcode_from_emoji({name, _url}), do: name - - defp process_remove(object, :url, patterns) do - process_remove_impl(object, &url_from_tag/1, &url_from_emoji/1, patterns) - end - - defp process_remove(object, :shortcode, patterns) do - process_remove_impl(object, &shortcode_from_tag/1, &shortcode_from_emoji/1, patterns) - end - - defp process_remove_impl(object, extract_from_tag, extract_from_emoji, patterns) do - processed_tag = - Enum.filter( - object["tag"], - fn - %{"type" => "Emoji"} = tag -> - str = extract_from_tag.(tag) - - if is_binary(str) do - not match_any?(str, patterns) - else - true - end - - _ -> - true - end - ) - - processed_emoji = - if object["emoji"] do - object["emoji"] - |> Enum.reduce(%{}, fn {name, url} = emoji, acc -> - if not match_any?(extract_from_emoji.(emoji), patterns) do - Map.put(acc, name, url) - else - acc - end - end) - else - nil - end - - if processed_emoji do - object - |> Map.put("tag", processed_tag) - |> Map.put("emoji", processed_emoji) - else - object - |> Map.put("tag", processed_tag) - end - end - - defp maybe_delist(%{"object" => object, "to" => to, "type" => "Create"} = activity) do - check = fn object -> - if any_emoji_match?(object, &url_from_tag/1, &url_from_emoji/1, config_unlist_url()) or - any_emoji_match?( - object, - &shortcode_from_tag/1, - &shortcode_from_emoji/1, - config_unlist_shortcode() - ) do - {:should_delist, nil} - else - {:ok, %{}} - end - end - - should_delist? = fn object -> - with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check) do - false - else - _ -> true - end - end - - if Pleroma.Constants.as_public() in to and should_delist?.(object) do - to = List.delete(to, Pleroma.Constants.as_public()) - cc = [Pleroma.Constants.as_public() | activity["cc"] || []] - - activity - |> Map.put("to", to) - |> Map.put("cc", cc) - else - activity - end - end - - defp maybe_delist(activity), do: activity - - defp any_emoji_match?(object, extract_from_tag, extract_from_emoji, patterns) do - Kernel.||( - Enum.any?( - object["tag"], - fn - %{"type" => "Emoji"} = tag -> - str = extract_from_tag.(tag) - - if is_binary(str) do - match_any?(str, patterns) - else - false - end - - _ -> - false - end - ), - object["emoji"] - |> Enum.any?(fn emoji -> match_any?(extract_from_emoji.(emoji), patterns) end) - ) - end - - @impl Pleroma.Web.ActivityPub.MRF.Policy - def describe do - # This horror is needed to convert regex sigils to strings - mrf_emoji = - Pleroma.Config.get(:mrf_emoji, []) - |> Enum.map(fn {key, value} -> - {key, - Enum.map(value, fn - pattern -> - if not is_binary(pattern) do - inspect(pattern) - else - pattern - end - end)} - end) - |> Enum.into(%{}) - - {:ok, %{mrf_emoji: mrf_emoji}} - end - - @impl Pleroma.Web.ActivityPub.MRF.Policy - def config_description do - %{ - key: :mrf_emoji, - related_policy: "Pleroma.Web.ActivityPub.MRF.EmojiPolicy", - label: "MRF Emoji", - description: - "Reject or force-unlisted emojis whose URLs or names match a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).", - children: [ - %{ - key: :remove_url, - type: {:list, :string}, - description: """ - A list of patterns which result in emoji whose URL matches being removed from the message. This will apply to both statuses and user profiles. - - Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - """, - suggestions: ["foo", ~r/foo/iu] - }, - %{ - key: :remove_shortcode, - type: {:list, :string}, - description: """ - A list of patterns which result in emoji whose shortcode matches being removed from the message. This will apply to both statuses and user profiles. - - Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - """, - suggestions: ["foo", ~r/foo/iu] - }, - %{ - key: :federated_timeline_removal_url, - type: {:list, :string}, - description: """ - A list of patterns which result in message with emojis whose URLs match being removed from federated timelines (a.k.a unlisted). This will apply only to statuses. - - Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - """, - suggestions: ["foo", ~r/foo/iu] - }, - %{ - key: :federated_timeline_removal_shortcode, - type: {:list, :string}, - description: """ - A list of patterns which result in message with emojis whose shortcodes match being removed from federated timelines (a.k.a unlisted). This will apply only to statuses. - - Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. - """, - suggestions: ["foo", ~r/foo/iu] - } - ] - } - end -end diff --git a/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex new file mode 100644 index 000000000..df1566ec3 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/emoji_policy.ex @@ -0,0 +1,269 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do + require Pleroma.Constants + + alias Pleroma.Object.Updater + + @moduledoc "Reject or force-unlisted emojis with certain URLs or names" + + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + defp config_remove_url do + Pleroma.Config.get([:mrf_emoji, :remove_url], []) + end + + defp config_remove_shortcode do + Pleroma.Config.get([:mrf_emoji, :remove_shortcode], []) + end + + defp config_unlist_url do + Pleroma.Config.get([:mrf_emoji, :federated_timeline_removal_url], []) + end + + defp config_unlist_shortcode do + Pleroma.Config.get([:mrf_emoji, :federated_timeline_removal_shortcode], []) + end + + @impl Pleroma.Web.ActivityPub.MRF.Policy + def history_awareness, do: :manual + + @impl Pleroma.Web.ActivityPub.MRF.Policy + def filter(%{"type" => type, "object" => %{} = object} = message) + when type in ["Create", "Update"] do + with {:ok, object} <- + Updater.do_with_history(object, fn object -> + {:ok, process_remove(object, :url, config_remove_url())} + end), + {:ok, object} <- + Updater.do_with_history(object, fn object -> + {:ok, process_remove(object, :shortcode, config_remove_shortcode())} + end), + activity <- Map.put(message, "object", object), + activity <- maybe_delist(activity) do + {:ok, activity} + end + end + + @impl Pleroma.Web.ActivityPub.MRF.Policy + def filter(%{"type" => type} = object) when type in Pleroma.Constants.actor_types() do + with object <- process_remove(object, :url, config_remove_url()), + object <- process_remove(object, :shortcode, config_remove_shortcode()) do + {:ok, object} + end + end + + @impl Pleroma.Web.ActivityPub.MRF.Policy + def filter(message) do + {:ok, message} + end + + defp match_string?(string, pattern) when is_binary(pattern) do + string == pattern + end + + defp match_string?(string, %Regex{} = pattern) do + String.match?(string, pattern) + end + + defp match_any?(string, patterns) do + Enum.any?(patterns, &match_string?(string, &1)) + end + + defp url_from_tag(%{"icon" => %{"url" => url}}), do: url + defp url_from_tag(_), do: nil + + defp url_from_emoji({_name, url}), do: url + + defp shortcode_from_tag(%{"name" => name}) when is_binary(name), do: String.trim(name, ":") + defp shortcode_from_tag(_), do: nil + + defp shortcode_from_emoji({name, _url}), do: name + + defp process_remove(object, :url, patterns) do + process_remove_impl(object, &url_from_tag/1, &url_from_emoji/1, patterns) + end + + defp process_remove(object, :shortcode, patterns) do + process_remove_impl(object, &shortcode_from_tag/1, &shortcode_from_emoji/1, patterns) + end + + defp process_remove_impl(object, extract_from_tag, extract_from_emoji, patterns) do + processed_tag = + Enum.filter( + object["tag"], + fn + %{"type" => "Emoji"} = tag -> + str = extract_from_tag.(tag) + + if is_binary(str) do + not match_any?(str, patterns) + else + true + end + + _ -> + true + end + ) + + processed_emoji = + if object["emoji"] do + object["emoji"] + |> Enum.reduce(%{}, fn {name, url} = emoji, acc -> + if not match_any?(extract_from_emoji.(emoji), patterns) do + Map.put(acc, name, url) + else + acc + end + end) + else + nil + end + + if processed_emoji do + object + |> Map.put("tag", processed_tag) + |> Map.put("emoji", processed_emoji) + else + object + |> Map.put("tag", processed_tag) + end + end + + defp maybe_delist(%{"object" => object, "to" => to, "type" => "Create"} = activity) do + check = fn object -> + if any_emoji_match?(object, &url_from_tag/1, &url_from_emoji/1, config_unlist_url()) or + any_emoji_match?( + object, + &shortcode_from_tag/1, + &shortcode_from_emoji/1, + config_unlist_shortcode() + ) do + {:should_delist, nil} + else + {:ok, %{}} + end + end + + should_delist? = fn object -> + with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check) do + false + else + _ -> true + end + end + + if Pleroma.Constants.as_public() in to and should_delist?.(object) do + to = List.delete(to, Pleroma.Constants.as_public()) + cc = [Pleroma.Constants.as_public() | activity["cc"] || []] + + activity + |> Map.put("to", to) + |> Map.put("cc", cc) + else + activity + end + end + + defp maybe_delist(activity), do: activity + + defp any_emoji_match?(object, extract_from_tag, extract_from_emoji, patterns) do + Kernel.||( + Enum.any?( + object["tag"], + fn + %{"type" => "Emoji"} = tag -> + str = extract_from_tag.(tag) + + if is_binary(str) do + match_any?(str, patterns) + else + false + end + + _ -> + false + end + ), + object["emoji"] + |> Enum.any?(fn emoji -> match_any?(extract_from_emoji.(emoji), patterns) end) + ) + end + + @impl Pleroma.Web.ActivityPub.MRF.Policy + def describe do + # This horror is needed to convert regex sigils to strings + mrf_emoji = + Pleroma.Config.get(:mrf_emoji, []) + |> Enum.map(fn {key, value} -> + {key, + Enum.map(value, fn + pattern -> + if not is_binary(pattern) do + inspect(pattern) + else + pattern + end + end)} + end) + |> Enum.into(%{}) + + {:ok, %{mrf_emoji: mrf_emoji}} + end + + @impl Pleroma.Web.ActivityPub.MRF.Policy + def config_description do + %{ + key: :mrf_emoji, + related_policy: "Pleroma.Web.ActivityPub.MRF.EmojiPolicy", + label: "MRF Emoji", + description: + "Reject or force-unlisted emojis whose URLs or names match a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).", + children: [ + %{ + key: :remove_url, + type: {:list, :string}, + description: """ + A list of patterns which result in emoji whose URL matches being removed from the message. This will apply to both statuses and user profiles. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :remove_shortcode, + type: {:list, :string}, + description: """ + A list of patterns which result in emoji whose shortcode matches being removed from the message. This will apply to both statuses and user profiles. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :federated_timeline_removal_url, + type: {:list, :string}, + description: """ + A list of patterns which result in message with emojis whose URLs match being removed from federated timelines (a.k.a unlisted). This will apply only to statuses. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :federated_timeline_removal_shortcode, + type: {:list, :string}, + description: """ + A list of patterns which result in message with emojis whose shortcodes match being removed from federated timelines (a.k.a unlisted). This will apply only to statuses. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/iu] + } + ] + } + end +end -- cgit v1.2.3