Page MenuHomePhorge

No OneTemporary

Size
81 KB
Referenced Files
None
Subscribers
None
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 e78254280..5f412566d 100644
--- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
@@ -1,61 +1,66 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
- @moduledoc "Adds expiration to all local Create activities"
+ @moduledoc "Adds expiration to all local Create/Update activities"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
def filter(activity) do
activity =
if note?(activity) and local?(activity) do
maybe_add_expiration(activity)
else
activity
end
{:ok, activity}
end
@impl true
def describe, do: {:ok, %{}}
defp local?(%{"actor" => actor}) do
String.starts_with?(actor, Pleroma.Web.Endpoint.url())
end
- defp note?(activity) do
- match?(%{"type" => "Create", "object" => %{"type" => "Note"}}, activity)
+ defp note?(%{"type" => type, "object" => %{"type" => "Note"}})
+ when type in ["Create", "Update"] do
+ true
+ end
+
+ defp note?(_) do
+ false
end
defp maybe_add_expiration(activity) do
days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365)
expires_at = DateTime.utc_now() |> Timex.shift(days: days)
with %{"expires_at" => existing_expires_at} <- activity,
:lt <- DateTime.compare(existing_expires_at, expires_at) do
activity
else
_ -> Map.put(activity, "expires_at", expires_at)
end
end
@impl true
def config_description do
%{
key: :mrf_activity_expiration,
related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
label: "MRF Activity Expiration Policy",
description: "Adds automatic expiration to all local activities",
children: [
%{
key: :days,
type: :integer,
description: "Default global expiration time for all local activities (in days)",
suggestions: [90, 365]
}
]
}
end
end
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 ba7c8400b..6885df863 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
@@ -1,60 +1,61 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Logger
@impl true
def history_awareness, do: :auto
# has the user successfully posted before?
defp old_user?(%User{} = u) do
u.note_count > 0 || u.follower_count > 0
end
# does the post contain links?
defp contains_links?(%{"content" => content} = _object) do
content
|> Floki.parse_fragment!()
|> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"],a.zrl")
|> Floki.attribute("a", "href")
|> length() > 0
end
defp contains_links?(_), do: false
@impl true
- def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do
+ def filter(%{"type" => type, "actor" => actor, "object" => object} = message)
+ when type in ["Create", "Update"] do
with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor),
{:contains_links, true} <- {:contains_links, contains_links?(object)},
{:old_user, true} <- {:old_user, old_user?(u)} do
{:ok, message}
else
{:ok, %User{local: true}} ->
{:ok, message}
{:contains_links, false} ->
{:ok, message}
{:old_user, false} ->
{:reject, "[AntiLinkSpamPolicy] User has no posts nor followers"}
{:error, _} ->
{:reject, "[AntiLinkSpamPolicy] Failed to get or fetch user by ap_id"}
e ->
{:reject, "[AntiLinkSpamPolicy] Unhandled error #{inspect(e)}"}
end
end
# in all other cases, pass through
def filter(message), do: {:ok, message}
@impl true
def describe, do: {:ok, %{}}
end
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 11871375e..fa6b93333 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
@@ -1,56 +1,57 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# 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"
require Pleroma.Constants
defp check_by_actor_type(user), do: user.actor_type in ["Application", "Service"]
defp check_by_nickname(user), do: Regex.match?(~r/bot@|ebooks@/i, user.nickname)
defp check_if_bot(user), do: check_by_actor_type(user) or check_by_nickname(user)
@impl true
def filter(
%{
- "type" => "Create",
+ "type" => type,
"to" => to,
"cc" => cc,
"actor" => actor,
"object" => object
} = message
- ) do
+ )
+ when type in ["Create", "Update"] do
user = User.get_cached_by_ap_id(actor)
isbot = check_if_bot(user)
if isbot and Enum.member?(to, Pleroma.Constants.as_public()) do
to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
object =
object
|> Map.put("to", to)
|> Map.put("cc", cc)
message =
message
|> Map.put("to", to)
|> Map.put("cc", cc)
|> Map.put("object", object)
{:ok, message}
else
{:ok, message}
end
end
@impl true
def filter(message), do: {:ok, message}
@impl true
def describe, do: {:ok, %{}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
index 504bd4d57..18704fc2c 100644
--- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
@@ -1,127 +1,127 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
alias Pleroma.User
require Pleroma.Constants
@moduledoc "Block messages with too much mentions (configurable)"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp delist_message(message, threshold) when threshold > 0 do
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
to = message["to"] || []
cc = message["cc"] || []
follower_collection? = Enum.member?(to ++ cc, follower_collection)
message =
case get_recipient_count(message) do
{:public, recipients}
when follower_collection? and recipients > threshold ->
message
|> Map.put("to", [follower_collection])
|> Map.put("cc", [Pleroma.Constants.as_public()])
{:public, recipients} when recipients > threshold ->
message
|> Map.put("to", [])
|> Map.put("cc", [Pleroma.Constants.as_public()])
_ ->
message
end
{:ok, message}
end
defp delist_message(message, _threshold), do: {:ok, message}
defp reject_message(message, threshold) when threshold > 0 do
with {_, recipients} <- get_recipient_count(message) do
if recipients > threshold do
{:reject, "[HellthreadPolicy] #{recipients} recipients is over the limit of #{threshold}"}
else
{:ok, message}
end
end
end
defp reject_message(message, _threshold), do: {:ok, message}
defp get_recipient_count(message) do
recipients = (message["to"] || []) ++ (message["cc"] || [])
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
if Enum.member?(recipients, Pleroma.Constants.as_public()) do
recipients =
recipients
|> List.delete(Pleroma.Constants.as_public())
|> List.delete(follower_collection)
{:public, length(recipients)}
else
recipients =
recipients
|> List.delete(follower_collection)
{:not_public, length(recipients)}
end
end
@impl true
- def filter(%{"type" => "Create", "object" => %{"type" => object_type}} = message)
- when object_type in ~w{Note Article} do
+ def filter(%{"type" => type, "object" => %{"type" => object_type}} = message)
+ when type in ~w{Create Update} and object_type in ~w{Note Article} do
reject_threshold =
Pleroma.Config.get(
[:mrf_hellthread, :reject_threshold],
Pleroma.Config.get([:mrf_hellthread, :threshold])
)
delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold])
with {:ok, message} <- reject_message(message, reject_threshold),
{:ok, message} <- delist_message(message, delist_threshold) do
{:ok, message}
else
e -> e
end
end
@impl true
def filter(message), do: {:ok, message}
@impl true
def describe,
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
@impl true
def config_description do
%{
key: :mrf_hellthread,
related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
label: "MRF Hellthread",
description: "Block messages with excessive user mentions",
children: [
%{
key: :delist_threshold,
type: :integer,
description:
"Number of mentioned users after which the message gets removed from timelines and" <>
"disables notifications. Set to 0 to disable.",
suggestions: [10]
},
%{
key: :reject_threshold,
type: :integer,
description:
"Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
suggestions: [20]
}
]
}
end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
index 05b28e4f5..11e9bd478 100644
--- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
@@ -1,46 +1,46 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
@moduledoc "Block messages which mention a user"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
- def filter(%{"type" => "Create"} = message) do
+ def filter(%{"type" => type} = message) when type in ["Create", "Update"] do
reject_actors = Pleroma.Config.get([:mrf_mention, :actors], [])
recipients = (message["to"] || []) ++ (message["cc"] || [])
if rejected_mention =
Enum.find(recipients, fn recipient -> Enum.member?(reject_actors, recipient) end) do
{:reject, "[MentionPolicy] Rejected for mention of #{rejected_mention}"}
else
{:ok, message}
end
end
@impl true
def filter(message), do: {:ok, message}
@impl true
def describe, do: {:ok, %{}}
@impl true
def config_description do
%{
key: :mrf_mention,
related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
label: "MRF Mention",
description: "Block messages which mention a specific user",
children: [
%{
key: :actors,
type: {:list, :string},
description: "A list of actors for which any post mentioning them will be dropped",
suggestions: ["actor1", "actor2"]
}
]
}
end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 415c5d2dd..a59212db4 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -1,396 +1,397 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
@moduledoc "Filter activities depending on their origin instance"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config
alias Pleroma.FollowingRelationship
alias Pleroma.User
alias Pleroma.Web.ActivityPub.MRF
require Pleroma.Constants
defp check_accept(%{host: actor_host} = _actor_info, object) do
accepts =
instance_list(:accept)
|> MRF.subdomains_regex()
cond do
accepts == [] -> {:ok, object}
actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
true -> {:reject, "[SimplePolicy] host not in accept list"}
end
end
defp check_reject(%{host: actor_host} = _actor_info, object) do
rejects =
instance_list(:reject)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(rejects, actor_host) do
{:reject, "[SimplePolicy] host in reject list"}
else
{:ok, object}
end
end
defp check_media_removal(
%{host: actor_host} = _actor_info,
- %{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
+ %{"type" => type, "object" => %{"attachment" => child_attachment}} = object
)
- when length(child_attachment) > 0 do
+ when type in ["Create", "Update"] and length(child_attachment) > 0 do
media_removal =
instance_list(:media_removal)
|> MRF.subdomains_regex()
object =
if MRF.subdomain_match?(media_removal, actor_host) do
child_object = Map.delete(object["object"], "attachment")
Map.put(object, "object", child_object)
else
object
end
{:ok, object}
end
defp check_media_removal(_actor_info, object), do: {:ok, object}
defp check_media_nsfw(
%{host: actor_host} = _actor_info,
%{
- "type" => "Create",
+ "type" => type,
"object" => %{} = _child_object
} = object
- ) do
+ )
+ when type in ["Create", "Update"] do
media_nsfw =
instance_list(:media_nsfw)
|> MRF.subdomains_regex()
object =
if MRF.subdomain_match?(media_nsfw, actor_host) do
Kernel.put_in(object, ["object", "sensitive"], true)
else
object
end
{:ok, object}
end
defp check_media_nsfw(_actor_info, object), do: {:ok, object}
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
timeline_removal =
instance_list(:federated_timeline_removal)
|> MRF.subdomains_regex()
object =
with true <- MRF.subdomain_match?(timeline_removal, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]),
true <- Pleroma.Constants.as_public() in object["to"] do
to = List.delete(object["to"], Pleroma.Constants.as_public()) ++ [user.follower_address]
cc = List.delete(object["cc"], user.follower_address) ++ [Pleroma.Constants.as_public()]
object
|> Map.put("to", to)
|> Map.put("cc", cc)
else
_ -> object
end
{:ok, object}
end
defp intersection(list1, list2) do
list1 -- list1 -- list2
end
defp check_followers_only(%{host: actor_host} = _actor_info, object) do
followers_only =
instance_list(:followers_only)
|> MRF.subdomains_regex()
object =
with true <- MRF.subdomain_match?(followers_only, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]) do
# Don't use Map.get/3 intentionally, these must not be nil
fixed_to = object["to"] || []
fixed_cc = object["cc"] || []
to = FollowingRelationship.followers_ap_ids(user, fixed_to)
cc = FollowingRelationship.followers_ap_ids(user, fixed_cc)
object
|> Map.put("to", intersection([user.follower_address | to], fixed_to))
|> Map.put("cc", intersection([user.follower_address | cc], fixed_cc))
else
_ -> object
end
{:ok, object}
end
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
report_removal =
instance_list(:report_removal)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(report_removal, actor_host) do
{:reject, "[SimplePolicy] host in report_removal list"}
else
{:ok, object}
end
end
defp check_report_removal(_actor_info, object), do: {:ok, object}
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
avatar_removal =
instance_list(:avatar_removal)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(avatar_removal, actor_host) do
{:ok, Map.delete(object, "icon")}
else
{:ok, object}
end
end
defp check_avatar_removal(_actor_info, object), do: {:ok, object}
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
banner_removal =
instance_list(:banner_removal)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(banner_removal, actor_host) do
{:ok, Map.delete(object, "image")}
else
{:ok, object}
end
end
defp check_banner_removal(_actor_info, object), do: {:ok, object}
defp check_object(%{"object" => object} = activity) do
with {:ok, _object} <- filter(object) do
{:ok, activity}
end
end
defp check_object(object), do: {:ok, object}
defp instance_list(config_key) do
Config.get([:mrf_simple, config_key])
|> MRF.instance_list_from_tuples()
end
@impl true
def filter(%{"type" => "Delete", "actor" => actor} = object) do
%{host: actor_host} = URI.parse(actor)
reject_deletes =
instance_list(:reject_deletes)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(reject_deletes, actor_host) do
{:reject, "[SimplePolicy] host in reject_deletes list"}
else
{:ok, object}
end
end
@impl true
def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object),
{:ok, object} <- check_reject(actor_info, object),
{:ok, object} <- check_media_removal(actor_info, object),
{:ok, object} <- check_media_nsfw(actor_info, object),
{:ok, object} <- check_ftl_removal(actor_info, object),
{:ok, object} <- check_followers_only(actor_info, object),
{:ok, object} <- check_report_removal(actor_info, object),
{:ok, object} <- check_object(object) do
{:ok, object}
else
{:reject, nil} -> {:reject, "[SimplePolicy]"}
{:reject, _} = e -> e
_ -> {:reject, "[SimplePolicy]"}
end
end
def filter(%{"id" => actor, "type" => obj_type} = object)
when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do
actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object),
{:ok, object} <- check_reject(actor_info, object),
{:ok, object} <- check_avatar_removal(actor_info, object),
{:ok, object} <- check_banner_removal(actor_info, object) do
{:ok, object}
else
{:reject, nil} -> {:reject, "[SimplePolicy]"}
{:reject, _} = e -> e
_ -> {:reject, "[SimplePolicy]"}
end
end
def filter(object) when is_binary(object) do
uri = URI.parse(object)
with {:ok, object} <- check_accept(uri, object),
{:ok, object} <- check_reject(uri, object) do
{:ok, object}
else
{:reject, nil} -> {:reject, "[SimplePolicy]"}
{:reject, _} = e -> e
_ -> {:reject, "[SimplePolicy]"}
end
end
def filter(object), do: {:ok, object}
defp obfuscate(string) when is_binary(string) do
string
|> to_charlist()
|> Enum.with_index()
|> Enum.map(fn
{?., _index} ->
?.
{char, index} ->
if 3 <= index && index < String.length(string) - 3, do: ?*, else: char
end)
|> to_string()
end
defp maybe_obfuscate(host, obfuscations) do
if MRF.subdomain_match?(obfuscations, host) do
obfuscate(host)
else
host
end
end
@impl true
def describe do
exclusions = Config.get([:mrf, :transparency_exclusions]) |> MRF.instance_list_from_tuples()
obfuscations =
Config.get([:mrf, :transparency_obfuscate_domains], []) |> MRF.subdomains_regex()
mrf_simple_excluded =
Config.get(:mrf_simple)
|> Enum.map(fn {rule, instances} ->
{rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)}
end)
mrf_simple =
mrf_simple_excluded
|> Enum.map(fn {rule, instances} ->
{rule, Enum.map(instances, fn {host, _} -> maybe_obfuscate(host, obfuscations) end)}
end)
|> Map.new()
# This is for backwards compatibility. We originally didn't sent
# extra info like a reason why an instance was rejected/quarantined/etc.
# Because we didn't want to break backwards compatibility it was decided
# to add an extra "info" key.
mrf_simple_info =
mrf_simple_excluded
|> Enum.map(fn {rule, instances} ->
{rule, Enum.reject(instances, fn {_, reason} -> reason == "" end)}
end)
|> Enum.reject(fn {_, instances} -> instances == [] end)
|> Enum.map(fn {rule, instances} ->
instances =
instances
|> Enum.map(fn {host, reason} ->
{maybe_obfuscate(host, obfuscations), %{"reason" => reason}}
end)
|> Map.new()
{rule, instances}
end)
|> Map.new()
{:ok, %{mrf_simple: mrf_simple, mrf_simple_info: mrf_simple_info}}
end
@impl true
def config_description do
%{
key: :mrf_simple,
related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
label: "MRF Simple",
description: "Simple ingress policies",
children:
[
%{
key: :media_removal,
description:
"List of instances to strip media attachments from and the reason for doing so"
},
%{
key: :media_nsfw,
label: "Media NSFW",
description:
"List of instances to tag all media as NSFW (sensitive) from and the reason for doing so"
},
%{
key: :federated_timeline_removal,
description:
"List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so"
},
%{
key: :reject,
description:
"List of instances to reject activities from (except deletes) and the reason for doing so"
},
%{
key: :accept,
description:
"List of instances to only accept activities from (except deletes) and the reason for doing so"
},
%{
key: :followers_only,
description:
"Force posts from the given instances to be visible by followers only and the reason for doing so"
},
%{
key: :report_removal,
description: "List of instances to reject reports from and the reason for doing so"
},
%{
key: :avatar_removal,
description: "List of instances to strip avatars from and the reason for doing so"
},
%{
key: :banner_removal,
description: "List of instances to strip banners from and the reason for doing so"
},
%{
key: :reject_deletes,
description: "List of instances to reject deletions from and the reason for doing so"
}
]
|> Enum.map(fn setting ->
Map.merge(
setting,
%{
type: {:list, :tuple},
key_placeholder: "instance",
value_placeholder: "reason",
suggestions: [{"example.com", "Some reason"}, {"*.example.com", "Another reason"}]
}
)
end)
}
end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index 56ae654f2..634b6a62f 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -1,163 +1,165 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# 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
This policy applies policies on a user activities depending on their tags
on your instance.
- `mrf_tag:media-force-nsfw`: Mark as sensitive on presence of attachments
- `mrf_tag:media-strip`: Remove attachments
- `mrf_tag:force-unlisted`: Mark as unlisted (removes from the federated timeline)
- `mrf_tag:sandbox`: Remove from public (local and federated) timelines
- `mrf_tag:disable-remote-subscription`: Reject non-local follow requests
- `mrf_tag:disable-any-subscription`: Reject any follow requests
"""
require Pleroma.Constants
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
defp get_tags(_), do: []
defp process_tag(
"mrf_tag:media-force-nsfw",
%{
- "type" => "Create",
+ "type" => type,
"object" => %{"attachment" => child_attachment}
} = message
)
- when length(child_attachment) > 0 do
+ when length(child_attachment) > 0 and type in ["Create", "Update"] do
{:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
end
defp process_tag(
"mrf_tag:media-strip",
%{
- "type" => "Create",
+ "type" => type,
"object" => %{"attachment" => child_attachment} = object
} = message
)
- when length(child_attachment) > 0 do
+ when length(child_attachment) > 0 and type in ["Create", "Update"] do
object = Map.delete(object, "attachment")
message = Map.put(message, "object", object)
{:ok, message}
end
defp process_tag(
"mrf_tag:force-unlisted",
%{
- "type" => "Create",
+ "type" => type,
"to" => to,
"cc" => cc,
"actor" => actor,
"object" => object
} = message
- ) do
+ )
+ when type in ["Create", "Update"] do
user = User.get_cached_by_ap_id(actor)
if Enum.member?(to, Pleroma.Constants.as_public()) do
to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
object =
object
|> Map.put("to", to)
|> Map.put("cc", cc)
message =
message
|> Map.put("to", to)
|> Map.put("cc", cc)
|> Map.put("object", object)
{:ok, message}
else
{:ok, message}
end
end
defp process_tag(
"mrf_tag:sandbox",
%{
- "type" => "Create",
+ "type" => type,
"to" => to,
"cc" => cc,
"actor" => actor,
"object" => object
} = message
- ) do
+ )
+ when type in ["Create", "Update"] do
user = User.get_cached_by_ap_id(actor)
if Enum.member?(to, Pleroma.Constants.as_public()) or
Enum.member?(cc, Pleroma.Constants.as_public()) do
to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
cc = List.delete(cc, Pleroma.Constants.as_public())
object =
object
|> Map.put("to", to)
|> Map.put("cc", cc)
message =
message
|> Map.put("to", to)
|> Map.put("cc", cc)
|> Map.put("object", object)
{:ok, message}
else
{:ok, message}
end
end
defp process_tag(
"mrf_tag:disable-remote-subscription",
%{"type" => "Follow", "actor" => actor} = message
) do
user = User.get_cached_by_ap_id(actor)
if user.local == true do
{:ok, message}
else
{:reject,
"[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-remote-subscription"}
end
end
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"}
defp process_tag(_, message), do: {:ok, message}
def filter_message(actor, message) do
User.get_cached_by_ap_id(actor)
|> get_tags()
|> Enum.reduce({:ok, message}, fn
tag, {:ok, message} ->
process_tag(tag, message)
_, error ->
error
end)
end
@impl true
def filter(%{"object" => target_actor, "type" => "Follow"} = message),
do: filter_message(target_actor, message)
@impl true
- def filter(%{"actor" => actor, "type" => "Create"} = message),
+ def filter(%{"actor" => actor, "type" => type} = message) when type in ["Create", "Update"],
do: filter_message(actor, message)
@impl true
def filter(message), do: {:ok, message}
@impl true
def describe, do: {:ok, %{}}
end
diff --git a/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs
index 47b07fdd9..a3c23a85c 100644
--- a/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs
@@ -1,84 +1,124 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
use ExUnit.Case, async: true
alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
@id Pleroma.Web.Endpoint.url() <> "/activities/cofe"
@local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe"
test "adds `expires_at` property" do
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{
"id" => @id,
"actor" => @local_actor,
"type" => "Create",
"object" => %{"type" => "Note"}
})
assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364
+
+ assert {:ok, %{"type" => "Update", "expires_at" => expires_at}} =
+ ActivityExpirationPolicy.filter(%{
+ "id" => @id,
+ "actor" => @local_actor,
+ "type" => "Update",
+ "object" => %{"type" => "Note"}
+ })
+
+ assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364
end
test "keeps existing `expires_at` if it less than the config setting" do
expires_at = DateTime.utc_now() |> Timex.shift(days: 1)
assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} =
ActivityExpirationPolicy.filter(%{
"id" => @id,
"actor" => @local_actor,
"type" => "Create",
"expires_at" => expires_at,
"object" => %{"type" => "Note"}
})
+
+ assert {:ok, %{"type" => "Update", "expires_at" => ^expires_at}} =
+ ActivityExpirationPolicy.filter(%{
+ "id" => @id,
+ "actor" => @local_actor,
+ "type" => "Update",
+ "expires_at" => expires_at,
+ "object" => %{"type" => "Note"}
+ })
end
test "overwrites existing `expires_at` if it greater than the config setting" do
too_distant_future = DateTime.utc_now() |> Timex.shift(years: 2)
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{
"id" => @id,
"actor" => @local_actor,
"type" => "Create",
"expires_at" => too_distant_future,
"object" => %{"type" => "Note"}
})
assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364
+
+ assert {:ok, %{"type" => "Update", "expires_at" => expires_at}} =
+ ActivityExpirationPolicy.filter(%{
+ "id" => @id,
+ "actor" => @local_actor,
+ "type" => "Update",
+ "expires_at" => too_distant_future,
+ "object" => %{"type" => "Note"}
+ })
+
+ assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364
end
test "ignores remote activities" do
assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Create",
"object" => %{"type" => "Note"}
})
refute Map.has_key?(activity, "expires_at")
+
+ assert {:ok, activity} =
+ ActivityExpirationPolicy.filter(%{
+ "id" => "https://example.com/123",
+ "actor" => "https://example.com/users/cofe",
+ "type" => "Update",
+ "object" => %{"type" => "Note"}
+ })
+
+ refute Map.has_key?(activity, "expires_at")
end
- test "ignores non-Create/Note activities" do
+ test "ignores non-Create/Update/Note activities" do
assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Follow"
})
refute Map.has_key?(activity, "expires_at")
assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Create",
"object" => %{"type" => "Cofe"}
})
refute Map.has_key?(activity, "expires_at")
end
end
diff --git a/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs
index c3ee03a05..6182e9717 100644
--- a/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs
@@ -1,191 +1,286 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
use Pleroma.DataCase
import Pleroma.Factory
import ExUnit.CaptureLog
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy
@linkless_message %{
"type" => "Create",
"object" => %{
"content" => "hi world!"
}
}
@linkful_message %{
"type" => "Create",
"object" => %{
"content" => "<a href='https://example.com'>hi world!</a>"
}
}
@response_message %{
"type" => "Create",
"object" => %{
"name" => "yes",
"type" => "Answer"
}
}
+ @linkless_update_message %{
+ "type" => "Update",
+ "object" => %{
+ "content" => "hi world!"
+ }
+ }
+
+ @linkful_update_message %{
+ "type" => "Update",
+ "object" => %{
+ "content" => "<a href='https://example.com'>hi world!</a>"
+ }
+ }
+
+ @response_update_message %{
+ "type" => "Update",
+ "object" => %{
+ "name" => "yes",
+ "type" => "Answer"
+ }
+ }
+
describe "with new user" do
test "it allows posts without links" do
user = insert(:user, local: false)
assert user.note_count == 0
message =
@linkless_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkless_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
test "it disallows posts with links" do
user = insert(:user, local: false)
assert user.note_count == 0
message = %{
"type" => "Create",
"actor" => user.ap_id,
"object" => %{
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"content" => "<a href='https://example.com'>hi world!</a>"
}
]
},
"content" => "mew"
}
}
+ update_message = %{
+ "type" => "Update",
+ "actor" => user.ap_id,
+ "object" => %{
+ "formerRepresentations" => %{
+ "type" => "OrderedCollection",
+ "orderedItems" => [
+ %{
+ "content" => "<a href='https://example.com'>hi world!</a>"
+ }
+ ]
+ },
+ "content" => "mew"
+ }
+ }
+
{:reject, _} = MRF.filter_one(AntiLinkSpamPolicy, message)
+ {:reject, _} = MRF.filter_one(AntiLinkSpamPolicy, update_message)
end
test "it allows posts with links for local users" do
user = insert(:user)
assert user.note_count == 0
message =
@linkful_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkful_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
test "it disallows posts with links in history" do
user = insert(:user, local: false)
assert user.note_count == 0
message =
@linkful_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkful_update_message
+ |> Map.put("actor", user.ap_id)
+
{:reject, _} = AntiLinkSpamPolicy.filter(message)
+ {:reject, _} = AntiLinkSpamPolicy.filter(update_message)
end
end
describe "with old user" do
test "it allows posts without links" do
user = insert(:user, note_count: 1)
assert user.note_count == 1
message =
@linkless_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkless_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
test "it allows posts with links" do
user = insert(:user, note_count: 1)
assert user.note_count == 1
message =
@linkful_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkful_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
end
describe "with followed new user" do
test "it allows posts without links" do
user = insert(:user, follower_count: 1)
assert user.follower_count == 1
message =
@linkless_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkless_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
test "it allows posts with links" do
user = insert(:user, follower_count: 1)
assert user.follower_count == 1
message =
@linkful_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @linkful_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
end
describe "with unknown actors" do
setup do
Tesla.Mock.mock(fn
%{method: :get, url: "http://invalid.actor"} ->
%Tesla.Env{status: 500, body: ""}
end)
:ok
end
test "it rejects posts without links" do
message =
@linkless_message
|> Map.put("actor", "http://invalid.actor")
+ update_message =
+ @linkless_update_message
+ |> Map.put("actor", "http://invalid.actor")
+
assert capture_log(fn ->
{:reject, _} = AntiLinkSpamPolicy.filter(message)
end) =~ "[error] Could not decode user at fetch http://invalid.actor"
+
+ assert capture_log(fn ->
+ {:reject, _} = AntiLinkSpamPolicy.filter(update_message)
+ end) =~ "[error] Could not decode user at fetch http://invalid.actor"
end
test "it rejects posts with links" do
message =
@linkful_message
|> Map.put("actor", "http://invalid.actor")
+ update_message =
+ @linkful_update_message
+ |> Map.put("actor", "http://invalid.actor")
+
assert capture_log(fn ->
{:reject, _} = AntiLinkSpamPolicy.filter(message)
end) =~ "[error] Could not decode user at fetch http://invalid.actor"
+
+ assert capture_log(fn ->
+ {:reject, _} = AntiLinkSpamPolicy.filter(update_message)
+ end) =~ "[error] Could not decode user at fetch http://invalid.actor"
end
end
describe "with contentless-objects" do
test "it does not reject them or error out" do
user = insert(:user, note_count: 1)
message =
@response_message
|> Map.put("actor", user.ap_id)
+ update_message =
+ @response_update_message
+ |> Map.put("actor", user.ap_id)
+
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ {:ok, _update_message} = AntiLinkSpamPolicy.filter(update_message)
end
end
end
diff --git a/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
index e3325d144..aa88f2f93 100644
--- a/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
@@ -1,60 +1,85 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy
@public "https://www.w3.org/ns/activitystreams#Public"
defp generate_messages(actor) do
{%{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{},
"to" => [@public, "f"],
"cc" => [actor.follower_address, "d"]
},
%{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
"to" => ["f", actor.follower_address],
"cc" => ["d", @public]
}}
end
+ defp generate_update_messages(actor) do
+ {%{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{},
+ "to" => [@public, "f"],
+ "cc" => [actor.follower_address, "d"]
+ },
+ %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+ "to" => ["f", actor.follower_address],
+ "cc" => ["d", @public]
+ }}
+ end
+
test "removes from the federated timeline by nickname heuristics 1" do
actor = insert(:user, %{nickname: "annoying_ebooks@example.com"})
{message, except_message} = generate_messages(actor)
+ {update_message, except_update_message} = generate_update_messages(actor)
assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+ assert ForceBotUnlistedPolicy.filter(update_message) == {:ok, except_update_message}
end
test "removes from the federated timeline by nickname heuristics 2" do
actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"})
{message, except_message} = generate_messages(actor)
+ {update_message, except_update_message} = generate_update_messages(actor)
assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+ assert ForceBotUnlistedPolicy.filter(update_message) == {:ok, except_update_message}
end
test "removes from the federated timeline by actor type Application" do
actor = insert(:user, %{actor_type: "Application"})
{message, except_message} = generate_messages(actor)
+ {update_message, except_update_message} = generate_update_messages(actor)
assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+ assert ForceBotUnlistedPolicy.filter(update_message) == {:ok, except_update_message}
end
test "removes from the federated timeline by actor type Service" do
actor = insert(:user, %{actor_type: "Service"})
{message, except_message} = generate_messages(actor)
+ {update_message, except_update_message} = generate_update_messages(actor)
assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+ assert ForceBotUnlistedPolicy.filter(update_message) == {:ok, except_update_message}
end
end
diff --git a/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs
index a88e1fa2e..2bcf49bc5 100644
--- a/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs
@@ -1,79 +1,111 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
use Pleroma.DataCase
import Pleroma.Factory
import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy
setup do
user = insert(:user)
message = %{
"actor" => user.ap_id,
"cc" => [user.follower_address],
"type" => "Create",
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
"https://instance.tld/users/user1",
"https://instance.tld/users/user2",
"https://instance.tld/users/user3"
],
"object" => %{
"type" => "Note"
}
}
- [user: user, message: message]
+ update_message = %{
+ "actor" => user.ap_id,
+ "cc" => [user.follower_address],
+ "type" => "Update",
+ "to" => [
+ "https://www.w3.org/ns/activitystreams#Public",
+ "https://instance.tld/users/user1",
+ "https://instance.tld/users/user2",
+ "https://instance.tld/users/user3"
+ ],
+ "object" => %{
+ "type" => "Note"
+ }
+ }
+
+ [user: user, message: message, update_message: update_message]
end
setup do: clear_config(:mrf_hellthread)
describe "reject" do
test "rejects the message if the recipient count is above reject_threshold", %{
- message: message
+ message: message,
+ update_message: update_message
} do
clear_config([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2})
assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} ==
filter(message)
+
+ assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} ==
+ filter(update_message)
end
test "does not reject the message if the recipient count is below reject_threshold", %{
- message: message
+ message: message,
+ update_message: update_message
} do
clear_config([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3})
assert {:ok, ^message} = filter(message)
+ assert {:ok, ^update_message} = filter(update_message)
end
end
describe "delist" do
test "delists the message if the recipient count is above delist_threshold", %{
user: user,
- message: message
+ message: message,
+ update_message: update_message
} do
clear_config([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0})
{:ok, message} = filter(message)
assert user.follower_address in message["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in message["cc"]
+
+ {:ok, update_message} = filter(update_message)
+ assert user.follower_address in update_message["to"]
+ assert "https://www.w3.org/ns/activitystreams#Public" in update_message["cc"]
end
test "does not delist the message if the recipient count is below delist_threshold", %{
- message: message
+ message: message,
+ update_message: update_message
} do
clear_config([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0})
assert {:ok, ^message} = filter(message)
+ assert {:ok, ^update_message} = filter(update_message)
end
end
- test "excludes follower collection and public URI from threshold count", %{message: message} do
+ test "excludes follower collection and public URI from threshold count", %{
+ message: message,
+ update_message: update_message
+ } do
clear_config([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3})
assert {:ok, ^message} = filter(message)
+ assert {:ok, ^update_message} = filter(update_message)
end
end
diff --git a/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs
index 80ddcacbe..8aef8bb1a 100644
--- a/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs
@@ -1,96 +1,144 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do
use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.MRF.MentionPolicy
setup do: clear_config(:mrf_mention)
test "pass filter if allow list is empty" do
Pleroma.Config.delete([:mrf_mention])
message = %{
"type" => "Create",
"to" => ["https://example.com/ok"],
"cc" => ["https://example.com/blocked"]
}
+ update_message = %{
+ "type" => "Update",
+ "to" => ["https://example.com/ok"],
+ "cc" => ["https://example.com/blocked"]
+ }
+
assert MentionPolicy.filter(message) == {:ok, message}
+ assert MentionPolicy.filter(update_message) == {:ok, update_message}
end
describe "allow" do
test "empty" do
clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]})
message = %{
"type" => "Create"
}
+ update_message = %{
+ "type" => "Update"
+ }
+
assert MentionPolicy.filter(message) == {:ok, message}
+ assert MentionPolicy.filter(update_message) == {:ok, update_message}
end
test "to" do
clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]})
message = %{
"type" => "Create",
"to" => ["https://example.com/ok"]
}
+ update_message = %{
+ "type" => "Update",
+ "to" => ["https://example.com/ok"]
+ }
+
assert MentionPolicy.filter(message) == {:ok, message}
+ assert MentionPolicy.filter(update_message) == {:ok, update_message}
end
test "cc" do
clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]})
message = %{
"type" => "Create",
"cc" => ["https://example.com/ok"]
}
+ update_message = %{
+ "type" => "Update",
+ "cc" => ["https://example.com/ok"]
+ }
+
assert MentionPolicy.filter(message) == {:ok, message}
+ assert MentionPolicy.filter(update_message) == {:ok, update_message}
end
test "both" do
clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]})
message = %{
"type" => "Create",
"to" => ["https://example.com/ok"],
"cc" => ["https://example.com/ok2"]
}
+ update_message = %{
+ "type" => "Update",
+ "to" => ["https://example.com/ok"],
+ "cc" => ["https://example.com/ok2"]
+ }
+
assert MentionPolicy.filter(message) == {:ok, message}
+ assert MentionPolicy.filter(update_message) == {:ok, update_message}
end
end
describe "deny" do
test "to" do
clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]})
message = %{
"type" => "Create",
"to" => ["https://example.com/blocked"]
}
+ update_message = %{
+ "type" => "Update",
+ "to" => ["https://example.com/blocked"]
+ }
+
assert MentionPolicy.filter(message) ==
{:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
+
+ assert MentionPolicy.filter(update_message) ==
+ {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
end
test "cc" do
clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]})
message = %{
"type" => "Create",
"to" => ["https://example.com/ok"],
"cc" => ["https://example.com/blocked"]
}
+ update_message = %{
+ "type" => "Update",
+ "to" => ["https://example.com/ok"],
+ "cc" => ["https://example.com/blocked"]
+ }
+
assert MentionPolicy.filter(message) ==
{:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
+
+ assert MentionPolicy.filter(update_message) ==
+ {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
end
end
end
diff --git a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
index 0569bfed3..036573171 100644
--- a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
@@ -1,598 +1,634 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
use Pleroma.DataCase
import Pleroma.Factory
alias Pleroma.Web.ActivityPub.MRF.SimplePolicy
alias Pleroma.Web.CommonAPI
setup do:
clear_config(:mrf_simple,
media_removal: [],
media_nsfw: [],
federated_timeline_removal: [],
report_removal: [],
reject: [],
followers_only: [],
accept: [],
avatar_removal: [],
banner_removal: [],
reject_deletes: []
)
describe "when :media_removal" do
test "is empty" do
clear_config([:mrf_simple, :media_removal], [])
media_message = build_media_message()
+ media_update_message = build_media_update_message()
local_message = build_local_message()
assert SimplePolicy.filter(media_message) == {:ok, media_message}
+ assert SimplePolicy.filter(media_update_message) == {:ok, media_update_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
clear_config([:mrf_simple, :media_removal], [{"remote.instance", "Some reason"}])
media_message = build_media_message()
+ media_update_message = build_media_update_message()
local_message = build_local_message()
assert SimplePolicy.filter(media_message) ==
{:ok,
media_message
|> Map.put("object", Map.delete(media_message["object"], "attachment"))}
+ assert SimplePolicy.filter(media_update_message) ==
+ {:ok,
+ media_update_message
+ |> Map.put("object", Map.delete(media_update_message["object"], "attachment"))}
+
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "match with wildcard domain" do
clear_config([:mrf_simple, :media_removal], [{"*.remote.instance", "Whatever reason"}])
media_message = build_media_message()
+ media_update_message = build_media_update_message()
local_message = build_local_message()
assert SimplePolicy.filter(media_message) ==
{:ok,
media_message
|> Map.put("object", Map.delete(media_message["object"], "attachment"))}
+ assert SimplePolicy.filter(media_update_message) ==
+ {:ok,
+ media_update_message
+ |> Map.put("object", Map.delete(media_update_message["object"], "attachment"))}
+
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
end
describe "when :media_nsfw" do
test "is empty" do
clear_config([:mrf_simple, :media_nsfw], [])
media_message = build_media_message()
+ media_update_message = build_media_update_message()
local_message = build_local_message()
assert SimplePolicy.filter(media_message) == {:ok, media_message}
+ assert SimplePolicy.filter(media_update_message) == {:ok, media_update_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
clear_config([:mrf_simple, :media_nsfw], [{"remote.instance", "Whetever"}])
media_message = build_media_message()
+ media_update_message = build_media_update_message()
local_message = build_local_message()
assert SimplePolicy.filter(media_message) ==
{:ok, put_in(media_message, ["object", "sensitive"], true)}
+ assert SimplePolicy.filter(media_update_message) ==
+ {:ok, put_in(media_update_message, ["object", "sensitive"], true)}
+
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "match with wildcard domain" do
clear_config([:mrf_simple, :media_nsfw], [{"*.remote.instance", "yeah yeah"}])
media_message = build_media_message()
+ media_update_message = build_media_update_message()
local_message = build_local_message()
assert SimplePolicy.filter(media_message) ==
{:ok, put_in(media_message, ["object", "sensitive"], true)}
+ assert SimplePolicy.filter(media_update_message) ==
+ {:ok, put_in(media_update_message, ["object", "sensitive"], true)}
+
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
end
defp build_media_message do
%{
"actor" => "https://remote.instance/users/bob",
"type" => "Create",
"object" => %{
"attachment" => [%{}],
"tag" => ["foo"],
"sensitive" => false
}
}
end
+ defp build_media_update_message do
+ %{
+ "actor" => "https://remote.instance/users/bob",
+ "type" => "Update",
+ "object" => %{
+ "attachment" => [%{}],
+ "tag" => ["foo"],
+ "sensitive" => false
+ }
+ }
+ end
+
describe "when :report_removal" do
test "is empty" do
clear_config([:mrf_simple, :report_removal], [])
report_message = build_report_message()
local_message = build_local_message()
assert SimplePolicy.filter(report_message) == {:ok, report_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
clear_config([:mrf_simple, :report_removal], [{"remote.instance", "muh"}])
report_message = build_report_message()
local_message = build_local_message()
assert {:reject, _} = SimplePolicy.filter(report_message)
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "match with wildcard domain" do
clear_config([:mrf_simple, :report_removal], [{"*.remote.instance", "suya"}])
report_message = build_report_message()
local_message = build_local_message()
assert {:reject, _} = SimplePolicy.filter(report_message)
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
end
defp build_report_message do
%{
"actor" => "https://remote.instance/users/bob",
"type" => "Flag"
}
end
describe "when :federated_timeline_removal" do
test "is empty" do
clear_config([:mrf_simple, :federated_timeline_removal], [])
{_, ftl_message} = build_ftl_actor_and_message()
local_message = build_local_message()
assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
{actor, ftl_message} = build_ftl_actor_and_message()
ftl_message_actor_host =
ftl_message
|> Map.fetch!("actor")
|> URI.parse()
|> Map.fetch!(:host)
clear_config([:mrf_simple, :federated_timeline_removal], [{ftl_message_actor_host, "uwu"}])
local_message = build_local_message()
assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
assert actor.follower_address in ftl_message["to"]
refute actor.follower_address in ftl_message["cc"]
refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "match with wildcard domain" do
{actor, ftl_message} = build_ftl_actor_and_message()
ftl_message_actor_host =
ftl_message
|> Map.fetch!("actor")
|> URI.parse()
|> Map.fetch!(:host)
clear_config([:mrf_simple, :federated_timeline_removal], [
{"*." <> ftl_message_actor_host, "owo"}
])
local_message = build_local_message()
assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
assert actor.follower_address in ftl_message["to"]
refute actor.follower_address in ftl_message["cc"]
refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host but only as:Public in to" do
{_actor, ftl_message} = build_ftl_actor_and_message()
ftl_message_actor_host =
ftl_message
|> Map.fetch!("actor")
|> URI.parse()
|> Map.fetch!(:host)
ftl_message = Map.put(ftl_message, "cc", [])
clear_config([:mrf_simple, :federated_timeline_removal], [
{ftl_message_actor_host, "spiderwaifu goes 88w88"}
])
assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
end
end
describe "describe/1" do
test "returns a description of the policy" do
clear_config([:mrf_simple, :reject], [
{"remote.instance", "did not give my catboy a burg"}
])
assert {:ok, %{mrf_simple: %{reject: ["remote.instance"]}}} = SimplePolicy.describe()
end
test "excludes domains listed in :transparency_exclusions" do
clear_config([:mrf, :transparency_exclusions], [{"remote.instance", ":("}])
clear_config([:mrf_simple, :reject], [
{"remote.instance", "did not give my catboy a burg"}
])
{:ok, description} = SimplePolicy.describe()
assert %{mrf_simple: %{reject: []}} = description
assert description[:mrf_simple_info][:reject] == nil
end
test "obfuscates domains listed in :transparency_obfuscate_domains" do
clear_config([:mrf, :transparency_obfuscate_domains], ["remote.instance", "a.b"])
clear_config([:mrf_simple, :reject], [
{"remote.instance", "did not give my catboy a burg"},
{"a.b", "spam-poked me on facebook in 2006"}
])
assert {:ok,
%{
mrf_simple: %{reject: ["rem***.*****nce", "a.b"]},
mrf_simple_info: %{reject: %{"rem***.*****nce" => %{}}}
}} = SimplePolicy.describe()
end
end
defp build_ftl_actor_and_message do
actor = insert(:user)
{actor,
%{
"actor" => actor.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"],
"cc" => [actor.follower_address, "http://foo.bar/qux"]
}}
end
describe "when :reject" do
test "is empty" do
clear_config([:mrf_simple, :reject], [])
remote_message = build_remote_message()
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
end
test "activity has a matching host" do
clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "activity matches with wildcard domain" do
clear_config([:mrf_simple, :reject], [{"*.remote.instance", ""}])
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "actor has a matching host" do
clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
remote_user = build_remote_user()
assert {:reject, _} = SimplePolicy.filter(remote_user)
end
test "reject Announce when object would be rejected" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
announce = %{
"type" => "Announce",
"actor" => "https://okay.tld/users/alice",
"object" => %{"type" => "Note", "actor" => "https://blocked.tld/users/bob"}
}
assert {:reject, _} = SimplePolicy.filter(announce)
end
test "reject by URI object" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
announce = %{
"type" => "Announce",
"actor" => "https://okay.tld/users/alice",
"object" => "https://blocked.tld/activities/1"
}
assert {:reject, _} = SimplePolicy.filter(announce)
end
end
describe "when :followers_only" do
test "is empty" do
clear_config([:mrf_simple, :followers_only], [])
{_, ftl_message} = build_ftl_actor_and_message()
local_message = build_local_message()
assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
actor = insert(:user)
following_user = insert(:user)
non_following_user = insert(:user)
{:ok, _, _, _} = CommonAPI.follow(following_user, actor)
activity = %{
"actor" => actor.ap_id,
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
following_user.ap_id,
non_following_user.ap_id
],
"cc" => [actor.follower_address, "http://foo.bar/qux"]
}
dm_activity = %{
"actor" => actor.ap_id,
"to" => [
following_user.ap_id,
non_following_user.ap_id
],
"cc" => []
}
actor_domain =
activity
|> Map.fetch!("actor")
|> URI.parse()
|> Map.fetch!(:host)
clear_config([:mrf_simple, :followers_only], [{actor_domain, ""}])
assert {:ok, new_activity} = SimplePolicy.filter(activity)
assert actor.follower_address in new_activity["cc"]
assert following_user.ap_id in new_activity["to"]
refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"]
refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"]
refute non_following_user.ap_id in new_activity["to"]
refute non_following_user.ap_id in new_activity["cc"]
assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity)
assert new_dm_activity["to"] == [following_user.ap_id]
assert new_dm_activity["cc"] == []
end
end
describe "when :accept" do
test "is empty" do
clear_config([:mrf_simple, :accept], [])
local_message = build_local_message()
remote_message = build_remote_message()
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
end
test "is not empty but activity doesn't have a matching host" do
clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
local_message = build_local_message()
remote_message = build_remote_message()
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "activity has a matching host" do
clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
local_message = build_local_message()
remote_message = build_remote_message()
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
end
test "activity matches with wildcard domain" do
clear_config([:mrf_simple, :accept], [{"*.remote.instance", ""}])
local_message = build_local_message()
remote_message = build_remote_message()
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
end
test "actor has a matching host" do
clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
end
describe "when :avatar_removal" do
test "is empty" do
clear_config([:mrf_simple, :avatar_removal], [])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "is not empty but it doesn't have a matching host" do
clear_config([:mrf_simple, :avatar_removal], [{"non.matching.remote", ""}])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "has a matching host" do
clear_config([:mrf_simple, :avatar_removal], [{"remote.instance", ""}])
remote_user = build_remote_user()
{:ok, filtered} = SimplePolicy.filter(remote_user)
refute filtered["icon"]
end
test "match with wildcard domain" do
clear_config([:mrf_simple, :avatar_removal], [{"*.remote.instance", ""}])
remote_user = build_remote_user()
{:ok, filtered} = SimplePolicy.filter(remote_user)
refute filtered["icon"]
end
end
describe "when :banner_removal" do
test "is empty" do
clear_config([:mrf_simple, :banner_removal], [])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "is not empty but it doesn't have a matching host" do
clear_config([:mrf_simple, :banner_removal], [{"non.matching.remote", ""}])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "has a matching host" do
clear_config([:mrf_simple, :banner_removal], [{"remote.instance", ""}])
remote_user = build_remote_user()
{:ok, filtered} = SimplePolicy.filter(remote_user)
refute filtered["image"]
end
test "match with wildcard domain" do
clear_config([:mrf_simple, :banner_removal], [{"*.remote.instance", ""}])
remote_user = build_remote_user()
{:ok, filtered} = SimplePolicy.filter(remote_user)
refute filtered["image"]
end
end
describe "when :reject_deletes is empty" do
setup do: clear_config([:mrf_simple, :reject_deletes], [])
test "it accepts deletions even from rejected servers" do
clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
deletion_message = build_remote_deletion_message()
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
end
test "it accepts deletions even from non-whitelisted servers" do
clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
deletion_message = build_remote_deletion_message()
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
end
end
describe "when :reject_deletes is not empty but it doesn't have a matching host" do
setup do: clear_config([:mrf_simple, :reject_deletes], [{"non.matching.remote", ""}])
test "it accepts deletions even from rejected servers" do
clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
deletion_message = build_remote_deletion_message()
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
end
test "it accepts deletions even from non-whitelisted servers" do
clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
deletion_message = build_remote_deletion_message()
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
end
end
describe "when :reject_deletes has a matching host" do
setup do: clear_config([:mrf_simple, :reject_deletes], [{"remote.instance", ""}])
test "it rejects the deletion" do
deletion_message = build_remote_deletion_message()
assert {:reject, _} = SimplePolicy.filter(deletion_message)
end
end
describe "when :reject_deletes match with wildcard domain" do
setup do: clear_config([:mrf_simple, :reject_deletes], [{"*.remote.instance", ""}])
test "it rejects the deletion" do
deletion_message = build_remote_deletion_message()
assert {:reject, _} = SimplePolicy.filter(deletion_message)
end
end
defp build_local_message do
%{
"actor" => "#{Pleroma.Web.Endpoint.url()}/users/alice",
"to" => [],
"cc" => []
}
end
defp build_remote_message do
%{"actor" => "https://remote.instance/users/bob"}
end
defp build_remote_user do
%{
"id" => "https://remote.instance/users/bob",
"icon" => %{
"url" => "http://example.com/image.jpg",
"type" => "Image"
},
"image" => %{
"url" => "http://example.com/image.jpg",
"type" => "Image"
},
"type" => "Person"
}
end
defp build_remote_deletion_message do
%{
"type" => "Delete",
"actor" => "https://remote.instance/users/bob"
}
end
end
diff --git a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs
index faaadff79..0c84b5bf3 100644
--- a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs
@@ -1,123 +1,183 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
alias Pleroma.Web.ActivityPub.MRF.TagPolicy
@public "https://www.w3.org/ns/activitystreams#Public"
describe "mrf_tag:disable-any-subscription" do
test "rejects message" do
actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"])
message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id}
assert {:reject, _} = TagPolicy.filter(message)
end
end
describe "mrf_tag:disable-remote-subscription" do
test "rejects non-local follow requests" do
actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false)
message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
assert {:reject, _} = TagPolicy.filter(message)
end
test "allows non-local follow requests" do
actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true)
message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
assert {:ok, _message} = TagPolicy.filter(message)
end
end
describe "mrf_tag:sandbox" do
test "removes from public timelines" do
actor = insert(:user, tags: ["mrf_tag:sandbox"])
message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{},
"to" => [@public, "f"],
"cc" => [@public, "d"]
}
except_message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]},
"to" => ["f", actor.follower_address],
"cc" => ["d"]
}
+ edit_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{},
+ "to" => [@public, "f"],
+ "cc" => [@public, "d"]
+ }
+
+ edit_expect_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]},
+ "to" => ["f", actor.follower_address],
+ "cc" => ["d"]
+ }
+
assert TagPolicy.filter(message) == {:ok, except_message}
+ assert TagPolicy.filter(edit_message) == {:ok, edit_expect_message}
end
end
describe "mrf_tag:force-unlisted" do
test "removes from the federated timeline" do
actor = insert(:user, tags: ["mrf_tag:force-unlisted"])
message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{},
"to" => [@public, "f"],
"cc" => [actor.follower_address, "d"]
}
except_message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
"to" => ["f", actor.follower_address],
"cc" => ["d", @public]
}
+ edit_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{},
+ "to" => [@public, "f"],
+ "cc" => [actor.follower_address, "d"]
+ }
+
+ edit_expect_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+ "to" => ["f", actor.follower_address],
+ "cc" => ["d", @public]
+ }
+
assert TagPolicy.filter(message) == {:ok, except_message}
+ assert TagPolicy.filter(edit_message) == {:ok, edit_expect_message}
end
end
describe "mrf_tag:media-strip" do
test "removes attachments" do
actor = insert(:user, tags: ["mrf_tag:media-strip"])
message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"attachment" => ["file1"]}
}
except_message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{}
}
+ edit_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{"attachment" => ["file1"]}
+ }
+
+ edit_expect_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{}
+ }
+
assert TagPolicy.filter(message) == {:ok, except_message}
+ assert TagPolicy.filter(edit_message) == {:ok, edit_expect_message}
end
end
describe "mrf_tag:media-force-nsfw" do
test "Mark as sensitive on presence of attachments" do
actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"])
message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"tag" => ["test"], "attachment" => ["file1"]}
}
except_message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"tag" => ["test"], "attachment" => ["file1"], "sensitive" => true}
}
+ edit_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{"tag" => ["test"], "attachment" => ["file1"]}
+ }
+
+ edit_expect_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Update",
+ "object" => %{"tag" => ["test"], "attachment" => ["file1"], "sensitive" => true}
+ }
+
assert TagPolicy.filter(message) == {:ok, except_message}
+ assert TagPolicy.filter(edit_message) == {:ok, edit_expect_message}
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 14, 7:39 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166789
Default Alt Text
(81 KB)

Event Timeline