Split failed-signature inbox retries
Route failed-signature ActivityPub inbox retries through a dedicated worker so legacy and malformed retry jobs fail closed before processing.
This commit is contained in:
parent
bd45704dba
commit
7756f491d5
7 changed files with 786 additions and 461 deletions
|
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Workers.ReceiverWorker
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
|
|
@ -36,6 +37,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
defp expect_signature_retry_from(%User{} = signer) do
|
||||
signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url} when url == signer.ap_id ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: Jason.encode!(signer_json),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
end
|
||||
|
||||
describe "/relay" do
|
||||
setup do: clear_config([:instance, :allow_relay])
|
||||
|
||||
|
|
@ -727,6 +746,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
end
|
||||
|
||||
test "does not create a forged post after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/inbox-forged-note"
|
||||
|
||||
|
|
@ -750,6 +770,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|
|
@ -760,13 +782,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged like after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
|
|
@ -779,6 +802,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|
|
@ -789,7 +814,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
|
@ -820,7 +845,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
}
|
||||
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
@ -837,7 +862,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
|
|
@ -858,7 +883,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
@ -875,7 +900,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,11 +11,9 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Workers.ReceiverWorker
|
||||
|
||||
defp mismatched_signature_headers do
|
||||
defp signature_headers_for(%User{} = signer) do
|
||||
[
|
||||
{"host", "local.test"},
|
||||
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||
|
|
@ -23,39 +21,15 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
{"content-type", "application/activity+json"},
|
||||
{
|
||||
"signature",
|
||||
"keyId=\"https://one.com/users/alice#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
"keyId=\"#{signer.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp expect_signature_from(%User{} = signer) do
|
||||
signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url} when url == signer.ap_id ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: Jason.encode!(signer_json),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
end
|
||||
|
||||
defp assert_mismatched_signature_cancelled(params, signer) do
|
||||
expect_signature_from(signer)
|
||||
|
||||
assert {:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: mismatched_signature_headers(),
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} = ReceiverWorker.perform(oban_job)
|
||||
defp perform_incoming(params) do
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{"op" => "incoming_ap_doc", "params" => params}
|
||||
})
|
||||
end
|
||||
|
||||
test "it does not retry MRF reject" do
|
||||
|
|
@ -125,16 +99,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://springfield.social/users/bart")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:error, :forbidden}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:error, :forbidden}} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "when request returns a 404" do
|
||||
|
|
@ -142,16 +107,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://springfield.social/users/troymcclure")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:error, :not_found}} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "when request returns a 410" do
|
||||
|
|
@ -159,16 +115,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://springfield.social/users/hankscorpio")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:error, :not_found}} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "when user account is disabled" do
|
||||
|
|
@ -182,86 +129,16 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
|
||||
{:ok, %User{}} = User.set_activation(user, false)
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:user_active, false}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:user_active, false}} = perform_incoming(params)
|
||||
end
|
||||
end
|
||||
|
||||
test "it can validate the signature" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: "https://phpc.social/users/denniskoch"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/denniskoch.json"),
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
|
||||
%{url: "https://phpc.social/users/denniskoch/collections/featured"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
File.read!("test/fixtures/users_mock/masto_featured.json")
|
||||
|> String.replace("{{domain}}", "phpc.social")
|
||||
|> String.replace("{{nickname}}", "denniskoch")
|
||||
}
|
||||
end)
|
||||
|
||||
params =
|
||||
File.read!("test/fixtures/receiver_worker_signature_activity.json") |> Jason.decode!()
|
||||
|
||||
req_headers = [
|
||||
["accept-encoding", "gzip"],
|
||||
["content-length", "5184"],
|
||||
["content-type", "application/activity+json"],
|
||||
["date", "Thu, 25 Jul 2024 13:33:31 GMT"],
|
||||
["digest", "SHA-256=ouge/6HP2/QryG6F3JNtZ6vzs/hSwMk67xdxe87eH7A="],
|
||||
["host", "bikeshed.party"],
|
||||
[
|
||||
"signature",
|
||||
"keyId=\"https://mastodon.social/users/bastianallgeier#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"ymE3vn5Iw50N6ukSp8oIuXJB5SBjGAGjBasdTDvn+ahZIzq2SIJfmVCsIIzyqIROnhWyQoTbavTclVojEqdaeOx+Ejz2wBnRBmhz5oemJLk4RnnCH0lwMWyzeY98YAvxi9Rq57Gojuv/1lBqyGa+rDzynyJpAMyFk17XIZpjMKuTNMCbjMDy76ILHqArykAIL/v1zxkgwxY/+ELzxqMpNqtZ+kQ29znNMUBB3eVZ/mNAHAz6o33Y9VKxM2jw+08vtuIZOusXyiHbRiaj2g5HtN2WBUw1MzzfRfHF2/yy7rcipobeoyk5RvP5SyHV3WrIeZ3iyoNfmv33y8fxllF0EA==\""
|
||||
],
|
||||
[
|
||||
"user-agent",
|
||||
"http.rb/5.2.0 (Mastodon/4.3.0-nightly.2024-07-25; +https://mastodon.social/)"
|
||||
]
|
||||
]
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: req_headers,
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:ok, %Pleroma.Activity{}} = ReceiverWorker.perform(oban_job)
|
||||
end
|
||||
|
||||
test "cancels due to origin containment" do
|
||||
params =
|
||||
insert(:note_activity).data
|
||||
|> Map.put("id", "https://notorigindomain.com/activity")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, :origin_containment_failed} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, :origin_containment_failed} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "canceled due to deleted object" do
|
||||
|
|
@ -277,16 +154,98 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
}
|
||||
end)
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
assert {:cancel, _} = perform_incoming(params)
|
||||
end
|
||||
|
||||
assert {:cancel, _} = ReceiverWorker.perform(oban_job)
|
||||
test "delegates legacy failed-signature metadata jobs instead of processing them as trusted" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/legacy-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/legacy-forged-create",
|
||||
"context" => "https://two.com/contexts/legacy-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/legacy-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"method" => "POST",
|
||||
"params" => create,
|
||||
"req_headers" => signature_headers_for(alice),
|
||||
"request_path" => "/inbox",
|
||||
"query_string" => ""
|
||||
}
|
||||
})
|
||||
|
||||
refute Pleroma.Activity.get_by_ap_id(create["id"])
|
||||
refute Pleroma.Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "fails closed for the old persisted failed-signature job shape" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/old-shape-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/old-shape-forged-create",
|
||||
"context" => "https://two.com/contexts/old-shape-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/old-shape-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"params" => create,
|
||||
"req_headers" => signature_headers_for(alice),
|
||||
"timeout" => 20_000
|
||||
}
|
||||
})
|
||||
|
||||
refute Pleroma.Activity.get_by_ap_id(create["id"])
|
||||
refute Pleroma.Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "fails closed for malformed legacy metadata jobs without params" do
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"req_headers" => [],
|
||||
"timeout" => 20_000
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
describe "Server reachability:" do
|
||||
|
|
@ -346,262 +305,4 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "cancels when signature actor does not match payload actor" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: bob,
|
||||
object_local: false,
|
||||
data: %{"id" => "https://two.com/objects/malicious-update-note"}
|
||||
)
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/malicious-update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data
|
||||
}
|
||||
|
||||
req_headers = [
|
||||
["host", "local.test"],
|
||||
["date", "Thu, 25 Jul 2024 13:33:31 GMT"],
|
||||
["digest", "SHA-256=fake-digest"],
|
||||
["content-type", "application/activity+json"],
|
||||
[
|
||||
"signature",
|
||||
"keyId=\"https://one.com/users/alice#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
]
|
||||
]
|
||||
|
||||
oban_job = %Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"method" => "POST",
|
||||
"params" => update,
|
||||
"req_headers" => req_headers,
|
||||
"request_path" => "/inbox",
|
||||
"query_string" => ""
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_from(alice)
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} = ReceiverWorker.perform(oban_job)
|
||||
end
|
||||
|
||||
test "Federator preserves request metadata needed for ReceiverWorker signature checks" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
req_headers = [
|
||||
{"host", "local.test"},
|
||||
{"signature", "keyId=\"https://one.com/users/alice#main-key\""}
|
||||
]
|
||||
|
||||
assert {:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: req_headers,
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: "foo=bar"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"method" => "POST",
|
||||
"req_headers" => ^req_headers,
|
||||
"request_path" => "/inbox",
|
||||
"params" => ^params,
|
||||
"query_string" => "foo=bar"
|
||||
} = oban_job.args
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch through Federator-created jobs" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: bob,
|
||||
object_local: false,
|
||||
data: %{"id" => "https://two.com/objects/federator-malicious-note"}
|
||||
)
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/federator-malicious-update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(update, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Create" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://two.com/objects/forged-note",
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(create, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before actually creating a forged post" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
object_id = "https://two.com/objects/actually-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/actually-forged-create",
|
||||
"context" => "https://two.com/contexts/actually-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/actually-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_from(alice)
|
||||
|
||||
assert {:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: mismatched_signature_headers(),
|
||||
request_path: "/inbox",
|
||||
params: create,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} = ReceiverWorker.perform(oban_job)
|
||||
refute Pleroma.Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Like" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
like = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(like, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before actually creating a forged Like" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
like = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/actually-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
expect_signature_from(alice)
|
||||
|
||||
assert {:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: mismatched_signature_headers(),
|
||||
request_path: "/inbox",
|
||||
params: like,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} = ReceiverWorker.perform(oban_job)
|
||||
refute Pleroma.Activity.get_by_ap_id(like["id"])
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Announce" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
announce = %{
|
||||
"type" => "Announce",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-announce",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(announce, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Follow" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
followed = insert(:user)
|
||||
|
||||
follow = %{
|
||||
"type" => "Follow",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-follow",
|
||||
"to" => [followed.ap_id],
|
||||
"cc" => [],
|
||||
"object" => followed.ap_id
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(follow, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Undo" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
undo = %{
|
||||
"type" => "Undo",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-undo",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => "https://two.com/activities/existing-bob-activity"
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(undo, alice)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
469
test/pleroma/workers/signature_retry_worker_test.exs
Normal file
469
test/pleroma/workers/signature_retry_worker_test.exs
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.SignatureRetryWorkerTest do
|
||||
use Pleroma.DataCase, async: false
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
defp signature_headers_for(%User{} = signer) do
|
||||
[
|
||||
{"host", "local.test"},
|
||||
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||
{"digest", "SHA-256=fake-digest"},
|
||||
{"content-type", "application/activity+json"},
|
||||
{
|
||||
"signature",
|
||||
"keyId=\"#{signer.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp stub_actor_fetch(%User{} = signer) do
|
||||
signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url} when url == signer.ap_id ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: Jason.encode!(signer_json),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
defp expect_signature_from(%User{} = signer) do
|
||||
stub_actor_fetch(signer)
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
end
|
||||
|
||||
defp enqueue_failed_signature(params, signer) do
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: signature_headers_for(signer),
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
end
|
||||
|
||||
defp failed_signature_job(params, req_headers, opts \\ []) do
|
||||
%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_failed_signature_ap_doc",
|
||||
"method" => Keyword.get(opts, :method, "POST"),
|
||||
"req_headers" => req_headers,
|
||||
"request_path" => Keyword.get(opts, :request_path, "/inbox"),
|
||||
"params" => params,
|
||||
"query_string" => Keyword.get(opts, :query_string, "")
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_mismatched_signature_cancelled(params, signer) do
|
||||
assert {:ok, oban_job} = enqueue_failed_signature(params, signer)
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} = SignatureRetryWorker.perform(oban_job)
|
||||
end
|
||||
|
||||
test "Federator preserves request metadata for failed-signature retry jobs" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
req_headers = [
|
||||
{"host", "local.test"},
|
||||
{"signature", "keyId=\"https://one.com/users/alice#main-key\""}
|
||||
]
|
||||
|
||||
assert {:ok, oban_job} =
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: req_headers,
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: "foo=bar"
|
||||
})
|
||||
|
||||
assert oban_job.worker == "Pleroma.Workers.SignatureRetryWorker"
|
||||
|
||||
assert %{
|
||||
"op" => "incoming_failed_signature_ap_doc",
|
||||
"method" => "POST",
|
||||
"req_headers" => ^req_headers,
|
||||
"request_path" => "/inbox",
|
||||
"params" => ^params,
|
||||
"query_string" => "foo=bar"
|
||||
} = oban_job.args
|
||||
end
|
||||
|
||||
test "cancels retry jobs without request metadata" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
SignatureRetryWorker.perform(%Oban.Job{
|
||||
args: %{"op" => "incoming_failed_signature_ap_doc", "params" => params}
|
||||
})
|
||||
end
|
||||
|
||||
test "cancels retry jobs with malformed serialized request headers" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
assert {:cancel, :invalid_signature_retry_metadata} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, [["signature"]]))
|
||||
end
|
||||
|
||||
test "cancels retry jobs without a signature header" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity, user: alice).data
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, [{"host", "local.test"}]))
|
||||
end
|
||||
|
||||
test "cancels missing signature before fetching an unavailable payload actor" do
|
||||
params =
|
||||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://unavailable.example/users/bob")
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, [{"host", "local.test"}]))
|
||||
end
|
||||
|
||||
test "cancels signer mismatch before fetching an unavailable payload actor" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
params =
|
||||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://unavailable.example/users/bob")
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} =
|
||||
SignatureRetryWorker.perform(
|
||||
failed_signature_job(params, signature_headers_for(alice))
|
||||
)
|
||||
end
|
||||
|
||||
test "cancels retry jobs with a signature header without keyId" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity, user: alice).data
|
||||
|
||||
req_headers = [{"signature", "algorithm=\"rsa-sha256\",signature=\"fake-signature\""}]
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, req_headers))
|
||||
end
|
||||
|
||||
test "cancels retry jobs with an unparsable signature keyId" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity, user: alice).data
|
||||
req_headers = [{"signature", "keyId=\"not an activitypub id\",signature=\"fake-signature\""}]
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, req_headers))
|
||||
end
|
||||
|
||||
test "cancels when the refetched key still cannot validate the signature" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => alice.ap_id,
|
||||
"id" => "https://one.com/activities/invalid-signature-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://one.com/objects/invalid-signature-note",
|
||||
"actor" => alice.ap_id,
|
||||
"attributedTo" => alice.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
stub_actor_fetch(alice)
|
||||
|
||||
assert {:ok, oban_job} = enqueue_failed_signature(create, alice)
|
||||
assert {:cancel, :invalid_signature} = SignatureRetryWorker.perform(oban_job)
|
||||
refute Activity.get_by_ap_id(create["id"])
|
||||
end
|
||||
|
||||
test "processes the activity after refetching a valid matching signature" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => alice.ap_id,
|
||||
"id" => "https://one.com/activities/valid-signature-create",
|
||||
"context" => "https://one.com/contexts/valid-signature-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://one.com/objects/valid-signature-note",
|
||||
"actor" => alice.ap_id,
|
||||
"attributedTo" => alice.ap_id,
|
||||
"context" => "https://one.com/contexts/valid-signature-create",
|
||||
"content" => "valid post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_from(alice)
|
||||
|
||||
assert {:ok, oban_job} = enqueue_failed_signature(create, alice)
|
||||
assert {:ok, %Activity{}} = SignatureRetryWorker.perform(oban_job)
|
||||
assert Activity.get_by_ap_id(create["id"])
|
||||
end
|
||||
|
||||
test "processes the activity when a real signature validates with a query string" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => alice.ap_id,
|
||||
"id" => "https://one.com/activities/valid-query-signature-create",
|
||||
"context" => "https://one.com/contexts/valid-query-signature-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://one.com/objects/valid-query-signature-note",
|
||||
"actor" => alice.ap_id,
|
||||
"attributedTo" => alice.ap_id,
|
||||
"context" => "https://one.com/contexts/valid-query-signature-create",
|
||||
"content" => "valid signed post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
stub_actor_fetch(alice)
|
||||
|
||||
date = "Thu, 25 Jul 2024 13:33:31 GMT"
|
||||
digest = "SHA-256=fake-digest"
|
||||
|
||||
signature =
|
||||
Signature.sign(alice, %{
|
||||
"(request-target)" => "post /inbox?foo=bar",
|
||||
"content-type" => "application/activity+json",
|
||||
date: date,
|
||||
digest: digest,
|
||||
host: "local.test"
|
||||
})
|
||||
|
||||
req_headers = [
|
||||
["host", "local.test"],
|
||||
["date", date],
|
||||
["digest", digest],
|
||||
["content-type", "application/activity+json"],
|
||||
["signature", signature]
|
||||
]
|
||||
|
||||
assert {:ok, %Activity{}} =
|
||||
SignatureRetryWorker.perform(
|
||||
failed_signature_job(create, req_headers, query_string: "foo=bar")
|
||||
)
|
||||
|
||||
assert Activity.get_by_ap_id(create["id"])
|
||||
end
|
||||
|
||||
test "cancels when signature actor does not match payload actor" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: bob,
|
||||
object_local: false,
|
||||
data: %{"id" => "https://two.com/objects/malicious-update-note"}
|
||||
)
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/malicious-update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(update, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch through Federator-created jobs" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: bob,
|
||||
object_local: false,
|
||||
data: %{"id" => "https://two.com/objects/federator-malicious-note"}
|
||||
)
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/federator-malicious-update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(update, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Create" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://two.com/objects/forged-note",
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(create, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before actually creating a forged post" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
object_id = "https://two.com/objects/actually-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/actually-forged-create",
|
||||
"context" => "https://two.com/contexts/actually-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/actually-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(create, alice)
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Like" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
like = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(like, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before actually creating a forged Like" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
like = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/actually-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(like, alice)
|
||||
refute Activity.get_by_ap_id(like["id"])
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Announce" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
announce = %{
|
||||
"type" => "Announce",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-announce",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(announce, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Follow" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
followed = insert(:user)
|
||||
|
||||
follow = %{
|
||||
"type" => "Follow",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-follow",
|
||||
"to" => [followed.ap_id],
|
||||
"cc" => [],
|
||||
"object" => followed.ap_id
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(follow, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Undo" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
undo = %{
|
||||
"type" => "Undo",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-undo",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => "https://two.com/activities/existing-bob-activity"
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(undo, alice)
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue