Add :reject_deletes option to SimplePolicy
authorAlex Gleason <alex@alexgleason.me>
Mon, 13 Apr 2020 18:48:32 +0000 (13:48 -0500)
committerAlex Gleason <alex@alexgleason.me>
Mon, 20 Apr 2020 15:11:06 +0000 (10:11 -0500)
CHANGELOG.md
config/config.exs
config/description.exs
docs/configuration/mrf.md
lib/pleroma/web/activity_pub/mrf/simple_policy.ex
test/web/activity_pub/mrf/simple_policy_test.exs

index 36897503ac1fa3fbfb3a0601c2353defb0a7a443..cd2536f9eca2239b8d91eba453965bc795b04115 100644 (file)
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - NodeInfo: `pleroma_emoji_reactions` to the `features` list.
 - Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
 - New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
+- Added `:reject_deletes` group to SimplePolicy
 <details>
   <summary>API Changes</summary>
 - Mastodon API: Support for `include_types` in `/api/v1/notifications`.
@@ -20,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Fixed
 - Support pagination in conversations API
+- **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again
 
 ## [unreleased-patch]
 
index 232a91bf132c8063f8f0f730121bb68eade6ad6f..9a6b93a378952fa658c9df51aebc7ea42313ba2b 100644 (file)
@@ -334,7 +334,8 @@ config :pleroma, :mrf_simple,
   reject: [],
   accept: [],
   avatar_removal: [],
-  banner_removal: []
+  banner_removal: [],
+  reject_deletes: []
 
 config :pleroma, :mrf_keyword,
   reject: [],
index 642f1a3ce9d2be466a15fe24495dfb189bf8ed0b..9d8e3b93cd53409909c309d41a8dc74277bcb836 100644 (file)
@@ -1317,13 +1317,13 @@ config :pleroma, :config_description, [
       %{
         key: :reject,
         type: {:list, :string},
-        description: "List of instances to reject any activities from",
+        description: "List of instances to reject activities from (except deletes)",
         suggestions: ["example.com", "*.example.com"]
       },
       %{
         key: :accept,
         type: {:list, :string},
-        description: "List of instances to accept any activities from",
+        description: "List of instances to only accept activities from (except deletes)",
         suggestions: ["example.com", "*.example.com"]
       },
       %{
@@ -1343,6 +1343,12 @@ config :pleroma, :config_description, [
         type: {:list, :string},
         description: "List of instances to strip banners from",
         suggestions: ["example.com", "*.example.com"]
+      },
+      %{
+        key: :reject_deletes,
+        type: {:list, :string},
+        description: "List of instances to reject deletions from",
+        suggestions: ["example.com", "*.example.com"]
       }
     ]
   },
index c3957c2551fce3b2ccac8f4445eeeeff31270836..2eb9631bd8e397bd1ad9fa6bd43ed77662987f82 100644 (file)
@@ -43,9 +43,10 @@ Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_si
 
 * `media_removal`: Servers in this group will have media stripped from incoming messages.
 * `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
-* `reject`: Servers in this group will have their messages rejected.
+* `reject`: Servers in this group will have their messages (except deletions) rejected.
 * `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
 * `report_removal`: Servers in this group will have their reports (flags) rejected.
+* `reject_deletes`: Deletion requests will be rejected from these servers.
 
 Servers should be configured as lists.
 
index b23f263f5a151f612f8676f70bf74534742cfb0f..b7dcb1b861026622590103e1451f029fe75ecaf7 100644 (file)
@@ -149,7 +149,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
   defp check_banner_removal(_actor_info, object), do: {:ok, object}
 
   @impl true
-  def filter(%{"type" => "Delete"} = object), do: {:ok, object}
+  def filter(%{"type" => "Delete", "actor" => actor} = object) do
+    %{host: actor_host} = URI.parse(actor)
+
+    reject_deletes =
+      Pleroma.Config.get([:mrf_simple, :reject_deletes])
+      |> MRF.subdomains_regex()
+
+    if MRF.subdomain_match?(reject_deletes, actor_host) do
+      {:reject, nil}
+    else
+      {:ok, object}
+    end
+  end
 
   @impl true
   def filter(%{"actor" => actor} = object) do
index eaa595706acf50fc9680a4d0a426321378be40c0..b7b9bc6a2f8907caa80684b50c6a0b37b6237c98 100644 (file)
@@ -17,7 +17,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
             reject: [],
             accept: [],
             avatar_removal: [],
-            banner_removal: []
+            banner_removal: [],
+            reject_deletes: []
           )
 
   describe "when :media_removal" do
@@ -258,14 +259,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
 
       assert SimplePolicy.filter(remote_user) == {:reject, nil}
     end
-
-    test "always accept deletions" do
-      Config.put([:mrf_simple, :reject], ["remote.instance"])
-
-      deletion_message = build_remote_deletion_message()
-
-      assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
-    end
   end
 
   describe "when :accept" do
@@ -316,14 +309,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
 
       assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
     end
-
-    test "always accept deletions" 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 :avatar_removal" do
@@ -398,6 +383,66 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     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 SimplePolicy.filter(deletion_message) == {:reject, nil}
+    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 SimplePolicy.filter(deletion_message) == {:reject, nil}
+    end
+  end
+
   defp build_local_message do
     %{
       "actor" => "#{Pleroma.Web.base_url()}/users/alice",