aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/pleroma/object/updater.ex40
-rw-r--r--lib/pleroma/web/activity_pub/mrf/keyword_policy.ex57
-rw-r--r--test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs131
-rw-r--r--test/pleroma/web/common_api_test.exs17
4 files changed, 232 insertions, 13 deletions
diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex
index 3d34c3f27..6381320bd 100644
--- a/lib/pleroma/object/updater.ex
+++ b/lib/pleroma/object/updater.ex
@@ -197,4 +197,44 @@ defmodule Pleroma.Object.Updater do
used_history_in_new_object?: used_history_in_new_object?
}
end
+
+ defp for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do
+ new_items =
+ Enum.map(items, fun)
+ |> Enum.reduce_while(
+ {:ok, []},
+ fn
+ {:ok, item}, {:ok, acc} -> {:cont, {:ok, acc ++ [item]}}
+ e, _acc -> {:halt, e}
+ end
+ )
+
+ case new_items do
+ {:ok, items} -> {:ok, Map.put(history, "orderedItems", items)}
+ e -> e
+ end
+ end
+
+ defp for_each_history_item(history, _, _) do
+ {:ok, history}
+ end
+
+ def do_with_history(object, fun) do
+ with history <- object["formerRepresentations"],
+ object <- Map.drop(object, ["formerRepresentations"]),
+ {_, {:ok, object}} <- {:main_body, fun.(object)},
+ {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do
+ object =
+ if history do
+ Map.put(object, "formerRepresentations", history)
+ else
+ object
+ end
+
+ {:ok, object}
+ else
+ {:main_body, e} -> e
+ {:history_items, e} -> e
+ end
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
index 00b64744f..687ec6c2f 100644
--- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
@@ -27,24 +27,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end
defp check_reject(%{"object" => %{} = object} = message) do
- payload = object_payload(object)
-
- if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
- string_matches?(payload, pattern)
- end) do
- {:reject, "[KeywordPolicy] Matches with rejected keyword"}
- else
+ with {:ok, _new_object} <-
+ Pleroma.Object.Updater.do_with_history(object, fn object ->
+ payload = object_payload(object)
+
+ if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
+ string_matches?(payload, pattern)
+ end) do
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"}
+ else
+ {:ok, message}
+ end
+ end) do
{:ok, message}
+ else
+ e -> e
end
end
- defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
- payload = object_payload(object)
+ defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do
+ check_keyword = fn object ->
+ payload = object_payload(object)
- if Pleroma.Constants.as_public() in to and
- Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
+ if Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
string_matches?(payload, pattern)
end) do
+ {:should_delist, nil}
+ else
+ {:ok, %{}}
+ end
+ end
+
+ should_delist? = fn object ->
+ with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check_keyword) do
+ false
+ else
+ _ -> true
+ end
+ end
+
+ if Pleroma.Constants.as_public() in to and should_delist?.(object) do
to = List.delete(to, Pleroma.Constants.as_public())
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
@@ -59,8 +81,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end
end
+ defp check_ftl_removal(message) do
+ {:ok, message}
+ end
+
defp check_replace(%{"object" => %{} = object} = message) do
- object =
+ replace_kw = fn object ->
["content", "name", "summary"]
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|> Enum.reduce(object, fn field, object ->
@@ -73,6 +99,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
Map.put(object, field, data)
end)
+ |> (fn object -> {:ok, object} end).()
+ end
+
+ {:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw)
message = Map.put(message, "object", object)
@@ -80,7 +110,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end
@impl true
- def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do
+ def filter(%{"type" => type, "object" => %{"content" => _content}} = message)
+ when type in ["Create", "Update"] do
with {:ok, message} <- check_reject(message),
{:ok, message} <- check_ftl_removal(message),
{:ok, message} <- check_replace(message) do
diff --git a/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs
index bfa8e8f59..a0e77d7b9 100644
--- a/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs
@@ -79,6 +79,54 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
KeywordPolicy.filter(message)
end)
end
+
+ test "rejects if string matches in history" do
+ clear_config([:mrf_keyword, :reject], ["pun"])
+
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "content" => "just a daily reminder that compLAINer is a good",
+ "summary" => "",
+ "formerRepresentations" => %{
+ "type" => "OrderedCollection",
+ "orderedItems" => [
+ %{
+ "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 Updates" do
+ clear_config([:mrf_keyword, :reject], ["pun"])
+
+ message = %{
+ "type" => "Update",
+ "object" => %{
+ "content" => "just a daily reminder that compLAINer is a good",
+ "summary" => "",
+ "formerRepresentations" => %{
+ "type" => "OrderedCollection",
+ "orderedItems" => [
+ %{
+ "content" => "just a daily reminder that compLAINer is a good pun",
+ "summary" => ""
+ }
+ ]
+ }
+ }
+ }
+
+ assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
+ KeywordPolicy.filter(message)
+ end
end
describe "delisting from ftl based on keywords" do
@@ -157,6 +205,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
end)
end
+
+ test "delists if string matches in history" do
+ clear_config([: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",
+ "summary" => "",
+ "formerRepresentations" => %{
+ "orderedItems" => [
+ %{
+ "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
end
describe "replacing keywords" do
@@ -221,5 +294,63 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
result == "ZFS is free software"
end)
end
+
+ test "replaces keyword if string matches in history" do
+ clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
+
+ message = %{
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{
+ "content" => "ZFS is opensource",
+ "summary" => "",
+ "formerRepresentations" => %{
+ "type" => "OrderedCollection",
+ "orderedItems" => [
+ %{"content" => "ZFS is opensource mew mew", "summary" => ""}
+ ]
+ }
+ }
+ }
+
+ {:ok,
+ %{
+ "object" => %{
+ "content" => "ZFS is free software",
+ "formerRepresentations" => %{
+ "orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
+ }
+ }
+ }} = KeywordPolicy.filter(message)
+ end
+
+ test "replaces keyword in Updates" do
+ clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
+
+ message = %{
+ "type" => "Update",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "object" => %{
+ "content" => "ZFS is opensource",
+ "summary" => "",
+ "formerRepresentations" => %{
+ "type" => "OrderedCollection",
+ "orderedItems" => [
+ %{"content" => "ZFS is opensource mew mew", "summary" => ""}
+ ]
+ }
+ }
+ }
+
+ {:ok,
+ %{
+ "object" => %{
+ "content" => "ZFS is free software",
+ "formerRepresentations" => %{
+ "orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
+ }
+ }
+ }} = KeywordPolicy.filter(message)
+ end
end
end
diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs
index fcfcb9a2e..c87e64d9e 100644
--- a/test/pleroma/web/common_api_test.exs
+++ b/test/pleroma/web/common_api_test.exs
@@ -1647,5 +1647,22 @@ defmodule Pleroma.Web.CommonAPITest do
assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
end
+
+ test "respects MRF" do
+ user = insert(:user)
+
+ clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
+ clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
+ assert Object.normalize(activity).data["summary"] == "mewmew 1"
+
+ {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
+
+ updated_object = Object.normalize(updated)
+ assert updated_object.data["content"] == "mewmew 2"
+ assert Map.get(updated_object.data, "summary", "") == ""
+ assert Map.has_key?(updated_object.data, "updated")
+ end
end
end