From dc88b6f0919cf5686af7d5b935e8ee462491704b Mon Sep 17 00:00:00 2001
From: Alex Gleason <alex@alexgleason.me>
Date: Sun, 2 Aug 2020 14:53:42 -0500
Subject: [PATCH 01/17] Add email blacklist, fixes #1404

---
 config/config.exs                |  3 ++-
 config/description.exs           |  7 +++++++
 docs/configuration/cheatsheet.md |  5 +++++
 lib/pleroma/user.ex              | 11 ++++++++++-
 test/user_test.exs               | 23 +++++++++++++++++++++++
 5 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/config/config.exs b/config/config.exs
index d31208c25..ba263bf95 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -509,7 +509,8 @@
     "user_exists",
     "users",
     "web"
-  ]
+  ],
+  email_blacklist: []
 
 config :pleroma, Oban,
   repo: Pleroma.Repo,
diff --git a/config/description.exs b/config/description.exs
index 11fbe0d78..3fe22e969 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -3021,6 +3021,7 @@
       %{
         key: :restricted_nicknames,
         type: {:list, :string},
+        description: "List of nicknames users may not register with.",
         suggestions: [
           ".well-known",
           "~",
@@ -3053,6 +3054,12 @@
           "users",
           "web"
         ]
+      },
+      %{
+        key: :email_blacklist,
+        type: {:list, :string},
+        description: "List of email domains users may not register with.",
+        suggestions: ["mailinator.com", "maildrop.cc"]
       }
     ]
   },
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index 9c768abef..1a86179f3 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -202,6 +202,11 @@ config :pleroma, :mrf_user_allowlist, %{
 * `sign_object_fetches`: Sign object fetches with HTTP signatures
 * `authorized_fetch_mode`: Require HTTP signatures for AP fetches
 
+## Pleroma.User
+
+* `restricted_nicknames`: List of nicknames users may not register with.
+* `email_blacklist`: List of email domains users may not register with.
+
 ## Pleroma.ScheduledActivity
 
 * `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index dcf6ebee2..d0cc098fe 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -676,10 +676,19 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
     |> validate_required([:name, :nickname, :password, :password_confirmation])
     |> validate_confirmation(:password)
     |> unique_constraint(:email)
+    |> validate_format(:email, @email_regex)
+    |> validate_change(:email, fn :email, email ->
+      valid? =
+        Config.get([User, :email_blacklist])
+        |> Enum.all?(fn blacklisted_domain ->
+          !String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain])
+        end)
+
+      if valid?, do: [], else: [email: "Email domain is blacklisted"]
+    end)
     |> unique_constraint(:nickname)
     |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
     |> validate_format(:nickname, local_nickname_regex())
-    |> validate_format(:email, @email_regex)
     |> validate_length(:bio, max: bio_limit)
     |> validate_length(:name, min: 1, max: name_limit)
     |> validate_length(:registration_reason, max: reason_limit)
diff --git a/test/user_test.exs b/test/user_test.exs
index 904cea536..7c45e69e7 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -490,6 +490,29 @@ test "it restricts certain nicknames" do
       refute changeset.valid?
     end
 
+    test "it blocks blacklisted email domains" do
+      clear_config([User, :email_blacklist], ["trolling.world"])
+
+      # Block with match
+      params = Map.put(@full_user_data, :email, "troll@trolling.world")
+      changeset = User.register_changeset(%User{}, params)
+      refute changeset.valid?
+
+      # Block with subdomain match
+      params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world")
+      changeset = User.register_changeset(%User{}, params)
+      refute changeset.valid?
+
+      # Pass with different domains that are similar
+      params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world")
+      changeset = User.register_changeset(%User{}, params)
+      assert changeset.valid?
+
+      params = Map.put(@full_user_data, :email, "troll@trolling.world.us")
+      changeset = User.register_changeset(%User{}, params)
+      assert changeset.valid?
+    end
+
     test "it sets the password_hash and ap_id" do
       changeset = User.register_changeset(%User{}, @full_user_data)
 

From de3bdc63adac0141500bdc2692124cd104330bda Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Mon, 3 Aug 2020 15:00:14 +0200
Subject: [PATCH 02/17] AccountControllerTest: Add test for message returned.

---
 .../controllers/account_controller_test.exs   | 29 ++++++++++++++-----
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs
index d390c3ce1..2cb388655 100644
--- a/test/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/web/mastodon_api/controllers/account_controller_test.exs
@@ -940,17 +940,32 @@ test "registers and logs in without :account_activation_required / :account_appr
       assert refresh
       assert scope == "read write follow"
 
+      clear_config([User, :email_blacklist], ["example.org"])
+
+      params = %{
+        username: "lain",
+        email: "lain@example.org",
+        password: "PlzDontHackLain",
+        bio: "Test Bio",
+        agreement: true
+      }
+
       conn =
         build_conn()
         |> put_req_header("content-type", "multipart/form-data")
         |> put_req_header("authorization", "Bearer " <> token)
-        |> post("/api/v1/accounts", %{
-          username: "lain",
-          email: "lain@example.org",
-          password: "PlzDontHackLain",
-          bio: "Test Bio",
-          agreement: true
-        })
+        |> post("/api/v1/accounts", params)
+
+      assert %{"error" => "{\"email\":[\"Email domain is blacklisted\"]}"} =
+               json_response_and_validate_schema(conn, 400)
+
+      Pleroma.Config.put([User, :email_blacklist], [])
+
+      conn =
+        build_conn()
+        |> put_req_header("content-type", "multipart/form-data")
+        |> put_req_header("authorization", "Bearer " <> token)
+        |> post("/api/v1/accounts", params)
 
       %{
         "access_token" => token,

From 016d8d6c560cb81dfe67cc660e12d2e70d0bc6af Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@FreeBSD.org>
Date: Mon, 3 Aug 2020 12:37:31 -0500
Subject: [PATCH 03/17] Consolidate construction of Rich Media Parser HTTP
 requests

---
 lib/pleroma/web/rich_media/helpers.ex         | 21 +++++++++++++++++++
 lib/pleroma/web/rich_media/parser.ex          | 20 +-----------------
 .../web/rich_media/parsers/oembed_parser.ex   |  2 +-
 3 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
index 5c7daf1a5..6210f2c5a 100644
--- a/lib/pleroma/web/rich_media/helpers.ex
+++ b/lib/pleroma/web/rich_media/helpers.ex
@@ -9,6 +9,11 @@ defmodule Pleroma.Web.RichMedia.Helpers do
   alias Pleroma.Object
   alias Pleroma.Web.RichMedia.Parser
 
+  @rich_media_options [
+    pool: :media,
+    max_body: 2_000_000
+  ]
+
   @spec validate_page_url(URI.t() | binary()) :: :ok | :error
   defp validate_page_url(page_url) when is_binary(page_url) do
     validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld])
@@ -77,4 +82,20 @@ def perform(:fetch, %Activity{} = activity) do
     fetch_data_for_activity(activity)
     :ok
   end
+
+  def rich_media_get(url) do
+    headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
+
+    options =
+      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
+        Keyword.merge(@rich_media_options,
+          recv_timeout: 2_000,
+          with_body: true
+        )
+      else
+        @rich_media_options
+      end
+
+    Pleroma.HTTP.get(url, headers, options)
+  end
 end
diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex
index c8a767935..ca592833f 100644
--- a/lib/pleroma/web/rich_media/parser.ex
+++ b/lib/pleroma/web/rich_media/parser.ex
@@ -3,11 +3,6 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.RichMedia.Parser do
-  @options [
-    pool: :media,
-    max_body: 2_000_000
-  ]
-
   defp parsers do
     Pleroma.Config.get([:rich_media, :parsers])
   end
@@ -75,21 +70,8 @@ defp get_ttl_from_image(data, url) do
   end
 
   defp parse_url(url) do
-    opts =
-      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
-        Keyword.merge(@options,
-          recv_timeout: 2_000,
-          with_body: true
-        )
-      else
-        @options
-      end
-
     try do
-      rich_media_agent = Pleroma.Application.user_agent() <> "; Bot"
-
-      {:ok, %Tesla.Env{body: html}} =
-        Pleroma.HTTP.get(url, [{"user-agent", rich_media_agent}], adapter: opts)
+      {:ok, %Tesla.Env{body: html}} = Pleroma.Web.RichMedia.Helpers.rich_media_get(url)
 
       html
       |> parse_html()
diff --git a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
index 6bdeac89c..1fe6729c3 100644
--- a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
+++ b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
@@ -22,7 +22,7 @@ defp get_oembed_url([{"link", attributes, _children} | _]) do
   end
 
   defp get_oembed_data(url) do
-    with {:ok, %Tesla.Env{body: json}} <- Pleroma.HTTP.get(url, [], adapter: [pool: :media]) do
+    with {:ok, %Tesla.Env{body: json}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do
       Jason.decode(json)
     end
   end

From 058daf498f10e58221bd29a42799f52e56a800a9 Mon Sep 17 00:00:00 2001
From: Alex Gleason <alex@alexgleason.me>
Date: Mon, 3 Aug 2020 19:57:53 -0500
Subject: [PATCH 04/17] Email blacklist: Update response phrasing

---
 lib/pleroma/user.ex                                           | 2 +-
 test/web/mastodon_api/controllers/account_controller_test.exs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index d0cc098fe..16679ac42 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -684,7 +684,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
           !String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain])
         end)
 
-      if valid?, do: [], else: [email: "Email domain is blacklisted"]
+      if valid?, do: [], else: [credentials: "Invalid credentials"]
     end)
     |> unique_constraint(:nickname)
     |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs
index 2cb388655..86e3ac3e7 100644
--- a/test/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/web/mastodon_api/controllers/account_controller_test.exs
@@ -956,7 +956,7 @@ test "registers and logs in without :account_activation_required / :account_appr
         |> put_req_header("authorization", "Bearer " <> token)
         |> post("/api/v1/accounts", params)
 
-      assert %{"error" => "{\"email\":[\"Email domain is blacklisted\"]}"} =
+      assert %{"error" => "{\"credentials\":[\"Invalid credentials\"]}"} =
                json_response_and_validate_schema(conn, 400)
 
       Pleroma.Config.put([User, :email_blacklist], [])

From 4f57e85ab9c80fb7cb51428cef978793ba22971c Mon Sep 17 00:00:00 2001
From: Alex Gleason <alex@alexgleason.me>
Date: Mon, 3 Aug 2020 22:20:49 -0500
Subject: [PATCH 05/17] Email blacklist: Update phrasing again

---
 lib/pleroma/user.ex                                           | 2 +-
 test/web/mastodon_api/controllers/account_controller_test.exs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 16679ac42..9e03373de 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -684,7 +684,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
           !String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain])
         end)
 
-      if valid?, do: [], else: [credentials: "Invalid credentials"]
+      if valid?, do: [], else: [email: "Invalid email"]
     end)
     |> unique_constraint(:nickname)
     |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs
index 86e3ac3e7..17a1e7d66 100644
--- a/test/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/web/mastodon_api/controllers/account_controller_test.exs
@@ -956,7 +956,7 @@ test "registers and logs in without :account_activation_required / :account_appr
         |> put_req_header("authorization", "Bearer " <> token)
         |> post("/api/v1/accounts", params)
 
-      assert %{"error" => "{\"credentials\":[\"Invalid credentials\"]}"} =
+      assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
                json_response_and_validate_schema(conn, 400)
 
       Pleroma.Config.put([User, :email_blacklist], [])

From 2f4289d455fbd2d949ac1e10d5ea2b9c78f15e82 Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 12:49:56 +0200
Subject: [PATCH 06/17] Changelog: Add info about email blacklist

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 129c269aa..6ae5fb928 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Added
 
+- Configuration: Added a blacklist for email servers.
 - Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
 - Chats: Added support for federated chats. For details, see the docs.
 - ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.

From 953f71bcfa25569d8b92d4047f4bdbee97e0077c Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 13:38:30 +0200
Subject: [PATCH 07/17] App Test: Make more resilient

---
 test/tasks/app_test.exs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/tasks/app_test.exs b/test/tasks/app_test.exs
index b8f03566d..71a84ac8e 100644
--- a/test/tasks/app_test.exs
+++ b/test/tasks/app_test.exs
@@ -50,13 +50,13 @@ test "with errors" do
   defp assert_app(name, redirect, scopes) do
     app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name)
 
-    assert_received {:mix_shell, :info, [message]}
+    assert_receive {:mix_shell, :info, [message]}
     assert message == "#{name} successfully created:"
 
-    assert_received {:mix_shell, :info, [message]}
+    assert_receive {:mix_shell, :info, [message]}
     assert message == "App client_id: #{app.client_id}"
 
-    assert_received {:mix_shell, :info, [message]}
+    assert_receive {:mix_shell, :info, [message]}
     assert message == "App client_secret: #{app.client_secret}"
 
     assert app.scopes == scopes

From 988ca4ab6a0d299308d96e84aa45ef63341128bf Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 14:07:10 +0200
Subject: [PATCH 08/17] Test Config: Don't have any MRFs by default

---
 config/test.exs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/config/test.exs b/config/test.exs
index db0655e73..413c7f0b9 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -120,6 +120,8 @@
 
 config :tzdata, :autoupdate, :disabled
 
+config :pleroma, :mrf, policies: []
+
 if File.exists?("./config/test.secret.exs") do
   import_config "test.secret.exs"
 else

From e92c040ad3d0cc568ea0dc4b79f207a392c7c90f Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 14:08:12 +0200
Subject: [PATCH 09/17] CommonAPITest: Add test that deactivated users can't
 post.

---
 test/web/common_api/common_api_test.exs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index 313dda21b..4ba6232dc 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -458,6 +458,11 @@ test "it adds emoji in the object" do
   end
 
   describe "posting" do
+    test "deactivated users can't post" do
+      user = insert(:user, deactivated: true)
+      assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
+    end
+
     test "it supports explicit addressing" do
       user = insert(:user)
       user_two = insert(:user)

From 0cfadcf2caf84e2db944036576bad888a9707ff1 Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 14:15:32 +0200
Subject: [PATCH 10/17] TransmogrifierTest: Add test for deactivated users

---
 test/web/activity_pub/transmogrifier_test.exs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 7d33feaf2..828964a36 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -163,6 +163,14 @@ test "it does not crash if the object in inReplyTo can't be fetched" do
              end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
     end
 
+    test "it does not work for deactivated users" do
+      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
+
+      insert(:user, ap_id: data["actor"], deactivated: true)
+
+      assert {:error, _} = Transmogrifier.handle_incoming(data)
+    end
+
     test "it works for incoming notices" do
       data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
 

From 1a00713744803824b16efd575c9c6880b1d1a57e Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 14:17:03 +0200
Subject: [PATCH 11/17] CommonValidations: Treat deactivated users as not
 present.

---
 .../object_validators/common_validations.ex    | 13 +++++++++----
 .../transmogrifier/chat_message_test.exs       | 18 ++++++++++++++++++
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
index aeef31945..bd46f8034 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
@@ -34,10 +34,15 @@ def validate_actor_presence(cng, options \\ []) do
 
     cng
     |> validate_change(field_name, fn field_name, actor ->
-      if User.get_cached_by_ap_id(actor) do
-        []
-      else
-        [{field_name, "can't find user"}]
+      case User.get_cached_by_ap_id(actor) do
+        %User{deactivated: true} ->
+          [{field_name, "user is deactivated"}]
+
+        %User{} ->
+          []
+
+        _ ->
+          [{field_name, "can't find user"}]
       end
     end)
   end
diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/web/activity_pub/transmogrifier/chat_message_test.exs
index d6736dc3e..31274c067 100644
--- a/test/web/activity_pub/transmogrifier/chat_message_test.exs
+++ b/test/web/activity_pub/transmogrifier/chat_message_test.exs
@@ -124,6 +124,24 @@ test "it fetches the actor if they aren't in our system" do
       {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data)
     end
 
+    test "it doesn't work for deactivated users" do
+      data =
+        File.read!("test/fixtures/create-chat-message.json")
+        |> Poison.decode!()
+
+      _author =
+        insert(:user,
+          ap_id: data["actor"],
+          local: false,
+          last_refreshed_at: DateTime.utc_now(),
+          deactivated: true
+        )
+
+      _recipient = insert(:user, ap_id: List.first(data["to"]), local: true)
+
+      assert {:error, _} = Transmogrifier.handle_incoming(data)
+    end
+
     test "it inserts it and creates a chat" do
       data =
         File.read!("test/fixtures/create-chat-message.json")

From 36aa34a1a8c489f74a9821095d823f8060afac5f Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Tue, 4 Aug 2020 15:08:51 +0200
Subject: [PATCH 12/17] MastodonAPITest: Do the needful

---
 test/web/mastodon_api/mastodon_api_test.exs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/web/mastodon_api/mastodon_api_test.exs
index c08be37d4..0c5a38bf6 100644
--- a/test/web/mastodon_api/mastodon_api_test.exs
+++ b/test/web/mastodon_api/mastodon_api_test.exs
@@ -17,8 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do
     test "returns error when followed user is deactivated" do
       follower = insert(:user)
       user = insert(:user, local: true, deactivated: true)
-      {:error, error} = MastodonAPI.follow(follower, user)
-      assert error == :rejected
+      assert {:error, _error} = MastodonAPI.follow(follower, user)
     end
 
     test "following for user" do

From 697e3db01c0a1ee1e18fe25946a4ef56527828e7 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@FreeBSD.org>
Date: Tue, 4 Aug 2020 08:55:40 -0500
Subject: [PATCH 13/17] Add analyze mix alias to run the same credo checks we
 use in CI

---
 mix.exs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mix.exs b/mix.exs
index 0e723c15f..63142dee7 100644
--- a/mix.exs
+++ b/mix.exs
@@ -214,7 +214,8 @@ defp aliases do
       "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
       "ecto.reset": ["ecto.drop", "ecto.setup"],
       test: ["ecto.create --quiet", "ecto.migrate", "test"],
-      docs: ["pleroma.docs", "docs"]
+      docs: ["pleroma.docs", "docs"],
+      analyze: ["credo --strict --only=warnings,todo,fixme,consistency,readability"]
     ]
   end
 

From 91fbb5b21f9d8f098c9796eb4dd917bcd1e92404 Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn <egor@kislitsyn.com>
Date: Tue, 4 Aug 2020 18:26:37 +0400
Subject: [PATCH 14/17] Fix ActivityExpirationPolicy

---
 .../web/activity_pub/mrf/activity_expiration_policy.ex     | 4 ++--
 .../activity_pub/mrf/activity_expiration_policy_test.exs   | 7 +++++++
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
index 8e47f1e02..7b4c78e0f 100644
--- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
@@ -21,8 +21,8 @@ def filter(activity) do
   @impl true
   def describe, do: {:ok, %{}}
 
-  defp local?(%{"id" => id}) do
-    String.starts_with?(id, Pleroma.Web.Endpoint.url())
+  defp local?(%{"actor" => actor}) do
+    String.starts_with?(actor, Pleroma.Web.Endpoint.url())
   end
 
   defp note?(activity) do
diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs
index 8babf49e7..f25cf8b12 100644
--- a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs
+++ b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs
@@ -7,11 +7,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
   alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
 
   @id Pleroma.Web.Endpoint.url() <> "/activities/cofe"
+  @local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe"
 
   test "adds `expires_at` property" do
     assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
              ActivityExpirationPolicy.filter(%{
                "id" => @id,
+               "actor" => @local_actor,
                "type" => "Create",
                "object" => %{"type" => "Note"}
              })
@@ -25,6 +27,7 @@ test "keeps existing `expires_at` if it less than the config setting" do
     assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} =
              ActivityExpirationPolicy.filter(%{
                "id" => @id,
+               "actor" => @local_actor,
                "type" => "Create",
                "expires_at" => expires_at,
                "object" => %{"type" => "Note"}
@@ -37,6 +40,7 @@ test "overwrites existing `expires_at` if it greater than the config setting" do
     assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
              ActivityExpirationPolicy.filter(%{
                "id" => @id,
+               "actor" => @local_actor,
                "type" => "Create",
                "expires_at" => too_distant_future,
                "object" => %{"type" => "Note"}
@@ -49,6 +53,7 @@ test "ignores remote activities" do
     assert {:ok, activity} =
              ActivityExpirationPolicy.filter(%{
                "id" => "https://example.com/123",
+               "actor" => "https://example.com/users/cofe",
                "type" => "Create",
                "object" => %{"type" => "Note"}
              })
@@ -60,6 +65,7 @@ test "ignores non-Create/Note activities" do
     assert {:ok, activity} =
              ActivityExpirationPolicy.filter(%{
                "id" => "https://example.com/123",
+               "actor" => "https://example.com/users/cofe",
                "type" => "Follow"
              })
 
@@ -68,6 +74,7 @@ test "ignores non-Create/Note activities" do
     assert {:ok, activity} =
              ActivityExpirationPolicy.filter(%{
                "id" => "https://example.com/123",
+               "actor" => "https://example.com/users/cofe",
                "type" => "Create",
                "object" => %{"type" => "Cofe"}
              })

From 184742af5eed2c48ba8518f1e114cbe0655ad467 Mon Sep 17 00:00:00 2001
From: Alex Gleason <alex@alexgleason.me>
Date: Mon, 3 Aug 2020 22:32:51 -0500
Subject: [PATCH 15/17] Unique apps.client_id for new installations, fixes
 #2022

---
 ...200804183107_add_unique_index_to_app_client_id.exs |  7 +++++++
 test/web/oauth/app_test.exs                           | 11 +++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 priv/repo/migrations/20200804183107_add_unique_index_to_app_client_id.exs

diff --git a/priv/repo/migrations/20200804183107_add_unique_index_to_app_client_id.exs b/priv/repo/migrations/20200804183107_add_unique_index_to_app_client_id.exs
new file mode 100644
index 000000000..83de18096
--- /dev/null
+++ b/priv/repo/migrations/20200804183107_add_unique_index_to_app_client_id.exs
@@ -0,0 +1,7 @@
+defmodule Pleroma.Repo.Migrations.AddUniqueIndexToAppClientId do
+  use Ecto.Migration
+
+  def change do
+    create(unique_index(:apps, [:client_id]))
+  end
+end
diff --git a/test/web/oauth/app_test.exs b/test/web/oauth/app_test.exs
index 899af648e..993a490e0 100644
--- a/test/web/oauth/app_test.exs
+++ b/test/web/oauth/app_test.exs
@@ -29,5 +29,16 @@ test "gets exist app and updates scopes" do
       assert exist_app.id == app.id
       assert exist_app.scopes == ["read", "write", "follow", "push"]
     end
+
+    test "has unique client_id" do
+      insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")
+
+      error =
+        catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop"))
+
+      assert %Ecto.ConstraintError{} = error
+      assert error.constraint == "apps_client_id_index"
+      assert error.type == :unique
+    end
   end
 end

From 079e410d6efcb39e72a238c13e52bd1898b442a2 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@FreeBSD.org>
Date: Tue, 4 Aug 2020 13:12:23 -0500
Subject: [PATCH 16/17] Add a migration to clean up activity_expirations table

---
 ...0804180322_remove_nonlocal_expirations.exs | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 priv/repo/migrations/20200804180322_remove_nonlocal_expirations.exs

diff --git a/priv/repo/migrations/20200804180322_remove_nonlocal_expirations.exs b/priv/repo/migrations/20200804180322_remove_nonlocal_expirations.exs
new file mode 100644
index 000000000..389935f0d
--- /dev/null
+++ b/priv/repo/migrations/20200804180322_remove_nonlocal_expirations.exs
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.RemoveNonlocalExpirations do
+  use Ecto.Migration
+
+  def up do
+    statement = """
+    DELETE FROM
+      activity_expirations A USING activities B
+    WHERE
+      A.activity_id = B.id
+      AND B.local = false;
+    """
+
+    execute(statement)
+  end
+
+  def down do
+    :ok
+  end
+end

From 577b11167cb55203d30c43773f40108a87b2be6d Mon Sep 17 00:00:00 2001
From: Karol Kosek <krkk@krkk.ct8.pl>
Date: Wed, 5 Aug 2020 00:01:30 +0200
Subject: [PATCH 17/17] templates/layout/app.html.eex: fix link color

---
 lib/pleroma/web/templates/layout/app.html.eex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex
index 5836ec1e0..51603fe0c 100644
--- a/lib/pleroma/web/templates/layout/app.html.eex
+++ b/lib/pleroma/web/templates/layout/app.html.eex
@@ -37,7 +37,7 @@
       }
 
       a {
-        color: color: #d8a070;
+        color: #d8a070;
         text-decoration: none;
       }