Merge branch 'mrf-cleanup' into 'develop'

MRF cleanup

See merge request pleroma/pleroma!4219
This commit is contained in:
feld 2024-08-13 18:52:43 +00:00
commit ccf476a4c8
33 changed files with 376 additions and 375 deletions

View file

View file

@ -63,20 +63,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
end end
@impl true @impl true
def filter(%{"type" => "Follow", "actor" => actor_id} = message) do def filter(%{"type" => "Follow", "actor" => actor_id} = activity) do
%User{} = actor = normalize_by_ap_id(actor_id) %User{} = actor = normalize_by_ap_id(actor_id)
score = determine_if_followbot(actor) score = determine_if_followbot(actor)
if score < 0.8 || bot_allowed?(message, actor) do if score < 0.8 || bot_allowed?(activity, actor) do
{:ok, message} {:ok, activity}
else else
{:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"} {:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"}
end end
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -29,17 +29,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
defp contains_links?(_), do: false defp contains_links?(_), do: false
@impl true @impl true
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do def filter(%{"type" => "Create", "actor" => actor, "object" => object} = activity) do
with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor), with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor),
{:contains_links, true} <- {:contains_links, contains_links?(object)}, {:contains_links, true} <- {:contains_links, contains_links?(object)},
{:old_user, true} <- {:old_user, old_user?(u)} do {:old_user, true} <- {:old_user, old_user?(u)} do
{:ok, message} {:ok, activity}
else else
{:ok, %User{local: true}} -> {:ok, %User{local: true}} ->
{:ok, message} {:ok, activity}
{:contains_links, false} -> {:contains_links, false} ->
{:ok, message} {:ok, activity}
{:old_user, false} -> {:old_user, false} ->
{:reject, "[AntiLinkSpamPolicy] User has no posts nor followers"} {:reject, "[AntiLinkSpamPolicy] User has no posts nor followers"}
@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
end end
# in all other cases, pass through # in all other cases, pass through
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -22,11 +22,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do
end end
# copied from HellthreadPolicy # copied from HellthreadPolicy
defp get_recipient_count(message) do defp get_recipient_count(activity) do
recipients = (message["to"] || []) ++ (message["cc"] || []) recipients = (activity["to"] || []) ++ (activity["cc"] || [])
follower_collection = follower_collection =
User.get_cached_by_ap_id(message["actor"] || message["attributedTo"]).follower_address User.get_cached_by_ap_id(activity["actor"] || activity["attributedTo"]).follower_address
if Enum.member?(recipients, Pleroma.Constants.as_public()) do if Enum.member?(recipients, Pleroma.Constants.as_public()) do
recipients = recipients =
@ -80,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do
end end
# in all other cases, pass through # in all other cases, pass through
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -38,18 +38,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do
@query_timeout 500 @query_timeout 500
@impl true @impl true
def filter(%{"actor" => actor} = object) do def filter(%{"actor" => actor} = activity) do
actor_info = URI.parse(actor) actor_info = URI.parse(actor)
with {:ok, object} <- check_rbl(actor_info, object) do with {:ok, activity} <- check_rbl(actor_info, activity) do
{:ok, object} {:ok, activity}
else else
_ -> {:reject, "[DNSRBLPolicy]"} _ -> {:reject, "[DNSRBLPolicy]"}
end end
end end
@impl true @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe do def describe do
@ -90,7 +90,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do
} }
end end
defp check_rbl(%{host: actor_host}, object) do defp check_rbl(%{host: actor_host}, activity) do
with false <- match?(^actor_host, Pleroma.Web.Endpoint.host()), with false <- match?(^actor_host, Pleroma.Web.Endpoint.host()),
zone when not is_nil(zone) <- Keyword.get(Config.get([:mrf_dnsrbl]), :zone) do zone when not is_nil(zone) <- Keyword.get(Config.get([:mrf_dnsrbl]), :zone) do
query = query =
@ -100,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do
rbl_response = rblquery(query) rbl_response = rblquery(query)
if Enum.empty?(rbl_response) do if Enum.empty?(rbl_response) do
{:ok, object} {:ok, activity}
else else
Task.start(fn -> Task.start(fn ->
reason = reason =
@ -117,7 +117,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do
:error :error
end end
else else
_ -> {:ok, object} _ -> {:ok, activity}
end end
end end

View file

@ -8,9 +8,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(object) do def filter(activity) do
Logger.debug("REJECTING #{inspect(object)}") Logger.debug("REJECTING #{inspect(activity)}")
{:reject, object} {:reject, activity}
end end
@impl true @impl true

View file

@ -28,11 +28,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
Pleroma.Config.get([:mrf_emoji, :federated_timeline_removal_shortcode], []) Pleroma.Config.get([:mrf_emoji, :federated_timeline_removal_shortcode], [])
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def history_awareness, do: :manual def history_awareness, do: :manual
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def filter(%{"type" => type, "object" => %{"type" => objtype} = object} = message) def filter(%{"type" => type, "object" => %{"type" => objtype} = object} = activity)
when type in ["Create", "Update"] and objtype in Pleroma.Constants.status_object_types() do when type in ["Create", "Update"] and objtype in Pleroma.Constants.status_object_types() do
with {:ok, object} <- with {:ok, object} <-
Updater.do_with_history(object, fn object -> Updater.do_with_history(object, fn object ->
@ -42,13 +42,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
Updater.do_with_history(object, fn object -> Updater.do_with_history(object, fn object ->
{:ok, process_remove(object, :shortcode, config_remove_shortcode())} {:ok, process_remove(object, :shortcode, config_remove_shortcode())}
end), end),
activity <- Map.put(message, "object", object), activity <- Map.put(activity, "object", object),
activity <- maybe_delist(activity) do activity <- maybe_delist(activity) do
{:ok, activity} {:ok, activity}
end end
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def filter(%{"type" => type} = object) when type in Pleroma.Constants.actor_types() do def filter(%{"type" => type} = object) when type in Pleroma.Constants.actor_types() do
with object <- process_remove(object, :url, config_remove_url()), with object <- process_remove(object, :url, config_remove_url()),
object <- process_remove(object, :shortcode, config_remove_shortcode()) do object <- process_remove(object, :shortcode, config_remove_shortcode()) do
@ -56,7 +56,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
end end
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def filter(%{"type" => "EmojiReact"} = object) do def filter(%{"type" => "EmojiReact"} = object) do
with {:ok, _} <- with {:ok, _} <-
matched_emoji_checker(config_remove_url(), config_remove_shortcode()).(object) do matched_emoji_checker(config_remove_url(), config_remove_shortcode()).(object) do
@ -67,9 +67,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
end end
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def filter(message) do def filter(activity) do
{:ok, message} {:ok, activity}
end end
defp match_string?(string, pattern) when is_binary(pattern) do defp match_string?(string, pattern) when is_binary(pattern) do
@ -214,7 +214,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
) )
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def describe do def describe do
mrf_emoji = mrf_emoji =
Pleroma.Config.get(:mrf_emoji, []) Pleroma.Config.get(:mrf_emoji, [])
@ -226,7 +226,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
{:ok, %{mrf_emoji: mrf_emoji}} {:ok, %{mrf_emoji: mrf_emoji}}
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def config_description do def config_description do
%{ %{
key: :mrf_emoji, key: :mrf_emoji,
@ -239,7 +239,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
key: :remove_url, key: :remove_url,
type: {:list, :string}, type: {:list, :string},
description: """ description: """
A list of patterns which result in emoji whose URL matches being removed from the message. This will apply to statuses, emoji reactions, and user profiles. A list of patterns which result in emoji whose URL matches being removed from the activity. This will apply to statuses, emoji reactions, and user profiles.
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
""", """,
@ -249,7 +249,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
key: :remove_shortcode, key: :remove_shortcode,
type: {:list, :string}, type: {:list, :string},
description: """ description: """
A list of patterns which result in emoji whose shortcode matches being removed from the message. This will apply to statuses, emoji reactions, and user profiles. A list of patterns which result in emoji whose shortcode matches being removed from the activity. This will apply to statuses, emoji reactions, and user profiles.
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
""", """,
@ -259,7 +259,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
key: :federated_timeline_removal_url, key: :federated_timeline_removal_url,
type: {:list, :string}, type: {:list, :string},
description: """ 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. A list of patterns which result in activity 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/`. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
""", """,
@ -269,7 +269,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
key: :federated_timeline_removal_shortcode, key: :federated_timeline_removal_shortcode,
type: {:list, :string}, type: {:list, :string},
description: """ 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. A list of patterns which result in activities 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/`. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
""", """,

View file

@ -29,19 +29,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
def filter_by_summary(_in_reply_to, child), do: child def filter_by_summary(_in_reply_to, child), do: child
def filter(%{"type" => type, "object" => child_object} = object) def filter(%{"type" => type, "object" => object} = activity)
when type in ["Create", "Update"] and is_map(child_object) do when type in ["Create", "Update"] and is_map(object) do
child = child =
child_object["inReplyTo"] object["inReplyTo"]
|> Object.normalize(fetch: false) |> Object.normalize(fetch: false)
|> filter_by_summary(child_object) |> filter_by_summary(object)
object = Map.put(object, "object", child) activity = Map.put(activity, "object", child)
{:ok, object} {:ok, activity}
end end
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
end end

View file

@ -11,12 +11,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
require Logger require Logger
@impl true @impl true
def filter(message) do def filter(activity) do
with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]), with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]),
%User{actor_type: "Service"} = follower <- %User{actor_type: "Service"} = follower <-
User.get_cached_by_nickname(follower_nickname), User.get_cached_by_nickname(follower_nickname),
%{"type" => "Create", "object" => %{"type" => "Note"}} <- message do %{"type" => "Create", "object" => %{"type" => "Note"}} <- activity do
try_follow(follower, message) try_follow(follower, activity)
else else
nil -> nil ->
Logger.warning( Logger.warning(
@ -24,17 +24,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
account does not exist, or the account is not correctly configured as a bot." account does not exist, or the account is not correctly configured as a bot."
) )
{:ok, message} {:ok, activity}
_ -> _ ->
{:ok, message} {:ok, activity}
end end
end end
defp try_follow(follower, message) do defp try_follow(follower, activity) do
to = Map.get(message, "to", []) to = Map.get(activity, "to", [])
cc = Map.get(message, "cc", []) cc = Map.get(activity, "cc", [])
actor = [message["actor"]] actor = [activity["actor"]]
Enum.concat([to, cc, actor]) Enum.concat([to, cc, actor])
|> List.flatten() |> List.flatten()
@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
end end
end) end)
{:ok, message} {:ok, activity}
end end
@impl true @impl true

View file

@ -22,7 +22,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
"cc" => cc, "cc" => cc,
"actor" => actor, "actor" => actor,
"object" => object "object" => object
} = message } = activity
) do ) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
isbot = check_if_bot(user) isbot = check_if_bot(user)
@ -36,20 +36,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
|> Map.put("object", object) |> Map.put("object", object)
{:ok, message} {:ok, activity}
else else
{:ok, message} {:ok, activity}
end end
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -52,7 +52,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMention do
end end
@impl true @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -79,18 +79,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do
%{ %{
"type" => type, "type" => type,
"object" => %{"type" => "Note", "to" => to, "inReplyTo" => in_reply_to} "object" => %{"type" => "Note", "to" => to, "inReplyTo" => in_reply_to}
} = object } = activity
) )
when type in ["Create", "Update"] and is_list(to) and is_binary(in_reply_to) do when type in ["Create", "Update"] and is_list(to) and is_binary(in_reply_to) do
# image-only posts from pleroma apparently reach this MRF without the content field # image-only posts from pleroma apparently reach this MRF without the content field
content = object["object"]["content"] || "" content = activity["object"]["content"] || ""
# Get the replied-to user for sorting # Get the replied-to user for sorting
replied_to_user = get_replied_to_user(object["object"]) replied_to_user = get_replied_to_user(activity["object"])
mention_users = mention_users =
to to
|> clean_recipients(object) |> clean_recipients(activity)
|> Enum.map(&User.get_cached_by_ap_id/1) |> Enum.map(&User.get_cached_by_ap_id/1)
|> Enum.reject(&is_nil/1) |> Enum.reject(&is_nil/1)
|> sort_replied_user(replied_to_user) |> sort_replied_user(replied_to_user)
@ -126,11 +126,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do
content content
end end
{:ok, put_in(object["object"]["content"], content)} {:ok, put_in(activity["object"]["content"], content)}
end end
@impl true @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
alias Pleroma.Object alias Pleroma.Object
@moduledoc """ @moduledoc """
Reject, TWKN-remove or Set-Sensitive messages with specific hashtags (without the leading #) Reject, TWKN-remove or Set-Sensitive activities with specific hashtags (without the leading #)
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists. Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
""" """
@ -19,40 +19,40 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
@impl true @impl true
def history_awareness, do: :manual def history_awareness, do: :manual
defp check_reject(message, hashtags) do defp check_reject(activity, hashtags) do
if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
{:reject, "[HashtagPolicy] Matches with rejected keyword"} {:reject, "[HashtagPolicy] Matches with rejected keyword"}
else else
{:ok, message} {:ok, activity}
end end
end end
defp check_ftl_removal(%{"to" => to} = message, hashtags) do defp check_ftl_removal(%{"to" => to} = activity, hashtags) do
if Pleroma.Constants.as_public() in to and if Pleroma.Constants.as_public() in to and
Enum.any?(Config.get([:mrf_hashtag, :federated_timeline_removal]), fn match -> Enum.any?(Config.get([:mrf_hashtag, :federated_timeline_removal]), fn match ->
match in hashtags match in hashtags
end) do end) do
to = List.delete(to, Pleroma.Constants.as_public()) to = List.delete(to, Pleroma.Constants.as_public())
cc = [Pleroma.Constants.as_public() | message["cc"] || []] cc = [Pleroma.Constants.as_public() | activity["cc"] || []]
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
|> Kernel.put_in(["object", "to"], to) |> Kernel.put_in(["object", "to"], to)
|> Kernel.put_in(["object", "cc"], cc) |> Kernel.put_in(["object", "cc"], cc)
{:ok, message} {:ok, activity}
else else
{:ok, message} {:ok, activity}
end end
end end
defp check_ftl_removal(message, _hashtags), do: {:ok, message} defp check_ftl_removal(activity, _hashtags), do: {:ok, activity}
defp check_sensitive(message) do defp check_sensitive(activity) do
{:ok, new_object} = {:ok, new_object} =
Object.Updater.do_with_history(message["object"], fn object -> Object.Updater.do_with_history(activity["object"], fn object ->
hashtags = Object.hashtags(%Object{data: object}) hashtags = Object.hashtags(%Object{data: object})
if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do
@ -62,11 +62,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
end end
end) end)
{:ok, Map.put(message, "object", new_object)} {:ok, Map.put(activity, "object", new_object)}
end end
@impl true @impl true
def filter(%{"type" => type, "object" => object} = message) when type in ["Create", "Update"] do def filter(%{"type" => type, "object" => object} = activity)
when type in ["Create", "Update"] do
history_items = history_items =
with %{"formerRepresentations" => %{"orderedItems" => items}} <- object do with %{"formerRepresentations" => %{"orderedItems" => items}} <- object do
items items
@ -82,23 +83,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
hashtags = Object.hashtags(%Object{data: object}) ++ historical_hashtags hashtags = Object.hashtags(%Object{data: object}) ++ historical_hashtags
if hashtags != [] do if hashtags != [] do
with {:ok, message} <- check_reject(message, hashtags), with {:ok, activity} <- check_reject(activity, hashtags),
{:ok, message} <- {:ok, activity} <-
(if type == "Create" do (if type == "Create" do
check_ftl_removal(message, hashtags) check_ftl_removal(activity, hashtags)
else else
{:ok, message} {:ok, activity}
end), end),
{:ok, message} <- check_sensitive(message) do {:ok, activity} <- check_sensitive(activity) do
{:ok, message} {:ok, activity}
end end
else else
{:ok, message} {:ok, activity}
end end
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe do def describe do
@ -120,21 +121,21 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
%{ %{
key: :reject, key: :reject,
type: {:list, :string}, type: {:list, :string},
description: "A list of hashtags which result in message being rejected.", description: "A list of hashtags which result in the activity being rejected.",
suggestions: ["foo"] suggestions: ["foo"]
}, },
%{ %{
key: :federated_timeline_removal, key: :federated_timeline_removal,
type: {:list, :string}, type: {:list, :string},
description: description:
"A list of hashtags which result in message being removed from federated timelines (a.k.a unlisted).", "A list of hashtags which result in the activity being removed from federated timelines (a.k.a unlisted).",
suggestions: ["foo"] suggestions: ["foo"]
}, },
%{ %{
key: :sensitive, key: :sensitive,
type: {:list, :string}, type: {:list, :string},
description: description:
"A list of hashtags which result in message being set as sensitive (a.k.a NSFW/R-18)", "A list of hashtags which result in the activity being set as sensitive (a.k.a NSFW/R-18)",
suggestions: ["nsfw", "r18"] suggestions: ["nsfw", "r18"]
} }
] ]

View file

@ -7,54 +7,54 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
require Pleroma.Constants require Pleroma.Constants
@moduledoc "Block messages with too much mentions (configurable)" @moduledoc "Block activities with too much mentions (configurable)"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp delist_message(message, threshold) when threshold > 0 do defp delist_activity(activity, threshold) when threshold > 0 do
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address follower_collection = User.get_cached_by_ap_id(activity["actor"]).follower_address
to = message["to"] || [] to = activity["to"] || []
cc = message["cc"] || [] cc = activity["cc"] || []
follower_collection? = Enum.member?(to ++ cc, follower_collection) follower_collection? = Enum.member?(to ++ cc, follower_collection)
message = activity =
case get_recipient_count(message) do case get_recipient_count(activity) do
{:public, recipients} {:public, recipients}
when follower_collection? and recipients > threshold -> when follower_collection? and recipients > threshold ->
message activity
|> Map.put("to", [follower_collection]) |> Map.put("to", [follower_collection])
|> Map.put("cc", [Pleroma.Constants.as_public()]) |> Map.put("cc", [Pleroma.Constants.as_public()])
{:public, recipients} when recipients > threshold -> {:public, recipients} when recipients > threshold ->
message activity
|> Map.put("to", []) |> Map.put("to", [])
|> Map.put("cc", [Pleroma.Constants.as_public()]) |> Map.put("cc", [Pleroma.Constants.as_public()])
_ -> _ ->
message activity
end end
{:ok, message} {:ok, activity}
end end
defp delist_message(message, _threshold), do: {:ok, message} defp delist_activity(activity, _threshold), do: {:ok, activity}
defp reject_message(message, threshold) when threshold > 0 do defp reject_activity(activity, threshold) when threshold > 0 do
with {_, recipients} <- get_recipient_count(message) do with {_, recipients} <- get_recipient_count(activity) do
if recipients > threshold do if recipients > threshold do
{:reject, "[HellthreadPolicy] #{recipients} recipients is over the limit of #{threshold}"} {:reject, "[HellthreadPolicy] #{recipients} recipients is over the limit of #{threshold}"}
else else
{:ok, message} {:ok, activity}
end end
end end
end end
defp reject_message(message, _threshold), do: {:ok, message} defp reject_activity(activity, _threshold), do: {:ok, activity}
defp get_recipient_count(message) do defp get_recipient_count(activity) do
recipients = (message["to"] || []) ++ (message["cc"] || []) recipients = (activity["to"] || []) ++ (activity["cc"] || [])
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address follower_collection = User.get_cached_by_ap_id(activity["actor"]).follower_address
if Enum.member?(recipients, Pleroma.Constants.as_public()) do if Enum.member?(recipients, Pleroma.Constants.as_public()) do
recipients = recipients =
@ -73,7 +73,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
end end
@impl true @impl true
def filter(%{"type" => "Create", "object" => %{"type" => object_type}} = message) def filter(%{"type" => "Create", "object" => %{"type" => object_type}} = activity)
when object_type in ~w{Note Article} do when object_type in ~w{Note Article} do
reject_threshold = reject_threshold =
Pleroma.Config.get( Pleroma.Config.get(
@ -83,16 +83,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold]) delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold])
with {:ok, message} <- reject_message(message, reject_threshold), with {:ok, activity} <- reject_activity(activity, reject_threshold),
{:ok, message} <- delist_message(message, delist_threshold) do {:ok, activity} <- delist_activity(activity, delist_threshold) do
{:ok, message} {:ok, activity}
else else
e -> e e -> e
end end
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, def describe,
@ -104,13 +104,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
key: :mrf_hellthread, key: :mrf_hellthread,
related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy", related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
label: "MRF Hellthread", label: "MRF Hellthread",
description: "Block messages with excessive user mentions", description: "Block activities with excessive user mentions",
children: [ children: [
%{ %{
key: :delist_threshold, key: :delist_threshold,
type: :integer, type: :integer,
description: description:
"Number of mentioned users after which the message gets removed from timelines and" <> "Number of mentioned users after which the activity gets removed from timelines and" <>
"disables notifications. Set to 0 to disable.", "disables notifications. Set to 0 to disable.",
suggestions: [10] suggestions: [10]
}, },
@ -118,7 +118,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
key: :reject_threshold, key: :reject_threshold,
type: :integer, type: :integer,
description: description:
"Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.", "Number of mentioned users after which the activity gets rejected. Set to 0 to disable.",
suggestions: [20] suggestions: [20]
} }
] ]

View file

@ -48,12 +48,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
end end
@impl true @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def history_awareness, do: :auto def history_awareness, do: :auto
@impl true @impl true

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
alias Pleroma.Web.ActivityPub.MRF.Utils alias Pleroma.Web.ActivityPub.MRF.Utils
@moduledoc "Reject or Word-Replace messages with a keyword or regex" @moduledoc "Reject or Word-Replace activities with a keyword or regex"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@ -25,7 +25,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|> Enum.join("\n") |> Enum.join("\n")
end end
defp check_reject(%{"object" => %{} = object} = message) do defp check_reject(%{"object" => %{} = object} = activity) do
with {:ok, _new_object} <- with {:ok, _new_object} <-
Pleroma.Object.Updater.do_with_history(object, fn object -> Pleroma.Object.Updater.do_with_history(object, fn object ->
payload = object_payload(object) payload = object_payload(object)
@ -35,16 +35,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end) do end) do
{:reject, "[KeywordPolicy] Matches with rejected keyword"} {:reject, "[KeywordPolicy] Matches with rejected keyword"}
else else
{:ok, message} {:ok, activity}
end end
end) do end) do
{:ok, message} {:ok, activity}
else else
e -> e e -> e
end end
end end
defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = activity) do
check_keyword = fn object -> check_keyword = fn object ->
payload = object_payload(object) payload = object_payload(object)
@ -67,24 +67,24 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
if Pleroma.Constants.as_public() in to and should_delist?.(object) do if Pleroma.Constants.as_public() in to and should_delist?.(object) do
to = List.delete(to, Pleroma.Constants.as_public()) to = List.delete(to, Pleroma.Constants.as_public())
cc = [Pleroma.Constants.as_public() | message["cc"] || []] cc = [Pleroma.Constants.as_public() | activity["cc"] || []]
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
{:ok, message} {:ok, activity}
else else
{:ok, message} {:ok, activity}
end end
end end
defp check_ftl_removal(message) do defp check_ftl_removal(activity) do
{:ok, message} {:ok, activity}
end end
defp check_replace(%{"object" => %{} = object} = message) do defp check_replace(%{"object" => %{} = object} = activity) do
replace_kw = fn object -> replace_kw = fn object ->
["content", "name", "summary"] ["content", "name", "summary"]
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end) |> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
@ -103,18 +103,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
{:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw) {:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw)
message = Map.put(message, "object", object) activity = Map.put(activity, "object", object)
{:ok, message} {:ok, activity}
end end
@impl true @impl true
def filter(%{"type" => type, "object" => %{"content" => _content}} = message) def filter(%{"type" => type, "object" => %{"content" => _content}} = activity)
when type in ["Create", "Update"] do when type in ["Create", "Update"] do
with {:ok, message} <- check_reject(message), with {:ok, activity} <- check_reject(activity),
{:ok, message} <- check_ftl_removal(message), {:ok, activity} <- check_ftl_removal(activity),
{:ok, message} <- check_replace(message) do {:ok, activity} <- check_replace(activity) do
{:ok, message} {:ok, activity}
else else
{:reject, nil} -> {:reject, "[KeywordPolicy] "} {:reject, nil} -> {:reject, "[KeywordPolicy] "}
{:reject, _} = e -> e {:reject, _} = e -> e
@ -123,7 +123,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe do def describe do
@ -154,13 +154,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy", related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
label: "MRF Keyword", label: "MRF Keyword",
description: description:
"Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).", "Reject or Word-Replace activities matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
children: [ children: [
%{ %{
key: :reject, key: :reject,
type: {:list, :string}, type: {:list, :string},
description: """ description: """
A list of patterns which result in message being rejected. A list of patterns which result in the activity being rejected.
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
""", """,
@ -170,7 +170,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
key: :federated_timeline_removal, key: :federated_timeline_removal,
type: {:list, :string}, type: {:list, :string},
description: """ description: """
A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). A list of patterns which result in the activity being removed from federated timelines (a.k.a unlisted).
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
""", """,

View file

@ -31,7 +31,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
HTTP.get(url, [], http_client_opts) HTTP.get(url, [], http_client_opts)
end end
defp preload(%{"object" => %{"attachment" => attachments}} = _message) do defp preload(%{"object" => %{"attachment" => attachments}} = _activity) do
Enum.each(attachments, fn Enum.each(attachments, fn
%{"url" => url} when is_list(url) -> %{"url" => url} when is_list(url) ->
url url
@ -49,15 +49,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
end end
@impl true @impl true
def filter(%{"type" => type, "object" => %{"attachment" => attachments} = _object} = message) def filter(%{"type" => type, "object" => %{"attachment" => attachments} = _object} = activity)
when type in ["Create", "Update"] and is_list(attachments) and length(attachments) > 0 do when type in ["Create", "Update"] and is_list(attachments) and length(attachments) > 0 do
preload(message) preload(activity)
{:ok, message} {:ok, activity}
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -3,25 +3,25 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
@moduledoc "Block messages which mention a user" @moduledoc "Block activities which mention a user"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(%{"type" => "Create"} = message) do def filter(%{"type" => "Create"} = activity) do
reject_actors = Pleroma.Config.get([:mrf_mention, :actors], []) reject_actors = Pleroma.Config.get([:mrf_mention, :actors], [])
recipients = (message["to"] || []) ++ (message["cc"] || []) recipients = (activity["to"] || []) ++ (activity["cc"] || [])
if rejected_mention = if rejected_mention =
Enum.find(recipients, fn recipient -> Enum.member?(reject_actors, recipient) end) do Enum.find(recipients, fn recipient -> Enum.member?(reject_actors, recipient) end) do
{:reject, "[MentionPolicy] Rejected for mention of #{rejected_mention}"} {:reject, "[MentionPolicy] Rejected for mention of #{rejected_mention}"}
else else
{:ok, message} {:ok, activity}
end end
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
@ -32,7 +32,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
key: :mrf_mention, key: :mrf_mention,
related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy", related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
label: "MRF Mention", label: "MRF Mention",
description: "Block messages which mention a specific user", description: "Block activities which mention a specific user",
children: [ children: [
%{ %{
key: :actors, key: :actors,

View file

@ -9,20 +9,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
@impl true @impl true
def filter(%{"actor" => actor} = object) do def filter(%{"actor" => actor} = activity) do
with true <- local?(actor), with true <- local?(actor),
true <- eligible_type?(object), true <- eligible_type?(activity),
true <- note?(object), true <- note?(activity),
false <- has_attachment?(object), false <- has_attachment?(activity),
true <- only_mentions?(object) do true <- only_mentions?(activity) do
{:reject, "[NoEmptyPolicy]"} {:reject, "[NoEmptyPolicy]"}
else else
_ -> _ ->
{:ok, object} {:ok, activity}
end end
end end
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
defp local?(actor) do defp local?(actor) do
if actor |> String.starts_with?("#{Endpoint.url()}") do if actor |> String.starts_with?("#{Endpoint.url()}") do

View file

@ -7,8 +7,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(object) do def filter(activity) do
{:ok, object} {:ok, activity}
end end
@impl true @impl true

View file

@ -13,15 +13,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
def filter( def filter(
%{ %{
"type" => type, "type" => type,
"object" => %{"content" => content, "attachment" => _} = _child_object "object" => %{"content" => content, "attachment" => _} = _object
} = object } = activity
) )
when type in ["Create", "Update"] and content in [".", "<p>.</p>"] do when type in ["Create", "Update"] and content in [".", "<p>.</p>"] do
{:ok, put_in(object, ["object", "content"], "")} {:ok, put_in(activity, ["object", "content"], "")}
end end
@impl true @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -12,20 +12,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
def history_awareness, do: :auto def history_awareness, do: :auto
@impl true @impl true
def filter(%{"type" => type, "object" => child_object} = object) def filter(%{"type" => type, "object" => object} = activity)
when type in ["Create", "Update"] do when type in ["Create", "Update"] do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy]) scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
content = content =
child_object["content"] object["content"]
|> HTML.filter_tags(scrub_policy) |> HTML.filter_tags(scrub_policy)
object = put_in(object, ["object", "content"], content) activity = put_in(activity, ["object", "content"], content)
{:ok, object} {:ok, activity}
end end
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -122,52 +122,52 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do
end end
end end
def check_object_nsfw(%{"object" => %{} = child_object} = object) do def check_object_nsfw(%{"object" => %{} = object} = activity) do
case check_object_nsfw(child_object) do case check_object_nsfw(object) do
{:sfw, _} -> {:sfw, object} {:sfw, _} -> {:sfw, activity}
{:nsfw, _} -> {:nsfw, object} {:nsfw, _} -> {:nsfw, activity}
end end
end end
def check_object_nsfw(object), do: {:sfw, object} def check_object_nsfw(object), do: {:sfw, object}
@impl true @impl true
def filter(object) do def filter(activity) do
with {:sfw, object} <- check_object_nsfw(object) do with {:sfw, activity} <- check_object_nsfw(activity) do
{:ok, object} {:ok, activity}
else else
{:nsfw, _data} -> handle_nsfw(object) {:nsfw, _data} -> handle_nsfw(activity)
end end
end end
defp handle_nsfw(object) do defp handle_nsfw(activity) do
if Config.get([@policy, :reject]) do if Config.get([@policy, :reject]) do
{:reject, object} {:reject, activity}
else else
{:ok, {:ok,
object activity
|> maybe_unlist() |> maybe_unlist()
|> maybe_mark_sensitive()} |> maybe_mark_sensitive()}
end end
end end
defp maybe_unlist(object) do defp maybe_unlist(activity) do
if Config.get([@policy, :unlist]) do if Config.get([@policy, :unlist]) do
unlist(object) unlist(activity)
else else
object activity
end end
end end
defp maybe_mark_sensitive(object) do defp maybe_mark_sensitive(activity) do
if Config.get([@policy, :mark_sensitive]) do if Config.get([@policy, :mark_sensitive]) do
mark_sensitive(object) mark_sensitive(activity)
else else
object activity
end end
end end
def unlist(%{"to" => to, "cc" => cc, "actor" => actor} = object) do def unlist(%{"to" => to, "cc" => cc, "actor" => actor} = activity) do
with %User{} = user <- User.get_cached_by_ap_id(actor) do with %User{} = user <- User.get_cached_by_ap_id(actor) do
to = to =
[user.follower_address | to] [user.follower_address | to]
@ -179,7 +179,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do
|> List.delete(user.follower_address) |> List.delete(user.follower_address)
|> Enum.uniq() |> Enum.uniq()
object activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
else else
@ -187,14 +187,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do
end end
end end
def mark_sensitive(%{"object" => child_object} = object) when is_map(child_object) do def mark_sensitive(%{"object" => object} = activity) when is_map(object) do
Map.put(object, "object", mark_sensitive(child_object)) Map.put(activity, "object", mark_sensitive(object))
end end
def mark_sensitive(object) when is_map(object) do def mark_sensitive(activity) when is_map(activity) do
tags = (object["tag"] || []) ++ ["nsfw"] tags = (activity["tag"] || []) ++ ["nsfw"]
object activity
|> Map.put("tag", tags) |> Map.put("tag", tags)
|> Map.put("sensitive", true) |> Map.put("sensitive", true)
end end

View file

@ -11,12 +11,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
@moduledoc "Filter activities depending on their age" @moduledoc "Filter activities depending on their age"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp check_date(%{"object" => %{"published" => published}} = message) do defp check_date(%{"object" => %{"published" => published}} = activity) do
with %DateTime{} = now <- DateTime.utc_now(), with %DateTime{} = now <- DateTime.utc_now(),
{:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published), {:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published),
max_ttl <- Config.get([:mrf_object_age, :threshold]), max_ttl <- Config.get([:mrf_object_age, :threshold]),
{:ttl, false} <- {:ttl, DateTime.diff(now, then) > max_ttl} do {:ttl, false} <- {:ttl, DateTime.diff(now, then) > max_ttl} do
{:ok, message} {:ok, activity}
else else
{:ttl, true} -> {:ttl, true} ->
{:reject, nil} {:reject, nil}
@ -26,73 +26,73 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
end end
end end
defp check_reject(message, actions) do defp check_reject(activity, actions) do
if :reject in actions do if :reject in actions do
{:reject, "[ObjectAgePolicy]"} {:reject, "[ObjectAgePolicy]"}
else else
{:ok, message} {:ok, activity}
end end
end end
defp check_delist(message, actions) do defp check_delist(activity, actions) do
if :delist in actions do if :delist in actions do
with %User{} = user <- User.get_cached_by_ap_id(message["actor"]) do with %User{} = user <- User.get_cached_by_ap_id(activity["actor"]) do
to = to =
List.delete(message["to"] || [], Pleroma.Constants.as_public()) ++ List.delete(activity["to"] || [], Pleroma.Constants.as_public()) ++
[user.follower_address] [user.follower_address]
cc = cc =
List.delete(message["cc"] || [], user.follower_address) ++ List.delete(activity["cc"] || [], user.follower_address) ++
[Pleroma.Constants.as_public()] [Pleroma.Constants.as_public()]
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
|> Kernel.put_in(["object", "to"], to) |> Kernel.put_in(["object", "to"], to)
|> Kernel.put_in(["object", "cc"], cc) |> Kernel.put_in(["object", "cc"], cc)
{:ok, message} {:ok, activity}
else else
_e -> _e ->
{:reject, "[ObjectAgePolicy] Unhandled error"} {:reject, "[ObjectAgePolicy] Unhandled error"}
end end
else else
{:ok, message} {:ok, activity}
end end
end end
defp check_strip_followers(message, actions) do defp check_strip_followers(activity, actions) do
if :strip_followers in actions do if :strip_followers in actions do
with %User{} = user <- User.get_cached_by_ap_id(message["actor"]) do with %User{} = user <- User.get_cached_by_ap_id(activity["actor"]) do
to = List.delete(message["to"] || [], user.follower_address) to = List.delete(activity["to"] || [], user.follower_address)
cc = List.delete(message["cc"] || [], user.follower_address) cc = List.delete(activity["cc"] || [], user.follower_address)
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
|> Kernel.put_in(["object", "to"], to) |> Kernel.put_in(["object", "to"], to)
|> Kernel.put_in(["object", "cc"], cc) |> Kernel.put_in(["object", "cc"], cc)
{:ok, message} {:ok, activity}
else else
_e -> _e ->
{:reject, "[ObjectAgePolicy] Unhandled error"} {:reject, "[ObjectAgePolicy] Unhandled error"}
end end
else else
{:ok, message} {:ok, activity}
end end
end end
@impl true @impl true
def filter(%{"type" => "Create", "object" => %{"published" => _}} = message) do def filter(%{"type" => "Create", "object" => %{"published" => _}} = activity) do
with actions <- Config.get([:mrf_object_age, :actions]), with actions <- Config.get([:mrf_object_age, :actions]),
{:reject, _} <- check_date(message), {:reject, _} <- check_date(activity),
{:ok, message} <- check_reject(message, actions), {:ok, activity} <- check_reject(activity, actions),
{:ok, message} <- check_delist(message, actions), {:ok, activity} <- check_delist(activity, actions),
{:ok, message} <- check_strip_followers(message, actions) do {:ok, activity} <- check_strip_followers(activity, actions) do
{:ok, message} {:ok, activity}
else else
# check_date() is allowed to short-circuit the pipeline # check_date() is allowed to short-circuit the pipeline
e -> e e -> e
@ -100,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe do def describe do
@ -131,8 +131,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
type: {:list, :atom}, type: {:list, :atom},
description: description:
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <> "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
"`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines, additionally for followers-only it degrades to a direct message; " <> "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines, additionally for followers-only it degrades to a direct activity; " <>
"`:reject` rejects the message entirely", "`:reject` rejects the activity entirely",
suggestions: [:delist, :strip_followers, :reject] suggestions: [:delist, :strip_followers, :reject]
} }
] ]

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.Policy do defmodule Pleroma.Web.ActivityPub.MRF.Policy do
@callback filter(map()) :: {:ok | :reject, map()} @callback filter(Pleroma.Activity.t()) :: {:ok | :reject, Pleroma.Activity.t()}
@callback describe() :: {:ok | :error, map()} @callback describe() :: {:ok | :error, map()}
@callback config_description() :: %{ @callback config_description() :: %{
optional(:children) => [map()], optional(:children) => [map()],

View file

@ -26,7 +26,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuietReply do
"type" => "Note", "type" => "Note",
"inReplyTo" => in_reply_to "inReplyTo" => in_reply_to
} }
} = object } = activity
) do ) do
with true <- is_binary(in_reply_to), with true <- is_binary(in_reply_to),
false <- match?([], cc), false <- match?([], cc),
@ -39,21 +39,21 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuietReply do
updated_cc = [Pleroma.Constants.as_public()] updated_cc = [Pleroma.Constants.as_public()]
updated_object = updated_activity =
object activity
|> Map.put("to", updated_to) |> Map.put("to", updated_to)
|> Map.put("cc", updated_cc) |> Map.put("cc", updated_cc)
|> put_in(["object", "to"], updated_to) |> put_in(["object", "to"], updated_to)
|> put_in(["object", "cc"], updated_cc) |> put_in(["object", "cc"], updated_cc)
{:ok, updated_object} {:ok, updated_activity}
else else
_ -> {:ok, object} _ -> {:ok, activity}
end end
end end
@impl true @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -10,18 +10,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do
require Pleroma.Constants require Pleroma.Constants
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def filter(%{"object" => %{"quoteUrl" => _} = object} = activity) do def filter(%{"object" => %{"quoteUrl" => _} = object} = activity) do
{:ok, Map.put(activity, "object", filter_object(object))} {:ok, Map.put(activity, "object", filter_object(object))}
end end
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
@impl Pleroma.Web.ActivityPub.MRF.Policy @impl true
def history_awareness, do: :auto def history_awareness, do: :auto
defp filter_object(%{"quoteUrl" => quote_url} = object) do defp filter_object(%{"quoteUrl" => quote_url} = object) do

View file

@ -13,20 +13,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
require Pleroma.Constants require Pleroma.Constants
defp check_accept(%{host: actor_host} = _actor_info, object) do defp check_accept(%{host: actor_host} = _actor_info, activity) do
accepts = accepts =
instance_list(:accept) instance_list(:accept)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
cond do cond do
accepts == [] -> {:ok, object} accepts == [] -> {:ok, activity}
actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object} actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, activity}
MRF.subdomain_match?(accepts, actor_host) -> {:ok, object} MRF.subdomain_match?(accepts, actor_host) -> {:ok, activity}
true -> {:reject, "[SimplePolicy] host not in accept list"} true -> {:reject, "[SimplePolicy] host not in accept list"}
end end
end end
defp check_reject(%{host: actor_host} = _actor_info, object) do defp check_reject(%{host: actor_host} = _actor_info, activity) do
rejects = rejects =
instance_list(:reject) instance_list(:reject)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
@ -34,109 +34,109 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
if MRF.subdomain_match?(rejects, actor_host) do if MRF.subdomain_match?(rejects, actor_host) do
{:reject, "[SimplePolicy] host in reject list"} {:reject, "[SimplePolicy] host in reject list"}
else else
{:ok, object} {:ok, activity}
end end
end end
defp check_media_removal( defp check_media_removal(
%{host: actor_host} = _actor_info, %{host: actor_host} = _actor_info,
%{"type" => type, "object" => %{"attachment" => child_attachment}} = object %{"type" => type, "object" => %{"attachment" => object_attachment}} = activity
) )
when length(child_attachment) > 0 and type in ["Create", "Update"] do when length(object_attachment) > 0 and type in ["Create", "Update"] do
media_removal = media_removal =
instance_list(:media_removal) instance_list(:media_removal)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
object = activity =
if MRF.subdomain_match?(media_removal, actor_host) do if MRF.subdomain_match?(media_removal, actor_host) do
child_object = Map.delete(object["object"], "attachment") object = Map.delete(activity["object"], "attachment")
Map.put(object, "object", child_object) Map.put(activity, "object", object)
else else
object activity
end end
{:ok, object} {:ok, activity}
end end
defp check_media_removal(_actor_info, object), do: {:ok, object} defp check_media_removal(_actor_info, activity), do: {:ok, activity}
defp check_media_nsfw( defp check_media_nsfw(
%{host: actor_host} = _actor_info, %{host: actor_host} = _actor_info,
%{ %{
"type" => type, "type" => type,
"object" => %{} = _child_object "object" => %{} = _object
} = object } = activity
) )
when type in ["Create", "Update"] do when type in ["Create", "Update"] do
media_nsfw = media_nsfw =
instance_list(:media_nsfw) instance_list(:media_nsfw)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
object = activity =
if MRF.subdomain_match?(media_nsfw, actor_host) do if MRF.subdomain_match?(media_nsfw, actor_host) do
Kernel.put_in(object, ["object", "sensitive"], true) Kernel.put_in(activity, ["object", "sensitive"], true)
else else
object activity
end end
{:ok, object} {:ok, activity}
end end
defp check_media_nsfw(_actor_info, object), do: {:ok, object} defp check_media_nsfw(_actor_info, activity), do: {:ok, activity}
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do defp check_ftl_removal(%{host: actor_host} = _actor_info, activity) do
timeline_removal = timeline_removal =
instance_list(:federated_timeline_removal) instance_list(:federated_timeline_removal)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
object = activity =
with true <- MRF.subdomain_match?(timeline_removal, actor_host), with true <- MRF.subdomain_match?(timeline_removal, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]), user <- User.get_cached_by_ap_id(activity["actor"]),
true <- Pleroma.Constants.as_public() in object["to"] do true <- Pleroma.Constants.as_public() in activity["to"] do
to = List.delete(object["to"], Pleroma.Constants.as_public()) ++ [user.follower_address] to = List.delete(activity["to"], Pleroma.Constants.as_public()) ++ [user.follower_address]
cc = List.delete(object["cc"], user.follower_address) ++ [Pleroma.Constants.as_public()] cc = List.delete(activity["cc"], user.follower_address) ++ [Pleroma.Constants.as_public()]
object activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
else else
_ -> object _ -> activity
end end
{:ok, object} {:ok, activity}
end end
defp intersection(list1, list2) do defp intersection(list1, list2) do
list1 -- list1 -- list2 list1 -- list1 -- list2
end end
defp check_followers_only(%{host: actor_host} = _actor_info, object) do defp check_followers_only(%{host: actor_host} = _actor_info, activity) do
followers_only = followers_only =
instance_list(:followers_only) instance_list(:followers_only)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
object = activity =
with true <- MRF.subdomain_match?(followers_only, actor_host), with true <- MRF.subdomain_match?(followers_only, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]) do user <- User.get_cached_by_ap_id(activity["actor"]) do
# Don't use Map.get/3 intentionally, these must not be nil # Don't use Map.get/3 intentionally, these must not be nil
fixed_to = object["to"] || [] fixed_to = activity["to"] || []
fixed_cc = object["cc"] || [] fixed_cc = activity["cc"] || []
to = FollowingRelationship.followers_ap_ids(user, fixed_to) to = FollowingRelationship.followers_ap_ids(user, fixed_to)
cc = FollowingRelationship.followers_ap_ids(user, fixed_cc) cc = FollowingRelationship.followers_ap_ids(user, fixed_cc)
object activity
|> Map.put("to", intersection([user.follower_address | to], fixed_to)) |> Map.put("to", intersection([user.follower_address | to], fixed_to))
|> Map.put("cc", intersection([user.follower_address | cc], fixed_cc)) |> Map.put("cc", intersection([user.follower_address | cc], fixed_cc))
else else
_ -> object _ -> activity
end end
{:ok, object} {:ok, activity}
end end
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = activity) do
report_removal = report_removal =
instance_list(:report_removal) instance_list(:report_removal)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
@ -144,39 +144,39 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
if MRF.subdomain_match?(report_removal, actor_host) do if MRF.subdomain_match?(report_removal, actor_host) do
{:reject, "[SimplePolicy] host in report_removal list"} {:reject, "[SimplePolicy] host in report_removal list"}
else else
{:ok, object} {:ok, activity}
end end
end end
defp check_report_removal(_actor_info, object), do: {:ok, object} defp check_report_removal(_actor_info, activity), do: {:ok, activity}
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = activity) do
avatar_removal = avatar_removal =
instance_list(:avatar_removal) instance_list(:avatar_removal)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
if MRF.subdomain_match?(avatar_removal, actor_host) do if MRF.subdomain_match?(avatar_removal, actor_host) do
{:ok, Map.delete(object, "icon")} {:ok, Map.delete(activity, "icon")}
else else
{:ok, object} {:ok, activity}
end end
end end
defp check_avatar_removal(_actor_info, object), do: {:ok, object} defp check_avatar_removal(_actor_info, activity), do: {:ok, activity}
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = activity) do
banner_removal = banner_removal =
instance_list(:banner_removal) instance_list(:banner_removal)
|> MRF.subdomains_regex() |> MRF.subdomains_regex()
if MRF.subdomain_match?(banner_removal, actor_host) do if MRF.subdomain_match?(banner_removal, actor_host) do
{:ok, Map.delete(object, "image")} {:ok, Map.delete(activity, "image")}
else else
{:ok, object} {:ok, activity}
end end
end end
defp check_banner_removal(_actor_info, object), do: {:ok, object} defp check_banner_removal(_actor_info, activity), do: {:ok, activity}
defp check_object(%{"object" => object} = activity) do defp check_object(%{"object" => object} = activity) do
with {:ok, _object} <- filter(object) do with {:ok, _object} <- filter(object) do
@ -184,7 +184,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
end end
end end
defp check_object(object), do: {:ok, object} defp check_object(activity), do: {:ok, activity}
defp instance_list(config_key) do defp instance_list(config_key) do
Config.get([:mrf_simple, config_key]) Config.get([:mrf_simple, config_key])
@ -192,7 +192,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
end end
@impl true @impl true
def filter(%{"type" => "Delete", "actor" => actor} = object) do def filter(%{"type" => "Delete", "actor" => actor} = activity) do
%{host: actor_host} = URI.parse(actor) %{host: actor_host} = URI.parse(actor)
reject_deletes = reject_deletes =
@ -202,54 +202,54 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
if MRF.subdomain_match?(reject_deletes, actor_host) do if MRF.subdomain_match?(reject_deletes, actor_host) do
{:reject, "[SimplePolicy] host in reject_deletes list"} {:reject, "[SimplePolicy] host in reject_deletes list"}
else else
{:ok, object} {:ok, activity}
end end
end end
@impl true @impl true
def filter(%{"actor" => actor} = object) do def filter(%{"actor" => actor} = activity) do
actor_info = URI.parse(actor) actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object), with {:ok, activity} <- check_accept(actor_info, activity),
{:ok, object} <- check_reject(actor_info, object), {:ok, activity} <- check_reject(actor_info, activity),
{:ok, object} <- check_media_removal(actor_info, object), {:ok, activity} <- check_media_removal(actor_info, activity),
{:ok, object} <- check_media_nsfw(actor_info, object), {:ok, activity} <- check_media_nsfw(actor_info, activity),
{:ok, object} <- check_ftl_removal(actor_info, object), {:ok, activity} <- check_ftl_removal(actor_info, activity),
{:ok, object} <- check_followers_only(actor_info, object), {:ok, activity} <- check_followers_only(actor_info, activity),
{:ok, object} <- check_report_removal(actor_info, object), {:ok, activity} <- check_report_removal(actor_info, activity),
{:ok, object} <- check_object(object) do {:ok, activity} <- check_object(activity) do
{:ok, object} {:ok, activity}
else else
{:reject, _} = e -> e {:reject, _} = e -> e
end end
end end
def filter(%{"id" => actor, "type" => obj_type} = object) def filter(%{"id" => actor, "type" => actor_type} = activity)
when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do when actor_type in ["Application", "Group", "Organization", "Person", "Service"] do
actor_info = URI.parse(actor) actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object), with {:ok, activity} <- check_accept(actor_info, activity),
{:ok, object} <- check_reject(actor_info, object), {:ok, activity} <- check_reject(actor_info, activity),
{:ok, object} <- check_avatar_removal(actor_info, object), {:ok, activity} <- check_avatar_removal(actor_info, activity),
{:ok, object} <- check_banner_removal(actor_info, object) do {:ok, activity} <- check_banner_removal(actor_info, activity) do
{:ok, object} {:ok, activity}
else else
{:reject, _} = e -> e {:reject, _} = e -> e
end end
end end
def filter(object) when is_binary(object) do def filter(activity) when is_binary(activity) do
uri = URI.parse(object) uri = URI.parse(activity)
with {:ok, object} <- check_accept(uri, object), with {:ok, activity} <- check_accept(uri, activity),
{:ok, object} <- check_reject(uri, object) do {:ok, activity} <- check_reject(uri, activity) do
{:ok, object} {:ok, activity}
else else
{:reject, _} = e -> e {:reject, _} = e -> e
end end
end end
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe do def describe do

View file

@ -62,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
end end
@impl true @impl true
def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = message) do def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = activity) do
host = URI.parse(actor).host host = URI.parse(actor).host
if host != Pleroma.Web.Endpoint.host() and accept_host?(host) do if host != Pleroma.Web.Endpoint.host() and accept_host?(host) do
@ -97,10 +97,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
end end
end end
{:ok, message} {:ok, activity}
end end
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
@spec config_description :: %{ @spec config_description :: %{

View file

@ -20,20 +20,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
end end
@impl true @impl true
def filter(%{"actor" => actor} = message) do def filter(%{"actor" => actor} = activity) do
with {:ok, match, subchain} <- lookup_subchain(actor) do with {:ok, match, subchain} <- lookup_subchain(actor) do
Logger.debug( Logger.debug(
"[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{inspect(subchain)}" "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{inspect(subchain)}"
) )
MRF.filter(subchain, message) MRF.filter(subchain, activity)
else else
_e -> {:ok, message} _e -> {:ok, activity}
end end
end end
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
@ -45,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy", related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
label: "MRF Subchain", label: "MRF Subchain",
description: description:
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <> "This policy processes activities through an alternate pipeline when a given activity matches certain criteria." <>
" All criteria are configured as a map of regular expressions to lists of policy modules.", " All criteria are configured as a map of regular expressions to lists of policy modules.",
children: [ children: [
%{ %{

View file

@ -28,25 +28,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
"mrf_tag:media-force-nsfw", "mrf_tag:media-force-nsfw",
%{ %{
"type" => type, "type" => type,
"object" => %{"attachment" => child_attachment} "object" => %{"attachment" => object_attachment}
} = message } = activity
) )
when length(child_attachment) > 0 and type in ["Create", "Update"] do when length(object_attachment) > 0 and type in ["Create", "Update"] do
{:ok, Kernel.put_in(message, ["object", "sensitive"], true)} {:ok, Kernel.put_in(activity, ["object", "sensitive"], true)}
end end
defp process_tag( defp process_tag(
"mrf_tag:media-strip", "mrf_tag:media-strip",
%{ %{
"type" => type, "type" => type,
"object" => %{"attachment" => child_attachment} = object "object" => %{"attachment" => object_attachment} = object
} = message } = activity
) )
when length(child_attachment) > 0 and type in ["Create", "Update"] do when length(object_attachment) > 0 and type in ["Create", "Update"] do
object = Map.delete(object, "attachment") object = Map.delete(object, "attachment")
message = Map.put(message, "object", object) activity = Map.put(activity, "object", object)
{:ok, message} {:ok, activity}
end end
defp process_tag( defp process_tag(
@ -57,7 +57,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
"cc" => cc, "cc" => cc,
"actor" => actor, "actor" => actor,
"object" => object "object" => object
} = message } = activity
) do ) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
@ -70,15 +70,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
|> Map.put("object", object) |> Map.put("object", object)
{:ok, message} {:ok, activity}
else else
{:ok, message} {:ok, activity}
end end
end end
@ -90,7 +90,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
"cc" => cc, "cc" => cc,
"actor" => actor, "actor" => actor,
"object" => object "object" => object
} = message } = activity
) do ) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
@ -104,26 +104,26 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
message = activity =
message activity
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
|> Map.put("object", object) |> Map.put("object", object)
{:ok, message} {:ok, activity}
else else
{:ok, message} {:ok, activity}
end end
end end
defp process_tag( defp process_tag(
"mrf_tag:disable-remote-subscription", "mrf_tag:disable-remote-subscription",
%{"type" => "Follow", "actor" => actor} = message %{"type" => "Follow", "actor" => actor} = activity
) do ) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
if user.local == true do if user.local == true do
{:ok, message} {:ok, activity}
else else
{:reject, {:reject,
"[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-remote-subscription"} "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-remote-subscription"}
@ -133,14 +133,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow", "actor" => actor}), defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow", "actor" => actor}),
do: {:reject, "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-any-subscription"} do: {:reject, "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-any-subscription"}
defp process_tag(_, message), do: {:ok, message} defp process_tag(_, activity), do: {:ok, activity}
def filter_message(actor, message) do def filter_activity(actor, activity) do
User.get_cached_by_ap_id(actor) User.get_cached_by_ap_id(actor)
|> get_tags() |> get_tags()
|> Enum.reduce({:ok, message}, fn |> Enum.reduce({:ok, activity}, fn
tag, {:ok, message} -> tag, {:ok, activity} ->
process_tag(tag, message) process_tag(tag, activity)
_, error -> _, error ->
error error
@ -148,15 +148,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
end end
@impl true @impl true
def filter(%{"object" => target_actor, "type" => "Follow"} = message), def filter(%{"object" => target_actor, "type" => "Follow"} = activity),
do: filter_message(target_actor, message) do: filter_activity(target_actor, activity)
@impl true @impl true
def filter(%{"actor" => actor, "type" => type} = message) when type in ["Create", "Update"], def filter(%{"actor" => actor, "type" => type} = activity) when type in ["Create", "Update"],
do: filter_message(actor, message) do: filter_activity(actor, activity)
@impl true @impl true
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View file

@ -8,18 +8,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
@moduledoc "Accept-list of users from specified instances" @moduledoc "Accept-list of users from specified instances"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp filter_by_list(object, []), do: {:ok, object} defp filter_by_list(activity, []), do: {:ok, activity}
defp filter_by_list(%{"actor" => actor} = object, allow_list) do defp filter_by_list(%{"actor" => actor} = activity, allow_list) do
if actor in allow_list do if actor in allow_list do
{:ok, object} {:ok, activity}
else else
{:reject, "[UserAllowListPolicy] #{actor} not in the list"} {:reject, "[UserAllowListPolicy] #{actor} not in the list"}
end end
end end
@impl true @impl true
def filter(%{"actor" => actor} = object) do def filter(%{"actor" => actor} = activity) do
actor_info = URI.parse(actor) actor_info = URI.parse(actor)
allow_list = allow_list =
@ -28,10 +28,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
[] []
) )
filter_by_list(object, allow_list) filter_by_list(activity, allow_list)
end end
def filter(object), do: {:ok, object} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe do def describe do

View file

@ -3,38 +3,38 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
@moduledoc "Filter messages which belong to certain activity vocabularies" @moduledoc "Filter activities which belong to certain activity vocabularies"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(%{"type" => "Undo", "object" => child_message} = message) do def filter(%{"type" => "Undo", "object" => object} = activity) do
with {:ok, _} <- filter(child_message) do with {:ok, _} <- filter(object) do
{:ok, message} {:ok, activity}
else else
{:reject, _} = e -> e {:reject, _} = e -> e
end end
end end
def filter(%{"type" => message_type} = message) do def filter(%{"type" => activity_type} = activity) do
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]), with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]), rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
{_, true} <- {_, true} <-
{:accepted, {:accepted,
Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, message_type)}, Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, activity_type)},
{_, false} <- {_, false} <-
{:rejected, {:rejected,
length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type)}, length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, activity_type)},
{:ok, _} <- filter(message["object"]) do {:ok, _} <- filter(activity["object"]) do
{:ok, message} {:ok, activity}
else else
{:reject, _} = e -> e {:reject, _} = e -> e
{:accepted, _} -> {:reject, "[VocabularyPolicy] #{message_type} not in accept list"} {:accepted, _} -> {:reject, "[VocabularyPolicy] #{activity_type} not in accept list"}
{:rejected, _} -> {:reject, "[VocabularyPolicy] #{message_type} in reject list"} {:rejected, _} -> {:reject, "[VocabularyPolicy] #{activity_type} in reject list"}
end end
end end
def filter(message), do: {:ok, message} def filter(activity), do: {:ok, activity}
@impl true @impl true
def describe, def describe,
@ -46,20 +46,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
key: :mrf_vocabulary, key: :mrf_vocabulary,
related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy", related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
label: "MRF Vocabulary", label: "MRF Vocabulary",
description: "Filter messages which belong to certain activity vocabularies", description: "Filter activities which belong to certain activity vocabularies",
children: [ children: [
%{ %{
key: :accept, key: :accept,
type: {:list, :string}, type: {:list, :string},
description: description:
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.", "A list of ActivityStreams terms to accept. If empty, all supported activities are accepted.",
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
}, },
%{ %{
key: :reject, key: :reject,
type: {:list, :string}, type: {:list, :string},
description: description:
"A list of ActivityStreams terms to reject. If empty, no messages are rejected.", "A list of ActivityStreams terms to reject. If empty, no activities are rejected.",
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
} }
] ]