summaryrefslogtreecommitdiff
path: root/test/pleroma/web/activity_pub/mrf
diff options
context:
space:
mode:
Diffstat (limited to 'test/pleroma/web/activity_pub/mrf')
-rw-r--r--test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs84
-rw-r--r--test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs72
-rw-r--r--test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs166
-rw-r--r--test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs92
-rw-r--r--test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs60
-rw-r--r--test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs92
-rw-r--r--test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs225
-rw-r--r--test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs53
-rw-r--r--test/pleroma/web/activity_pub/mrf/mention_policy_test.exs96
-rw-r--r--test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs37
-rw-r--r--test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs42
-rw-r--r--test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs148
-rw-r--r--test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs100
-rw-r--r--test/pleroma/web/activity_pub/mrf/simple_policy_test.exs539
-rw-r--r--test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs68
-rw-r--r--test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs33
-rw-r--r--test/pleroma/web/activity_pub/mrf/tag_policy_test.exs123
-rw-r--r--test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs31
-rw-r--r--test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs106
19 files changed, 2167 insertions, 0 deletions
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
new file mode 100644
index 000000000..e7370d4ef
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs
@@ -0,0 +1,84 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 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
+ 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"}
+ })
+ 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
+ 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")
+ end
+
+ test "ignores non-Create/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_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs
new file mode 100644
index 000000000..3c795f5ac
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs
@@ -0,0 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy
+
+ describe "blocking based on attributes" do
+ test "matches followbots by nickname" do
+ actor = insert(:user, %{nickname: "followbot@example.com"})
+ target = insert(:user)
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Follow",
+ "actor" => actor.ap_id,
+ "object" => target.ap_id,
+ "id" => "https://example.com/activities/1234"
+ }
+
+ assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message)
+ end
+
+ test "matches followbots by display name" do
+ actor = insert(:user, %{name: "Federation Bot"})
+ target = insert(:user)
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Follow",
+ "actor" => actor.ap_id,
+ "object" => target.ap_id,
+ "id" => "https://example.com/activities/1234"
+ }
+
+ assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message)
+ end
+ end
+
+ test "it allows non-followbots" do
+ actor = insert(:user)
+ target = insert(:user)
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Follow",
+ "actor" => actor.ap_id,
+ "object" => target.ap_id,
+ "id" => "https://example.com/activities/1234"
+ }
+
+ {:ok, _} = AntiFollowbotPolicy.filter(message)
+ end
+
+ test "it gracefully handles nil display names" do
+ actor = insert(:user, %{name: nil})
+ target = insert(:user)
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Follow",
+ "actor" => actor.ap_id,
+ "object" => target.ap_id,
+ "id" => "https://example.com/activities/1234"
+ }
+
+ {:ok, _} = AntiFollowbotPolicy.filter(message)
+ 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
new file mode 100644
index 000000000..6867c9853
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs
@@ -0,0 +1,166 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 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.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"
+ }
+ }
+
+ 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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ end
+
+ test "it disallows posts with links" do
+ user = insert(:user, local: false)
+
+ assert user.note_count == 0
+
+ message =
+ @linkful_message
+ |> Map.put("actor", user.ap_id)
+
+ {:reject, _} = AntiLinkSpamPolicy.filter(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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(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")
+
+ assert capture_log(fn ->
+ {:reject, _} = AntiLinkSpamPolicy.filter(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")
+
+ assert capture_log(fn ->
+ {:reject, _} = AntiLinkSpamPolicy.filter(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)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ end
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs
new file mode 100644
index 000000000..9a283f27d
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs
@@ -0,0 +1,92 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Activity
+ alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended
+
+ describe "rewrites summary" do
+ test "it adds `re:` to summary object when child summary and parent summary equal" do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "object-summary",
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res["object"]["summary"] == "re: object-summary"
+ end
+
+ test "it adds `re:` to summary object when child summary containts re-subject of parent summary " do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "object-summary",
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "re: object-summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res["object"]["summary"] == "re: object-summary"
+ end
+ end
+
+ describe "skip filter" do
+ test "it skip if type isn't 'Create'" do
+ message = %{
+ "type" => "Annotation",
+ "object" => %{"summary" => "object-summary"}
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skip if summary is empty" do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skip if inReplyTo is empty" do
+ message = %{"type" => "Create", "object" => %{"summary" => "summary"}}
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skip if parent and child summary isn't equal" do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "object-summary",
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skips if the object is only a reference" do
+ message = %{
+ "type" => "Create",
+ "object" => "somereference"
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == 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
new file mode 100644
index 000000000..86dd9ddae
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
@@ -0,0 +1,60 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
+ use Pleroma.DataCase
+ 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
+
+ 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)
+
+ assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_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)
+
+ assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_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)
+
+ assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_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)
+
+ assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_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
new file mode 100644
index 000000000..26f5bcdaa
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs
@@ -0,0 +1,92 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 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
+
+ alias Pleroma.Web.CommonAPI
+
+ 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]
+ end
+
+ setup do: clear_config(:mrf_hellthread)
+
+ test "doesn't die on chat messages" do
+ Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0})
+
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin")
+
+ assert {:ok, _} = filter(activity.data)
+ end
+
+ describe "reject" do
+ test "rejects the message if the recipient count is above reject_threshold", %{
+ message: message
+ } do
+ Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2})
+
+ assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} ==
+ filter(message)
+ end
+
+ test "does not reject the message if the recipient count is below reject_threshold", %{
+ message: message
+ } do
+ Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3})
+
+ assert {:ok, ^message} = filter(message)
+ end
+ end
+
+ describe "delist" do
+ test "delists the message if the recipient count is above delist_threshold", %{
+ user: user,
+ message: message
+ } do
+ Pleroma.Config.put([: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"]
+ end
+
+ test "does not delist the message if the recipient count is below delist_threshold", %{
+ message: message
+ } do
+ Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0})
+
+ assert {:ok, ^message} = filter(message)
+ end
+ end
+
+ test "excludes follower collection and public URI from threshold count", %{message: message} do
+ Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3})
+
+ assert {:ok, ^message} = filter(message)
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs
new file mode 100644
index 000000000..b3d0f3d90
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs
@@ -0,0 +1,225 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy
+
+ setup do: clear_config(:mrf_keyword)
+
+ setup do
+ Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []})
+ end
+
+ describe "rejecting based on keywords" do
+ test "rejects if string matches in content" do
+ Pleroma.Config.put([:mrf_keyword, :reject], ["pun"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "content" => "just a daily reminder that compLAINer is a good pun",
+ "summary" => ""
+ }
+ }
+
+ assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
+ KeywordPolicy.filter(message)
+ end
+
+ test "rejects if string matches in summary" do
+ Pleroma.Config.put([:mrf_keyword, :reject], ["pun"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "just a daily reminder that compLAINer is a good pun",
+ "content" => ""
+ }
+ }
+
+ assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
+ KeywordPolicy.filter(message)
+ end
+
+ test "rejects if regex matches in content" do
+ Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/])
+
+ assert true ==
+ Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content ->
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "content" => "just a daily reminder that #{content} is a good pun",
+ "summary" => ""
+ }
+ }
+
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
+ KeywordPolicy.filter(message)
+ end)
+ end
+
+ test "rejects if regex matches in summary" do
+ Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/])
+
+ assert true ==
+ Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content ->
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "just a daily reminder that #{content} is a good pun",
+ "content" => ""
+ }
+ }
+
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
+ KeywordPolicy.filter(message)
+ end)
+ end
+ end
+
+ describe "delisting from ftl based on keywords" do
+ test "delists if string matches in content" do
+ Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"])
+
+ message = %{
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "type" => "Create",
+ "object" => %{
+ "content" => "just a daily reminder that compLAINer is a good pun",
+ "summary" => ""
+ }
+ }
+
+ {:ok, result} = KeywordPolicy.filter(message)
+ assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"]
+ refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"]
+ end
+
+ test "delists if string matches in summary" do
+ Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"])
+
+ message = %{
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "type" => "Create",
+ "object" => %{
+ "summary" => "just a daily reminder that compLAINer is a good pun",
+ "content" => ""
+ }
+ }
+
+ {:ok, result} = KeywordPolicy.filter(message)
+ assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"]
+ refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"]
+ end
+
+ test "delists if regex matches in content" do
+ Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/])
+
+ assert true ==
+ Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content ->
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{
+ "content" => "just a daily reminder that #{content} is a good pun",
+ "summary" => ""
+ }
+ }
+
+ {:ok, result} = KeywordPolicy.filter(message)
+
+ ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and
+ not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
+ end)
+ end
+
+ test "delists if regex matches in summary" do
+ Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/])
+
+ assert true ==
+ Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content ->
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{
+ "summary" => "just a daily reminder that #{content} is a good pun",
+ "content" => ""
+ }
+ }
+
+ {:ok, result} = KeywordPolicy.filter(message)
+
+ ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and
+ not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
+ end)
+ end
+ end
+
+ describe "replacing keywords" do
+ test "replaces keyword if string matches in content" do
+ Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}])
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{"content" => "ZFS is opensource", "summary" => ""}
+ }
+
+ {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message)
+ assert result == "ZFS is free software"
+ end
+
+ test "replaces keyword if string matches in summary" do
+ Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}])
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{"summary" => "ZFS is opensource", "content" => ""}
+ }
+
+ {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message)
+ assert result == "ZFS is free software"
+ end
+
+ test "replaces keyword if regex matches in content" do
+ Pleroma.Config.put([:mrf_keyword, :replace], [
+ {~r/open(-|\s)?source\s?(software)?/, "free software"}
+ ])
+
+ assert true ==
+ Enum.all?(["opensource", "open-source", "open source"], fn content ->
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{"content" => "ZFS is #{content}", "summary" => ""}
+ }
+
+ {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message)
+ result == "ZFS is free software"
+ end)
+ end
+
+ test "replaces keyword if regex matches in summary" do
+ Pleroma.Config.put([:mrf_keyword, :replace], [
+ {~r/open(-|\s)?source\s?(software)?/, "free software"}
+ ])
+
+ assert true ==
+ Enum.all?(["opensource", "open-source", "open source"], fn content ->
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{"summary" => "ZFS is #{content}", "content" => ""}
+ }
+
+ {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message)
+ result == "ZFS is free software"
+ end)
+ end
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs
new file mode 100644
index 000000000..1710c4d2a
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs
@@ -0,0 +1,53 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.HTTP
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy
+
+ import Mock
+
+ @message %{
+ "type" => "Create",
+ "object" => %{
+ "type" => "Note",
+ "content" => "content",
+ "attachment" => [
+ %{"url" => [%{"href" => "http://example.com/image.jpg"}]}
+ ]
+ }
+ }
+
+ setup do: clear_config([:media_proxy, :enabled], true)
+
+ test "it prefetches media proxy URIs" do
+ with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
+ MediaProxyWarmingPolicy.filter(@message)
+
+ ObanHelpers.perform_all()
+ # Performing jobs which has been just enqueued
+ ObanHelpers.perform_all()
+
+ assert called(HTTP.get(:_, :_, :_))
+ end
+ end
+
+ test "it does nothing when no attachments are present" do
+ object =
+ @message["object"]
+ |> Map.delete("attachment")
+
+ message =
+ @message
+ |> Map.put("object", object)
+
+ with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
+ MediaProxyWarmingPolicy.filter(message)
+ refute called(HTTP.get(:_, :_, :_))
+ end
+ 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
new file mode 100644
index 000000000..220309cc9
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs
@@ -0,0 +1,96 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 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"]
+ }
+
+ assert MentionPolicy.filter(message) == {:ok, message}
+ end
+
+ describe "allow" do
+ test "empty" do
+ Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]})
+
+ message = %{
+ "type" => "Create"
+ }
+
+ assert MentionPolicy.filter(message) == {:ok, message}
+ end
+
+ test "to" do
+ Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]})
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://example.com/ok"]
+ }
+
+ assert MentionPolicy.filter(message) == {:ok, message}
+ end
+
+ test "cc" do
+ Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]})
+
+ message = %{
+ "type" => "Create",
+ "cc" => ["https://example.com/ok"]
+ }
+
+ assert MentionPolicy.filter(message) == {:ok, message}
+ end
+
+ test "both" do
+ Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]})
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://example.com/ok"],
+ "cc" => ["https://example.com/ok2"]
+ }
+
+ assert MentionPolicy.filter(message) == {:ok, message}
+ end
+ end
+
+ describe "deny" do
+ test "to" do
+ Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]})
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://example.com/blocked"]
+ }
+
+ assert MentionPolicy.filter(message) ==
+ {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
+ end
+
+ test "cc" do
+ Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]})
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://example.com/ok"],
+ "cc" => ["https://example.com/blocked"]
+ }
+
+ assert MentionPolicy.filter(message) ==
+ {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
+ end
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
new file mode 100644
index 000000000..64ea61dd4
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy
+
+ test "it clears content object" do
+ message = %{
+ "type" => "Create",
+ "object" => %{"content" => ".", "attachment" => "image"}
+ }
+
+ assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+ assert res["object"]["content"] == ""
+
+ message = put_in(message, ["object", "content"], "<p>.</p>")
+ assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+ assert res["object"]["content"] == ""
+ end
+
+ @messages [
+ %{
+ "type" => "Create",
+ "object" => %{"content" => "test", "attachment" => "image"}
+ },
+ %{"type" => "Create", "object" => %{"content" => "."}},
+ %{"type" => "Create", "object" => %{"content" => "<p>.</p>"}}
+ ]
+ test "it skips filter" do
+ Enum.each(@messages, fn message ->
+ assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+ assert res == message
+ end)
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs
new file mode 100644
index 000000000..9b39c45bd
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs
@@ -0,0 +1,42 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup
+
+ @html_sample """
+ <b>this is in bold</b>
+ <p>this is a paragraph</p>
+ this is a linebreak<br />
+ this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
+ this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a>
+ this is an image: <img src="http://example.com/image.jpg"><br />
+ <script>alert('hacked')</script>
+ """
+
+ test "it filter html tags" do
+ expected = """
+ <b>this is in bold</b>
+ <p>this is a paragraph</p>
+ this is a linebreak<br/>
+ this is a link with allowed &quot;rel&quot; attribute: <a href="http://example.com/" rel="tag">example.com</a>
+ this is a link with not allowed &quot;rel&quot; attribute: <a href="http://example.com/">example.com</a>
+ this is an image: <img src="http://example.com/image.jpg"/><br/>
+ alert(&#39;hacked&#39;)
+ """
+
+ message = %{"type" => "Create", "object" => %{"content" => @html_sample}}
+
+ assert {:ok, res} = NormalizeMarkup.filter(message)
+ assert res["object"]["content"] == expected
+ end
+
+ test "it skips filter if type isn't `Create`" do
+ message = %{"type" => "Note", "object" => %{}}
+
+ assert {:ok, res} = NormalizeMarkup.filter(message)
+ assert res == message
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs
new file mode 100644
index 000000000..cf6acc9a2
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs
@@ -0,0 +1,148 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
+ use Pleroma.DataCase
+ alias Pleroma.Config
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy
+ alias Pleroma.Web.ActivityPub.Visibility
+
+ setup do:
+ clear_config(:mrf_object_age,
+ threshold: 172_800,
+ actions: [:delist, :strip_followers]
+ )
+
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ defp get_old_message do
+ File.read!("test/fixtures/mastodon-post-activity.json")
+ |> Poison.decode!()
+ end
+
+ defp get_new_message do
+ old_message = get_old_message()
+
+ new_object =
+ old_message
+ |> Map.get("object")
+ |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601())
+
+ old_message
+ |> Map.put("object", new_object)
+ end
+
+ describe "with reject action" do
+ test "works with objects with empty to or cc fields" do
+ Config.put([:mrf_object_age, :actions], [:reject])
+
+ data =
+ get_old_message()
+ |> Map.put("cc", nil)
+ |> Map.put("to", nil)
+
+ assert match?({:reject, _}, ObjectAgePolicy.filter(data))
+ end
+
+ test "it rejects an old post" do
+ Config.put([:mrf_object_age, :actions], [:reject])
+
+ data = get_old_message()
+
+ assert match?({:reject, _}, ObjectAgePolicy.filter(data))
+ end
+
+ test "it allows a new post" do
+ Config.put([:mrf_object_age, :actions], [:reject])
+
+ data = get_new_message()
+
+ assert match?({:ok, _}, ObjectAgePolicy.filter(data))
+ end
+ end
+
+ describe "with delist action" do
+ test "works with objects with empty to or cc fields" do
+ Config.put([:mrf_object_age, :actions], [:delist])
+
+ data =
+ get_old_message()
+ |> Map.put("cc", nil)
+ |> Map.put("to", nil)
+
+ {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
+
+ {:ok, data} = ObjectAgePolicy.filter(data)
+
+ assert Visibility.get_visibility(%{data: data}) == "unlisted"
+ end
+
+ test "it delists an old post" do
+ Config.put([:mrf_object_age, :actions], [:delist])
+
+ data = get_old_message()
+
+ {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
+
+ {:ok, data} = ObjectAgePolicy.filter(data)
+
+ assert Visibility.get_visibility(%{data: data}) == "unlisted"
+ end
+
+ test "it allows a new post" do
+ Config.put([:mrf_object_age, :actions], [:delist])
+
+ data = get_new_message()
+
+ {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"])
+
+ assert match?({:ok, ^data}, ObjectAgePolicy.filter(data))
+ end
+ end
+
+ describe "with strip_followers action" do
+ test "works with objects with empty to or cc fields" do
+ Config.put([:mrf_object_age, :actions], [:strip_followers])
+
+ data =
+ get_old_message()
+ |> Map.put("cc", nil)
+ |> Map.put("to", nil)
+
+ {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"])
+
+ {:ok, data} = ObjectAgePolicy.filter(data)
+
+ refute user.follower_address in data["to"]
+ refute user.follower_address in data["cc"]
+ end
+
+ test "it strips followers collections from an old post" do
+ Config.put([:mrf_object_age, :actions], [:strip_followers])
+
+ data = get_old_message()
+
+ {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"])
+
+ {:ok, data} = ObjectAgePolicy.filter(data)
+
+ refute user.follower_address in data["to"]
+ refute user.follower_address in data["cc"]
+ end
+
+ test "it allows a new post" do
+ Config.put([:mrf_object_age, :actions], [:strip_followers])
+
+ data = get_new_message()
+
+ {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
+
+ assert match?({:ok, ^data}, ObjectAgePolicy.filter(data))
+ end
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs
new file mode 100644
index 000000000..e08eb3ba6
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs
@@ -0,0 +1,100 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic
+
+ setup do: clear_config([:mrf_rejectnonpublic])
+
+ describe "public message" do
+ test "it's allowed when address is public" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ assert {:ok, _message} = RejectNonPublic.filter(message)
+ end
+
+ test "it's allowed when cc address contain public address" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ assert {:ok, _message} = RejectNonPublic.filter(message)
+ end
+ end
+
+ describe "followers message" do
+ test "it's allowed when addrer of message in the follower addresses of user and it enabled in config" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["test-address"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true)
+ assert {:ok, _message} = RejectNonPublic.filter(message)
+ end
+
+ test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["test-address"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false)
+ assert {:reject, _} = RejectNonPublic.filter(message)
+ end
+ end
+
+ describe "direct message" do
+ test "it's allows when direct messages are allow" do
+ actor = insert(:user)
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true)
+ assert {:ok, _message} = RejectNonPublic.filter(message)
+ end
+
+ test "it's reject when direct messages aren't allow" do
+ actor = insert(:user)
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Publid~~~"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false)
+ assert {:reject, _} = RejectNonPublic.filter(message)
+ 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
new file mode 100644
index 000000000..d7dde62c4
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
@@ -0,0 +1,539 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 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.Config
+ 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
+ Config.put([:mrf_simple, :media_removal], [])
+ media_message = build_media_message()
+ local_message = build_local_message()
+
+ assert SimplePolicy.filter(media_message) == {:ok, media_message}
+ assert SimplePolicy.filter(local_message) == {:ok, local_message}
+ end
+
+ test "has a matching host" do
+ Config.put([:mrf_simple, :media_removal], ["remote.instance"])
+ media_message = build_media_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(local_message) == {:ok, local_message}
+ end
+
+ test "match with wildcard domain" do
+ Config.put([:mrf_simple, :media_removal], ["*.remote.instance"])
+ media_message = build_media_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(local_message) == {:ok, local_message}
+ end
+ end
+
+ describe "when :media_nsfw" do
+ test "is empty" do
+ Config.put([:mrf_simple, :media_nsfw], [])
+ media_message = build_media_message()
+ local_message = build_local_message()
+
+ assert SimplePolicy.filter(media_message) == {:ok, media_message}
+ assert SimplePolicy.filter(local_message) == {:ok, local_message}
+ end
+
+ test "has a matching host" do
+ Config.put([:mrf_simple, :media_nsfw], ["remote.instance"])
+ media_message = build_media_message()
+ local_message = build_local_message()
+
+ assert SimplePolicy.filter(media_message) ==
+ {:ok,
+ media_message
+ |> put_in(["object", "tag"], ["foo", "nsfw"])
+ |> put_in(["object", "sensitive"], true)}
+
+ assert SimplePolicy.filter(local_message) == {:ok, local_message}
+ end
+
+ test "match with wildcard domain" do
+ Config.put([:mrf_simple, :media_nsfw], ["*.remote.instance"])
+ media_message = build_media_message()
+ local_message = build_local_message()
+
+ assert SimplePolicy.filter(media_message) ==
+ {:ok,
+ media_message
+ |> put_in(["object", "tag"], ["foo", "nsfw"])
+ |> put_in(["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
+
+ describe "when :report_removal" do
+ test "is empty" do
+ Config.put([: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
+ Config.put([:mrf_simple, :report_removal], ["remote.instance"])
+ 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
+ Config.put([:mrf_simple, :report_removal], ["*.remote.instance"])
+ 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
+ Config.put([: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)
+
+ Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
+ 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)
+
+ Config.put([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host])
+ 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", [])
+
+ Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
+
+ 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
+
+ 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
+ Config.put([:mrf_simple, :reject], [])
+
+ remote_message = build_remote_message()
+
+ assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ end
+
+ test "activity has a matching host" do
+ Config.put([:mrf_simple, :reject], ["remote.instance"])
+
+ remote_message = build_remote_message()
+
+ assert {:reject, _} = SimplePolicy.filter(remote_message)
+ end
+
+ test "activity matches with wildcard domain" do
+ Config.put([:mrf_simple, :reject], ["*.remote.instance"])
+
+ remote_message = build_remote_message()
+
+ assert {:reject, _} = SimplePolicy.filter(remote_message)
+ end
+
+ test "actor has a matching host" do
+ Config.put([:mrf_simple, :reject], ["remote.instance"])
+
+ remote_user = build_remote_user()
+
+ assert {:reject, _} = SimplePolicy.filter(remote_user)
+ end
+ end
+
+ describe "when :followers_only" do
+ test "is empty" do
+ Config.put([: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)
+
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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
+ Config.put([: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: Config.put([:mrf_simple, :reject_deletes], [])
+
+ test "it accepts deletions even from rejected servers" do
+ Config.put([: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
+ Config.put([: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: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"])
+
+ test "it accepts deletions even from rejected servers" do
+ Config.put([: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
+ Config.put([: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: Config.put([: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: Config.put([: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.base_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/steal_emoji_policy_test.exs b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs
new file mode 100644
index 000000000..3f8222736
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs
@@ -0,0 +1,68 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Config
+ alias Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy
+
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ setup do
+ emoji_path = Path.join(Config.get([:instance, :static_dir]), "emoji/stolen")
+ File.rm_rf!(emoji_path)
+ File.mkdir!(emoji_path)
+
+ Pleroma.Emoji.reload()
+
+ on_exit(fn ->
+ File.rm_rf!(emoji_path)
+ end)
+
+ :ok
+ end
+
+ test "does nothing by default" do
+ installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
+ refute "firedfox" in installed_emoji
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}],
+ "actor" => "https://example.org/users/admin"
+ }
+ }
+
+ assert {:ok, message} == StealEmojiPolicy.filter(message)
+
+ installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
+ refute "firedfox" in installed_emoji
+ end
+
+ test "Steals emoji on unknown shortcode from allowed remote host" do
+ installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
+ refute "firedfox" in installed_emoji
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}],
+ "actor" => "https://example.org/users/admin"
+ }
+ }
+
+ clear_config([:mrf_steal_emoji, :hosts], ["example.org"])
+ clear_config([:mrf_steal_emoji, :size_limit], 284_468)
+
+ assert {:ok, message} == StealEmojiPolicy.filter(message)
+
+ installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
+ assert "firedfox" in installed_emoji
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs
new file mode 100644
index 000000000..fff66cb7e
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs
@@ -0,0 +1,33 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.ActivityPub.MRF.DropPolicy
+ alias Pleroma.Web.ActivityPub.MRF.SubchainPolicy
+
+ @message %{
+ "actor" => "https://banned.com",
+ "type" => "Create",
+ "object" => %{"content" => "hi"}
+ }
+ setup do: clear_config([:mrf_subchain, :match_actor])
+
+ test "it matches and processes subchains when the actor matches a configured target" do
+ Pleroma.Config.put([:mrf_subchain, :match_actor], %{
+ ~r/^https:\/\/banned.com/s => [DropPolicy]
+ })
+
+ {:reject, _} = SubchainPolicy.filter(@message)
+ end
+
+ test "it doesn't match and process subchains when the actor doesn't match a configured target" do
+ Pleroma.Config.put([:mrf_subchain, :match_actor], %{
+ ~r/^https:\/\/borked.com/s => [DropPolicy]
+ })
+
+ {:ok, _message} = SubchainPolicy.filter(@message)
+ 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
new file mode 100644
index 000000000..ffc30ba62
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs
@@ -0,0 +1,123 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do
+ use Pleroma.DataCase
+ 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"]
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_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]
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_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" => %{}
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_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", "nsfw"], "attachment" => ["file1"], "sensitive" => true}
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_message}
+ end
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs
new file mode 100644
index 000000000..8e1ad5bc8
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy
+
+ setup do: clear_config(:mrf_user_allowlist)
+
+ test "pass filter if allow list is empty" do
+ actor = insert(:user)
+ message = %{"actor" => actor.ap_id}
+ assert UserAllowListPolicy.filter(message) == {:ok, message}
+ end
+
+ test "pass filter if allow list isn't empty and user in allow list" do
+ actor = insert(:user)
+ Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => [actor.ap_id, "test-ap-id"]})
+ message = %{"actor" => actor.ap_id}
+ assert UserAllowListPolicy.filter(message) == {:ok, message}
+ end
+
+ test "rejected if allow list isn't empty and user not in allow list" do
+ actor = insert(:user)
+ Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]})
+ message = %{"actor" => actor.ap_id}
+ assert {:reject, _} = UserAllowListPolicy.filter(message)
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs
new file mode 100644
index 000000000..2bceb67ee
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs
@@ -0,0 +1,106 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy
+
+ describe "accept" do
+ setup do: clear_config([:mrf_vocabulary, :accept])
+
+ test "it accepts based on parent activity type" do
+ Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"])
+
+ message = %{
+ "type" => "Like",
+ "object" => "whatever"
+ }
+
+ {:ok, ^message} = VocabularyPolicy.filter(message)
+ end
+
+ test "it accepts based on child object type" do
+ Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "type" => "Note",
+ "content" => "whatever"
+ }
+ }
+
+ {:ok, ^message} = VocabularyPolicy.filter(message)
+ end
+
+ test "it does not accept disallowed child objects" do
+ Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "type" => "Article",
+ "content" => "whatever"
+ }
+ }
+
+ {:reject, _} = VocabularyPolicy.filter(message)
+ end
+
+ test "it does not accept disallowed parent types" do
+ Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "type" => "Note",
+ "content" => "whatever"
+ }
+ }
+
+ {:reject, _} = VocabularyPolicy.filter(message)
+ end
+ end
+
+ describe "reject" do
+ setup do: clear_config([:mrf_vocabulary, :reject])
+
+ test "it rejects based on parent activity type" do
+ Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"])
+
+ message = %{
+ "type" => "Like",
+ "object" => "whatever"
+ }
+
+ {:reject, _} = VocabularyPolicy.filter(message)
+ end
+
+ test "it rejects based on child object type" do
+ Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "type" => "Note",
+ "content" => "whatever"
+ }
+ }
+
+ {:reject, _} = VocabularyPolicy.filter(message)
+ end
+
+ test "it passes through objects that aren't disallowed" do
+ Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"])
+
+ message = %{
+ "type" => "Announce",
+ "object" => "whatever"
+ }
+
+ {:ok, ^message} = VocabularyPolicy.filter(message)
+ end
+ end
+end