Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
6 KB
Referenced Files
None
Subscribers
None
diff --git a/changelog.d/c2s-rate-limit.security b/changelog.d/c2s-rate-limit.security
new file mode 100644
index 000000000..8cab4a309
--- /dev/null
+++ b/changelog.d/c2s-rate-limit.security
@@ -0,0 +1 @@
+Rate limit ActivityPub C2S outbox posts and emoji reactions
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index a45f7bf27..ae45bc748 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -26,6 +26,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug
alias Pleroma.Web.Plugs.FederatingPlug
alias Pleroma.Web.Plugs.OAuthScopesPlug
+ alias Pleroma.Web.Plugs.RateLimiter
require Logger
@@ -66,6 +67,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
when action in [:followers, :following]
)
+ # Shares the per-user bucket with Mastodon API status actions and emoji reactions.
+ plug(RateLimiter, [name: :statuses_actions] when action == :update_outbox)
+
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:upload_media])
plug(
diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
index 82abfce7f..57e3a2d5b 100644
--- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.Plugs.OAuthScopesPlug
+ alias Pleroma.Web.Plugs.RateLimiter
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action in [:create, :delete])
@@ -22,6 +23,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
when action == :index
)
+ plug(
+ RateLimiter,
+ [name: :status_id_action, bucket_name: "status_id_action:emoji_react", params: [:id]]
+ when action in [:create, :delete]
+ )
+
+ plug(RateLimiter, [name: :statuses_actions] when action in [:create, :delete])
+
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.EmojiReactionOperation
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
index e1376b460..f5c0e6a82 100644
--- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -2709,6 +2709,43 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert result == "Character limit (5 characters) exceeded, contains 11 characters"
end
+
+ test "it is rate limited", %{conn: conn, activity: activity} do
+ clear_config([:rate_limit, :statuses_actions], {60_000, 2})
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> put_req_header("content-type", "application/activity+json")
+
+ for _ <- 1..2 do
+ conn
+ |> post("/users/#{user.nickname}/outbox", activity)
+ |> json_response(201)
+ end
+
+ assert conn
+ |> post("/users/#{user.nickname}/outbox", activity)
+ |> json_response(429) == %{"error" => "Throttled"}
+ end
+
+ test "it shares the rate limit with the Mastodon API", %{activity: activity} do
+ clear_config([:rate_limit, :statuses_actions], {60_000, 2})
+ %{user: user, conn: conn} = oauth_access(["write"])
+
+ for _ <- 1..2 do
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses", %{status: "cofe"})
+ |> json_response(200)
+ end
+
+ assert conn
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/users/#{user.nickname}/outbox", activity)
+ |> json_response(429) == %{"error" => "Throttled"}
+ end
end
describe "/relay/followers" do
diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
index 8fcdb233b..12b037a21 100644
--- a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
+++ b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
@@ -477,4 +477,53 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do
|> get("/api/v1/pleroma/statuses/#{activity_id}/reactions/🐈")
|> json_response_and_validate_schema(404) == %{"error" => "Record not found"}
end
+
+ describe "rate limiting" do
+ test "PUT and DELETE share a per-status limit", %{conn: conn} do
+ clear_config([:rate_limit, :status_id_action], {60_000, 2})
+ user = insert(:user)
+ %{id: activity_id} = insert(:note_activity)
+ %{id: other_activity_id} = insert(:note_activity)
+ conn = prepare_conn_of_user(conn, user)
+
+ conn
+ |> put("/api/v1/pleroma/statuses/#{activity_id}/reactions/☕")
+ |> json_response_and_validate_schema(200)
+
+ conn
+ |> delete("/api/v1/pleroma/statuses/#{activity_id}/reactions/☕")
+ |> json_response_and_validate_schema(200)
+
+ assert conn
+ |> put("/api/v1/pleroma/statuses/#{activity_id}/reactions/🐈")
+ |> json_response(429) == %{"error" => "Throttled"}
+
+ conn
+ |> put("/api/v1/pleroma/statuses/#{other_activity_id}/reactions/🐈")
+ |> json_response_and_validate_schema(200)
+ end
+
+ test "PUT and DELETE count against statuses_actions", %{conn: conn} do
+ clear_config([:rate_limit, :statuses_actions], {60_000, 2})
+ user = insert(:user)
+ [first, second, third] = insert_list(3, :note_activity)
+ conn = prepare_conn_of_user(conn, user)
+
+ conn
+ |> put("/api/v1/pleroma/statuses/#{first.id}/reactions/☕")
+ |> json_response_and_validate_schema(200)
+
+ conn
+ |> delete("/api/v1/pleroma/statuses/#{first.id}/reactions/☕")
+ |> json_response_and_validate_schema(200)
+
+ assert conn
+ |> put("/api/v1/pleroma/statuses/#{second.id}/reactions/☕")
+ |> json_response(429) == %{"error" => "Throttled"}
+
+ assert conn
+ |> delete("/api/v1/pleroma/statuses/#{third.id}/reactions/☕")
+ |> json_response(429) == %{"error" => "Throttled"}
+ end
+ end
end

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 2, 8:22 AM (12 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1803801
Default Alt Text
(6 KB)

Event Timeline