fix(federation): do not deliver to instances rejected by SimplePolicy

Public activities went to every inbox, so followers on an instance in
the SimplePolicy reject list still got our posts. With authorized fetch
mode on, signed fetches from those instances were also accepted, unless
the instance was in :instance, :rejected_instances too.

Check the SimplePolicy reject and accept lists for deliveries and signed
fetches, when the policy is enabled. Quarantine keeps its meaning (no
private activities). An inbox without a host no longer crashes.

Based on the Akkoma changes 90c4785b8, ca000f830, 03662501c and d2b0d8647.

Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Co-authored-by: Atsuko Karagi <atsuko.karagi@localhost>
This commit is contained in:
Lain Soykaf 2026-10-27 12:33:35 +04:00
commit a766335679
No known key found for this signature in database
9 changed files with 177 additions and 18 deletions

View file

@ -139,4 +139,37 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
type: :group
}
end
describe "instance_allowed?/1" do
setup do: clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
setup do: clear_config([:mrf_simple, :reject], [{"rejected.example", "spam"}])
setup do: clear_config([:mrf_simple, :accept], [])
test "is false for hosts rejected by SimplePolicy, including subdomains" do
clear_config([:mrf_simple, :reject], [{"*.rejected.example", "spam"}])
refute MRF.instance_allowed?("rejected.example")
refute MRF.instance_allowed?("sub.rejected.example")
assert MRF.instance_allowed?("fine.example")
end
test "only allows accepted hosts when SimplePolicy has an accept list" do
clear_config([:mrf_simple, :accept], [{"friend.example", ""}])
assert MRF.instance_allowed?("friend.example")
refute MRF.instance_allowed?("stranger.example")
end
test "ignores the SimplePolicy lists when the policy is not enabled" do
clear_config([:mrf, :policies], [])
clear_config([:mrf_simple, :accept], [{"friend.example", ""}])
assert MRF.instance_allowed?("rejected.example")
assert MRF.instance_allowed?("stranger.example")
end
test "is false without a host" do
refute MRF.instance_allowed?(nil)
end
end
end

View file

@ -32,8 +32,26 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
refute Publisher.should_federate?(nil, true)
end
test "it returns true when public is true" do
assert Publisher.should_federate?(false, true)
test "it returns true for public activities to quarantined instances" do
clear_config([:instance, :quarantined_instances], [{"quarantined.example", ""}])
assert Publisher.should_federate?("https://quarantined.example/inbox", true)
refute Publisher.should_federate?("https://quarantined.example/inbox", false)
end
test "it returns false for instances rejected by SimplePolicy, even for public activities" do
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
clear_config([:mrf_simple, :reject], [{"rejected.example", ""}])
refute Publisher.should_federate?("https://rejected.example/inbox", true)
refute Publisher.should_federate?("https://rejected.example/inbox", false)
assert Publisher.should_federate?("https://fine.example/inbox", true)
end
test "it returns false for inboxes without a host" do
clear_config([:instance, :quarantined_instances], [{"quarantined.example", ""}])
refute Publisher.should_federate?("inbox", false)
end
end
@ -204,6 +222,32 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
)
end
test "doesn't publish a public activity to instances rejected by SimplePolicy" do
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
clear_config([:mrf_simple, :reject], [{"rejected.example", "spam"}])
actor = insert(:user)
for host <- ["rejected.example", "fine.example"] do
follower = insert(:user, local: false, inbox: "https://#{host}/users/nick/inbox")
{:ok, _, _} = Pleroma.User.follow(follower, actor)
end
actor = refresh_record(actor)
note_activity =
insert(:note_activity, user: actor, recipients: [@as_public, actor.follower_address])
assert :ok == Publisher.publish(actor, note_activity)
inboxes =
all_enqueued(worker: "Pleroma.Workers.PublisherWorker")
|> Enum.map(& &1.args["params"]["inbox"])
assert "https://fine.example/users/nick/inbox" in inboxes
refute "https://rejected.example/users/nick/inbox" in inboxes
end
test "Publishes a non-public activity to non-quarantined instances." do
Config.put([:instance, :quarantined_instances], [{"somedomain.com", "some reason"}])

View file

@ -0,0 +1,38 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.HTTPSignaturePlugMRFTest do
# Not async: the MRF settings are global config.
use Pleroma.Web.ConnCase
alias Pleroma.StaticStubbedConfigMock, as: ConfigMock
alias Pleroma.StubbedHTTPSignaturesMock, as: HTTPSignaturesMock
alias Pleroma.Web.Plugs.HTTPSignaturePlug
import Mox
import Phoenix.Controller, only: [put_format: 2]
import Plug.Conn
setup do: clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
setup do: clear_config([:mrf_simple, :reject], [{"mastodon.example.org", "no reason"}])
defp signed_get(actor) do
ConfigMock
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|> expect(:get, fn [:instance, :rejected_instances] -> [] end)
HTTPSignaturesMock
|> expect(:validate_conn, fn _ -> true end)
build_conn(:get, "/doesntmattter", %{"actor" => actor})
|> put_req_header("signature", "keyId=\"#{actor}#main-key")
|> put_format("activity+json")
|> HTTPSignaturePlug.call(%{})
end
test "rejects signed requests from instances rejected by SimplePolicy" do
assert signed_get("http://mastodon.example.org/users/admin").halted
refute signed_get("http://allowed.example.org/users/admin").halted
end
end