diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | docs/configuration/cheatsheet.md | 1 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex | 21 | ||||
| -rw-r--r-- | test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs | 57 | 
4 files changed, 65 insertions, 15 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5abb9f1..19bccd115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  - Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available.  - AdminAPI: sort users so the newest are at the top.  - ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators +- MRF (`AntiFollowbotPolicy`): Bot accounts are now also considered followbots. Users can still allow bots to follow them by first following the bot.  ### Added diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 4dacdc68c..1e74d40e6 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -125,6 +125,7 @@ To add configuration to your config file, you can copy it from the base config.      * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections.      * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.      * `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed. +    * `Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy`: Drops follow requests from followbots. Users can still allow bots to follow them by first following the bot.      * `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).      * `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content.  * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index 605ca9971..97d75ecf2 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do    defp score_displayname("fedibot"), do: 1.0    defp score_displayname(_), do: 0.0 -  defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do +  defp determine_if_followbot(%User{nickname: nickname, name: displayname, actor_type: actor_type}) do      # nickname will be a binary string except when following a relay      nick_score =        if is_binary(nickname) do @@ -45,19 +45,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do          0.0        end -    nick_score + name_score +    # actor_type "Service" is a Bot account +    actor_type_score = +      if actor_type == "Service" do +        1.0 +      else +        0.0 +      end + +    nick_score + name_score + actor_type_score    end    defp determine_if_followbot(_), do: 0.0 +  defp bot_allowed?(%{"object" => target}, bot_actor) do +    %User{} = user = normalize_by_ap_id(target) + +    User.following?(user, bot_actor) +  end +    @impl true    def filter(%{"type" => "Follow", "actor" => actor_id} = message) do      %User{} = actor = normalize_by_ap_id(actor_id)      score = determine_if_followbot(actor) -    # TODO: scan biography data for keywords and score it somehow. -    if score < 0.8 do +    if score < 0.8 || bot_allowed?(message, actor) do        {:ok, message}      else        {:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"} diff --git a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs index 8192629c6..732a5a7f3 100644 --- a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do    use Pleroma.DataCase, async: true    import Pleroma.Factory +  alias Pleroma.User    alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy    describe "blocking based on attributes" do @@ -38,21 +39,55 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do        assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message)      end + +    test "matches followbots by actor_type" do +      actor = insert(:user, %{actor_type: "Service"}) +      target = insert(:user) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "type" => "Follow", +        "actor" => actor.ap_id, +        "object" => target.ap_id, +        "id" => "https://example.com/activities/1234" +      } + +      assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) +    end    end -  test "it allows non-followbots" do -    actor = insert(:user) -    target = insert(:user) +  describe "it allows" do +    test "non-followbots" do +      actor = insert(:user) +      target = insert(:user) -    message = %{ -      "@context" => "https://www.w3.org/ns/activitystreams", -      "type" => "Follow", -      "actor" => actor.ap_id, -      "object" => target.ap_id, -      "id" => "https://example.com/activities/1234" -    } +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "type" => "Follow", +        "actor" => actor.ap_id, +        "object" => target.ap_id, +        "id" => "https://example.com/activities/1234" +      } -    {:ok, _} = AntiFollowbotPolicy.filter(message) +      {:ok, _} = AntiFollowbotPolicy.filter(message) +    end + +    test "bots if the target follows the bots" do +      actor = insert(:user, %{actor_type: "Service"}) +      target = insert(:user) + +      User.follow(target, actor) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "type" => "Follow", +        "actor" => actor.ap_id, +        "object" => target.ap_id, +        "id" => "https://example.com/activities/1234" +      } + +      {:ok, _} = AntiFollowbotPolicy.filter(message) +    end    end    test "it gracefully handles nil display names" do  | 
