Add ability to obfuscate domains in MRF transparency
authorFloatingGhost <hannah@coffee-and-dreams.uk>
Sat, 27 Aug 2022 10:57:57 +0000 (11:57 +0100)
committerFloatingGhost <hannah@coffee-and-dreams.uk>
Sat, 27 Aug 2022 10:57:57 +0000 (11:57 +0100)
CHANGELOG.md
config/config.exs
docs/docs/configuration/cheatsheet.md
lib/pleroma/web/activity_pub/mrf.ex
lib/pleroma/web/activity_pub/mrf/simple_policy.ex
test/pleroma/web/activity_pub/mrf/simple_policy_test.exs

index df34d2bb6e6092958d516fc8cdaee1a9e294951c..183a60d1021c38fbd08a65268e42c55c43ca3e8c 100644 (file)
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - support for fedibird-fe, and non-breaking API parity for it to function
 - support for setting instance languages in metadata
 - support for reusing oauth tokens, and not requiring new authorizations
+- the ability to obfuscate domains in your MRF descriptions
 
 ### Changed
 - MFM parsing is now done on the backend by a modified version of ilja's parser -> https://akkoma.dev/AkkomaGang/mfm-parser
index ec82e872a9f5787a7aa78859e4cd0b241f3c974d..5ae7a33a2a83c58911146d6ba0bafa8c2144601e 100644 (file)
@@ -794,7 +794,8 @@ config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
 config :pleroma, :mrf,
   policies: [Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy, Pleroma.Web.ActivityPub.MRF.TagPolicy],
   transparency: true,
-  transparency_exclusions: []
+  transparency_exclusions: [],
+  transparency_obfuscate_domains: []
 
 config :ex_aws, http_client: Pleroma.HTTP.ExAws
 
index 8fa188de18ba0e8bf1e048671775d231a776619f..a29db208c21d20be82f2d36a588f065c339a2244 100644 (file)
@@ -120,6 +120,7 @@ To add configuration to your config file, you can copy it from the base config.
     * `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).
 * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
 * `transparency_exclusions`: Exclude specific instance names from MRF transparency.  The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
+* `transparency_obfuscate_domains`: Show domains with `*` in the middle, to censor them if needed. For example, `ridingho.me` will show as `rid*****.me`
 
 ## Federation
 ### MRF policies
index bd6f6777f3aabacbfc30ad303320b38b8da1e5c0..5606dac832ed3916a0252cdddb6a5450c268effc 100644 (file)
@@ -41,6 +41,16 @@ defmodule Pleroma.Web.ActivityPub.MRF do
           suggestions: [
             "exclusion.com"
           ]
+        },
+        %{
+          key: :transparency_obfuscate_domains,
+          label: "MRF domain obfuscation",
+          type: {:list, :string},
+          description:
+            "Obfuscate domains in MRF transparency. This is useful if the domain you're blocking contains words you don't want displayed, but still want to disclose the MRF settings.",
+          suggestions: [
+            "badword.com"
+          ]
         }
       ]
     }
index c631cc85fad4c814107a8c6d369dfcf66cd82537..415c5d2ddb87937605684f547bba6fe457426f03 100644 (file)
@@ -256,10 +256,35 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
 
   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} ->
@@ -269,7 +294,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
     mrf_simple =
       mrf_simple_excluded
       |> Enum.map(fn {rule, instances} ->
-        {rule, Enum.map(instances, fn {host, _} -> host end)}
+        {rule, Enum.map(instances, fn {host, _} -> maybe_obfuscate(host, obfuscations) end)}
       end)
       |> Map.new()
 
@@ -286,7 +311,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
       |> Enum.map(fn {rule, instances} ->
         instances =
           instances
-          |> Enum.map(fn {host, reason} -> {host, %{"reason" => reason}} end)
+          |> Enum.map(fn {host, reason} ->
+            {maybe_obfuscate(host, obfuscations), %{"reason" => reason}}
+          end)
           |> Map.new()
 
         {rule, instances}
index 0a0f51bdbb8c91bf27dc903f216579414e900887..0569bfed309e8ed2cacbcc7d8950e91f2f528062 100644 (file)
@@ -216,6 +216,43 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
     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)