summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Strizhakov <alex.strizhakov@gmail.com>2019-07-22 14:33:58 +0000
committerAriadne Conill <ariadne@dereferenced.org>2019-08-14 01:50:21 +0000
commitb9def3758ae97490f8c46a77a03c344568106376 (patch)
tree725aa7f5bcb3d6702579e14e76fcdced6478000d
parente08b97853f20c6d0571cf451d18defd0bdb2393f (diff)
Feature/1087 wildcard option for blocks
-rw-r--r--lib/pleroma/user.ex12
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex10
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex55
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex9
-rw-r--r--test/user_test.exs42
-rw-r--r--test/web/activity_pub/mrf/mrf_test.exs46
-rw-r--r--test/web/activity_pub/mrf/simple_policy_test.exs93
7 files changed, 245 insertions, 22 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index f7191762f..ed5403219 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -836,15 +836,15 @@ defmodule Pleroma.User do
def mutes?(nil, _), do: false
def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
- def blocks?(user, %{ap_id: ap_id}) do
- blocks = user.info.blocks
- domain_blocks = user.info.domain_blocks
+ def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
+ blocks = info.blocks
+
+ domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(info.domain_blocks)
+
%{host: host} = URI.parse(ap_id)
Enum.member?(blocks, ap_id) ||
- Enum.any?(domain_blocks, fn domain ->
- host == domain
- end)
+ Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
end
def subscribed_to?(user, %{ap_id: ap_id}) do
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index 10ceef715..dd204b21c 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -25,4 +25,14 @@ defmodule Pleroma.Web.ActivityPub.MRF do
defp get_policies(policy) when is_atom(policy), do: [policy]
defp get_policies(policies) when is_list(policies), do: policies
defp get_policies(_), do: []
+
+ @spec subdomains_regex([String.t()]) :: [Regex.t()]
+ def subdomains_regex(domains) when is_list(domains) do
+ for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$)
+ end
+
+ @spec subdomain_match?([Regex.t()], String.t()) :: boolean()
+ def subdomain_match?(domains, host) do
+ Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 433d23c5f..2cf63d3db 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -4,22 +4,29 @@
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.MRF
@moduledoc "Filter activities depending on their origin instance"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour MRF
defp check_accept(%{host: actor_host} = _actor_info, object) do
- accepts = Pleroma.Config.get([:mrf_simple, :accept])
+ accepts =
+ Pleroma.Config.get([:mrf_simple, :accept])
+ |> MRF.subdomains_regex()
cond do
accepts == [] -> {:ok, object}
actor_host == Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
- Enum.member?(accepts, actor_host) -> {:ok, object}
+ MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
true -> {:reject, nil}
end
end
defp check_reject(%{host: actor_host} = _actor_info, object) do
- if Enum.member?(Pleroma.Config.get([:mrf_simple, :reject]), actor_host) do
+ rejects =
+ Pleroma.Config.get([:mrf_simple, :reject])
+ |> MRF.subdomains_regex()
+
+ if MRF.subdomain_match?(rejects, actor_host) do
{:reject, nil}
else
{:ok, object}
@@ -31,8 +38,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
%{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
)
when length(child_attachment) > 0 do
+ media_removal =
+ Pleroma.Config.get([:mrf_simple, :media_removal])
+ |> MRF.subdomains_regex()
+
object =
- if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_removal]), actor_host) do
+ if MRF.subdomain_match?(media_removal, actor_host) do
child_object = Map.delete(object["object"], "attachment")
Map.put(object, "object", child_object)
else
@@ -51,8 +62,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
"object" => child_object
} = object
) do
+ media_nsfw =
+ Pleroma.Config.get([:mrf_simple, :media_nsfw])
+ |> MRF.subdomains_regex()
+
object =
- if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
+ if MRF.subdomain_match?(media_nsfw, actor_host) do
tags = (child_object["tag"] || []) ++ ["nsfw"]
child_object = Map.put(child_object, "tag", tags)
child_object = Map.put(child_object, "sensitive", true)
@@ -67,12 +82,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_media_nsfw(_actor_info, object), do: {:ok, object}
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
+ timeline_removal =
+ Pleroma.Config.get([:mrf_simple, :federated_timeline_removal])
+ |> MRF.subdomains_regex()
+
object =
- with true <-
- Enum.member?(
- Pleroma.Config.get([:mrf_simple, :federated_timeline_removal]),
- actor_host
- ),
+ with true <- MRF.subdomain_match?(timeline_removal, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]),
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
to =
@@ -94,7 +109,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
end
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
- if actor_host in Pleroma.Config.get([:mrf_simple, :report_removal]) do
+ report_removal =
+ Pleroma.Config.get([:mrf_simple, :report_removal])
+ |> MRF.subdomains_regex()
+
+ if MRF.subdomain_match?(report_removal, actor_host) do
{:reject, nil}
else
{:ok, object}
@@ -104,7 +123,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_report_removal(_actor_info, object), do: {:ok, object}
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
- if actor_host in Pleroma.Config.get([:mrf_simple, :avatar_removal]) do
+ avatar_removal =
+ Pleroma.Config.get([:mrf_simple, :avatar_removal])
+ |> MRF.subdomains_regex()
+
+ if MRF.subdomain_match?(avatar_removal, actor_host) do
{:ok, Map.delete(object, "icon")}
else
{:ok, object}
@@ -114,7 +137,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_avatar_removal(_actor_info, object), do: {:ok, object}
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
- if actor_host in Pleroma.Config.get([:mrf_simple, :banner_removal]) do
+ banner_removal =
+ Pleroma.Config.get([:mrf_simple, :banner_removal])
+ |> MRF.subdomains_regex()
+
+ if MRF.subdomain_match?(banner_removal, actor_host) do
{:ok, Map.delete(object, "image")}
else
{:ok, object}
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index a05e03263..3d181f57c 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -87,8 +87,13 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
if public do
true
else
- inbox_info = URI.parse(inbox)
- !Enum.member?(Config.get([:instance, :quarantined_instances], []), inbox_info.host)
+ %{host: host} = URI.parse(inbox)
+
+ quarantined_instances =
+ Config.get([:instance, :quarantined_instances], [])
+ |> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
+
+ !Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host)
end
end
diff --git a/test/user_test.exs b/test/user_test.exs
index 198a97fae..7bd745104 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -799,6 +799,48 @@ defmodule Pleroma.UserTest do
assert User.blocks?(user, collateral_user)
end
+ test "does not block domain with same end" do
+ user = insert(:user)
+
+ collateral_user =
+ insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
+
+ {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
+
+ refute User.blocks?(user, collateral_user)
+ end
+
+ test "does not block domain with same end if wildcard added" do
+ user = insert(:user)
+
+ collateral_user =
+ insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
+
+ {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
+
+ refute User.blocks?(user, collateral_user)
+ end
+
+ test "blocks domain with wildcard for subdomain" do
+ user = insert(:user)
+
+ user_from_subdomain =
+ insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"})
+
+ user_with_two_subdomains =
+ insert(:user, %{
+ ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully"
+ })
+
+ user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
+
+ {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
+
+ assert User.blocks?(user, user_from_subdomain)
+ assert User.blocks?(user, user_with_two_subdomains)
+ assert User.blocks?(user, user_domain)
+ end
+
test "unblocks domains" do
user = insert(:user)
collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs
new file mode 100644
index 000000000..a9cdf5317
--- /dev/null
+++ b/test/web/activity_pub/mrf/mrf_test.exs
@@ -0,0 +1,46 @@
+defmodule Pleroma.Web.ActivityPub.MRFTest do
+ use ExUnit.Case, async: true
+ alias Pleroma.Web.ActivityPub.MRF
+
+ test "subdomains_regex/1" do
+ assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
+ ~r/^unsafe.tld$/,
+ ~r/^(.*\.)*unsafe.tld$/
+ ]
+ end
+
+ describe "subdomain_match/2" do
+ test "common domains" do
+ regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
+
+ assert regexes == [~r/^unsafe.tld$/, ~r/^unsafe2.tld$/]
+
+ assert MRF.subdomain_match?(regexes, "unsafe.tld")
+ assert MRF.subdomain_match?(regexes, "unsafe2.tld")
+
+ refute MRF.subdomain_match?(regexes, "example.com")
+ end
+
+ test "wildcard domains with one subdomain" do
+ regexes = MRF.subdomains_regex(["*.unsafe.tld"])
+
+ assert regexes == [~r/^(.*\.)*unsafe.tld$/]
+
+ assert MRF.subdomain_match?(regexes, "unsafe.tld")
+ assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
+ refute MRF.subdomain_match?(regexes, "anotherunsafe.tld")
+ refute MRF.subdomain_match?(regexes, "unsafe.tldanother")
+ end
+
+ test "wildcard domains with two subdomains" do
+ regexes = MRF.subdomains_regex(["*.unsafe.tld"])
+
+ assert regexes == [~r/^(.*\.)*unsafe.tld$/]
+
+ assert MRF.subdomain_match?(regexes, "unsafe.tld")
+ assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
+ refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld")
+ refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother")
+ end
+ end
+end
diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs
index 0fd68e103..8e86d2219 100644
--- a/test/web/activity_pub/mrf/simple_policy_test.exs
+++ b/test/web/activity_pub/mrf/simple_policy_test.exs
@@ -49,6 +49,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
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
@@ -74,6 +87,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
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
@@ -106,6 +133,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(report_message) == {:reject, nil}
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 SimplePolicy.filter(report_message) == {:reject, nil}
+ assert SimplePolicy.filter(local_message) == {:ok, local_message}
+ end
end
defp build_report_message do
@@ -146,6 +182,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
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()
@@ -192,6 +249,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(remote_message) == {:reject, nil}
end
+
+ test "match with wildcard domain" do
+ Config.put([:mrf_simple, :reject], ["*.remote.instance"])
+
+ remote_message = build_remote_message()
+
+ assert SimplePolicy.filter(remote_message) == {:reject, nil}
+ end
end
describe "when :accept" do
@@ -224,6 +289,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
end
+
+ test "match 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
end
describe "when :avatar_removal" do
@@ -251,6 +326,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
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
@@ -278,6 +362,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
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
defp build_local_message do