fix(mastodon_api): rate limit status actions per status id

StatusController keeps string param keys (replace_params: false, since
b8db67daf), so params: [:id] never matched and status_id_action became
one bucket per user across all statuses (3 favourites/reblogs a minute).
This commit is contained in:
Lain Soykaf 2026-10-30 10:36:05 +04:00
commit 53b79781da
No known key found for this signature in database
3 changed files with 26 additions and 2 deletions

View file

@ -0,0 +1 @@
Rate limit favourites and reblogs per status, not across all statuses

View file

@ -92,13 +92,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
plug(
RateLimiter,
[name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: [:id]]
[name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]]
when action in ~w(reblog unreblog)a
)
plug(
RateLimiter,
[name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: [:id]]
[name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]]
when action in ~w(favourite unfavourite)a
)

View file

@ -2243,6 +2243,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|> json_response_and_validate_schema(404) == %{"error" => "Record not found"}
end
describe "status_id_action rate limit" do
setup do: clear_config([:rate_limit, :status_id_action], {60_000, 2})
setup do: clear_config([:rate_limit, :statuses_actions], {60_000, 100})
setup do: oauth_access(["write:statuses", "write:favourites"])
for {action, undo} <- [{"favourite", "unfavourite"}, {"reblog", "unreblog"}] do
test "#{action} is limited per status, not per user", %{conn: conn} do
conn = put_req_header(conn, "content-type", "application/json")
statuses = insert_list(3, :note_activity)
assert Enum.map(
statuses,
&post(conn, "/api/v1/statuses/#{&1.id}/#{unquote(action)}").status
) ==
[200, 200, 200]
status = hd(statuses)
assert post(conn, "/api/v1/statuses/#{status.id}/#{unquote(undo)}").status == 200
assert post(conn, "/api/v1/statuses/#{status.id}/#{unquote(action)}").status == 429
end
end
end
describe "pinned statuses" do
setup do: oauth_access(["write:accounts"])