Add keyword policy
authorrinpatch <rinpatch@sdf.org>
Fri, 8 Feb 2019 09:38:24 +0000 (12:38 +0300)
committerrinpatch <rinpatch@sdf.org>
Fri, 8 Feb 2019 09:38:24 +0000 (12:38 +0300)
docs/config.md
lib/pleroma/web/activity_pub/mrf/keyword_policy.ex [new file with mode: 0644]
lib/pleroma/web/nodeinfo/nodeinfo_controller.ex

index e042d216fdeb502097ddca98f078930236f81fd8..55d618925f978198a02f7ea3bd27fbf2cca01169 100644 (file)
@@ -151,6 +151,11 @@ This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:i
 * `delist_threshold`: Number of mentioned users after which the message gets delisted (the message can still be seen, but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable.
 * `reject_threshold`: Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.
 
+## :mrf_keyword
+* `reject`: A list of patterns which result in message being rejected, each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html)
+* `ftl_removal`: A list of patterns which result in message being removed from federated timelines(a.k.a unlisted), each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html)
+* `replace`: A list of tuples containing `{pattern, replacement`, `pattern` can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html)
+
 ## :media_proxy
 * `enabled`: Enables proxying of remote media to the instance’s proxy
 * `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
new file mode 100644 (file)
index 0000000..6e2673e
--- /dev/null
@@ -0,0 +1,74 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
+  @behaviour Pleroma.Web.ActivityPub.MRF
+  defp string_matches?(string, pattern) when is_binary(pattern) do
+    String.contains?(string, pattern)
+  end
+
+  defp string_matches?(string, pattern) do
+    String.match?(string, pattern)
+  end
+
+  defp check_reject(%{"object" => %{"content" => content}} = message) do
+    if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
+         string_matches?(content, pattern)
+       end) do
+      {:reject, nil}
+    else
+      {:ok, message}
+    end
+  end
+
+  defp check_ftl_removal(%{"to" => to, "object" => %{"content" => content}} = message) do
+    if "https://www.w3.org/ns/activitystreams#Public" in to and
+         Enum.any?(Pleroma.Config.get([:mrf_keyword, :ftl_removal]), fn pattern ->
+           string_matches?(content, pattern)
+         end) do
+      to = List.delete(to, "https://www.w3.org/ns/activitystreams#Public")
+      cc = ["https://www.w3.org/ns/activitystreams#Public" | [message["cc"] || []]]
+
+      message =
+        message
+        |> Map.put("to", to)
+        |> Map.put("cc", cc)
+
+      IO.inspect(message)
+      {:ok, message}
+    else
+      {:ok, message}
+    end
+  end
+
+  defp check_replace(%{"object" => %{"content" => content}} = message) do
+    content =
+      Enum.reduce(Pleroma.Config.get([:mrf_keyword, :replace]), content, fn {pattern, replacement},
+                                                                            acc ->
+        String.replace(acc, pattern, replacement)
+      end)
+
+    {:ok, put_in(message["object"]["content"], content)}
+  end
+
+  @impl true
+  def filter(%{"object" => %{"content" => nil}} = message) do
+    {:ok, message}
+  end
+
+  @impl true
+  def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do
+    with {:ok, message} <- check_reject(message),
+         {:ok, message} <- check_ftl_removal(message),
+         {:ok, message} <- check_replace(message) do
+      {:ok, message}
+    else
+      _e ->
+        {:reject, nil}
+    end
+  end
+
+  @impl true
+  def filter(message), do: {:ok, message}
+end
index 21694a5ee4983b0dc383c19705d8302c1f676d45..7c24d4761af061e4c5bc34a13f31be0311739a22 100644 (file)
@@ -44,6 +44,33 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
       Application.get_env(:pleroma, :mrf_simple)
       |> Enum.into(%{})
 
+    # This horror is needed to convert regex sigils to strings
+    mrf_keyword =
+      Application.get_env(:pleroma, :mrf_keyword)
+      |> Enum.map(fn {key, value} ->
+        {key,
+         Enum.map(value, fn
+           {pattern, replacement} ->
+             %{
+               "pattern" =>
+                 if not is_binary(pattern) do
+                   inspect(pattern)
+                 else
+                   pattern
+                 end,
+               "replacement" => replacement
+             }
+
+           pattern ->
+             if not is_binary(pattern) do
+               inspect(pattern)
+             else
+               pattern
+             end
+         end)}
+      end)
+      |> Enum.into(%{})
+
     mrf_policies =
       MRF.get_policies()
       |> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)
@@ -73,6 +100,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
         %{
           mrf_policies: mrf_policies,
           mrf_simple: mrf_simple,
+          mrf_keyword: mrf_keyword,
           mrf_user_allowlist: mrf_user_allowlist,
           quarantined_instances: quarantined
         }