Add spoofing regression tests
This commit is contained in:
parent
42683e79df
commit
80e72b79f5
5 changed files with 342 additions and 4 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -56,6 +56,9 @@ pleroma.iml
|
||||||
# asdf
|
# asdf
|
||||||
.tool-versions
|
.tool-versions
|
||||||
|
|
||||||
|
# mise
|
||||||
|
mise.toml
|
||||||
|
|
||||||
# Editor temp files
|
# Editor temp files
|
||||||
*~
|
*~
|
||||||
*#
|
*#
|
||||||
|
|
|
||||||
|
|
@ -726,6 +726,74 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
assert Activity.get_by_ap_id(data["id"])
|
assert Activity.get_by_ap_id(data["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not create a forged post after failed signature retry", %{conn: conn} do
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
object_id = "https://example.com/objects/inbox-forged-note"
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/inbox-forged-create",
|
||||||
|
"context" => "https://example.com/contexts/inbox-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://example.com/contexts/inbox-forged-create",
|
||||||
|
"content" => "forged post",
|
||||||
|
"published" => "2024-07-25T13:33:31Z",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:valid_signature, false)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> put_req_header("signature", "keyId=\"https://example.com/users/alice#main-key\"")
|
||||||
|
|> post("/inbox", data)
|
||||||
|
|
||||||
|
assert "ok" == json_response(conn, 200)
|
||||||
|
|
||||||
|
assert [{:cancel, :actor_signature_mismatch}] =
|
||||||
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
|
||||||
|
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
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
note = insert(:note)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"type" => "Like",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/inbox-forged-like",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => note.data["id"]
|
||||||
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:valid_signature, false)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> put_req_header("signature", "keyId=\"https://example.com/users/alice#main-key\"")
|
||||||
|
|> post("/inbox", data)
|
||||||
|
|
||||||
|
assert "ok" == json_response(conn, 200)
|
||||||
|
|
||||||
|
assert [{:cancel, :actor_signature_mismatch}] =
|
||||||
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
|
||||||
|
refute Activity.get_by_ap_id(data["id"])
|
||||||
|
end
|
||||||
|
|
||||||
test "accept follow activity", %{conn: conn} do
|
test "accept follow activity", %{conn: conn} do
|
||||||
clear_config([:instance, :federating], true)
|
clear_config([:instance, :federating], true)
|
||||||
relay = Relay.get_actor()
|
relay = Relay.get_actor()
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,32 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
||||||
|
|
||||||
assert {:ok, _update, _} = ObjectValidator.validate(update, [])
|
assert {:ok, _update, _} = ObjectValidator.validate(update, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns an error if the remote update target is unknown" do
|
||||||
|
remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||||
|
|
||||||
|
update = %{
|
||||||
|
"type" => "Update",
|
||||||
|
"actor" => remote_user.ap_id,
|
||||||
|
"id" => "https://example.com/activities/update-unknown-object",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"id" => "https://example.com/objects/unknown",
|
||||||
|
"actor" => remote_user.ap_id,
|
||||||
|
"content" => "edited content",
|
||||||
|
"published" => "2024-07-25T13:33:31Z",
|
||||||
|
"updated" => "2024-07-25T13:34:31Z",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false)
|
||||||
|
refute cng.valid?
|
||||||
|
assert Keyword.has_key?(cng.errors, :object)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "update note" do
|
describe "update note" do
|
||||||
|
|
|
||||||
|
|
@ -47,13 +47,15 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do
|
||||||
assert %{valid_signature: false} == conn.assigns
|
assert %{valid_signature: false} == conn.assigns
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag skip: "known breakage; the testsuite presently depends on it"
|
|
||||||
test "it considers a mapped identity to be invalid when the identity cannot be found" do
|
test "it considers a mapped identity to be invalid when the identity cannot be found" do
|
||||||
|
actor = "http://niu.moe/users/rye"
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"})
|
build_conn(:post, "/doesntmattter", %{"actor" => actor})
|
||||||
|> set_signature("http://niu.moe/users/rye")
|
|> set_signature(actor)
|
||||||
|> MappedSignatureToIdentityPlug.call(%{})
|
|> MappedSignatureToIdentityPlug.call(%{})
|
||||||
|
|
||||||
assert %{valid_signature: false} == conn.assigns
|
assert conn.assigns.valid_signature == false
|
||||||
|
refute Map.has_key?(conn.assigns, :user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,43 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Workers.ReceiverWorker
|
alias Pleroma.Workers.ReceiverWorker
|
||||||
|
|
||||||
|
defp mismatched_signature_headers do
|
||||||
|
[
|
||||||
|
{"host", "example.com"},
|
||||||
|
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||||
|
{"digest", "SHA-256=fake-digest"},
|
||||||
|
{"content-type", "application/activity+json"},
|
||||||
|
{
|
||||||
|
"signature",
|
||||||
|
"keyId=\"https://example.com/users/alice#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assert_mismatched_signature_cancelled(params) do
|
||||||
|
with_mocks [
|
||||||
|
{Pleroma.Signature, [:passthrough],
|
||||||
|
[
|
||||||
|
refetch_public_key: fn _conn -> {:ok, :fake_public_key} end,
|
||||||
|
validate_signature: fn _conn -> true end
|
||||||
|
]},
|
||||||
|
{Pleroma.Web.Federator, [:passthrough],
|
||||||
|
[perform: fn :incoming_ap_doc, _params -> {:ok, :processed} end]}
|
||||||
|
] do
|
||||||
|
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)
|
||||||
|
refute called(Pleroma.Web.Federator.perform(:incoming_ap_doc, :_))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "it does not retry MRF reject" do
|
test "it does not retry MRF reject" do
|
||||||
params = insert(:note).data
|
params = insert(:note).data
|
||||||
|
|
||||||
|
|
@ -346,4 +383,206 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
||||||
assert {:cancel, :actor_signature_mismatch} = ReceiverWorker.perform(oban_job)
|
assert {:cancel, :actor_signature_mismatch} = ReceiverWorker.perform(oban_job)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "Federator preserves request metadata needed for ReceiverWorker signature checks" do
|
||||||
|
params = insert(:note_activity).data
|
||||||
|
|
||||||
|
req_headers = [
|
||||||
|
{"host", "example.com"},
|
||||||
|
{"signature", "keyId=\"https://example.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://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
|
||||||
|
note = insert(:note, user: bob, object_local: false)
|
||||||
|
|
||||||
|
update = %{
|
||||||
|
"type" => "Update",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/federator-malicious-update",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => note.data
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_mismatched_signature_cancelled(update)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cancels signature actor mismatch before processing a forged Create" do
|
||||||
|
_alice = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
|
||||||
|
create = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/forged-create",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"id" => "https://example.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)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cancels signature actor mismatch before actually creating a forged post" do
|
||||||
|
_alice = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
|
||||||
|
object_id = "https://example.com/objects/actually-forged-note"
|
||||||
|
|
||||||
|
create = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/actually-forged-create",
|
||||||
|
"context" => "https://example.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://example.com/contexts/actually-forged-create",
|
||||||
|
"content" => "forged post",
|
||||||
|
"published" => "2024-07-25T13:33:31Z",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
note = insert(:note)
|
||||||
|
|
||||||
|
like = %{
|
||||||
|
"type" => "Like",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/forged-like",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => note.data["id"]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_mismatched_signature_cancelled(like)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cancels signature actor mismatch before actually creating a forged Like" do
|
||||||
|
_alice = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
note = insert(:note)
|
||||||
|
|
||||||
|
like = %{
|
||||||
|
"type" => "Like",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/actually-forged-like",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => note.data["id"]
|
||||||
|
}
|
||||||
|
|
||||||
|
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://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
note = insert(:note)
|
||||||
|
|
||||||
|
announce = %{
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/forged-announce",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => note.data["id"]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_mismatched_signature_cancelled(announce)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cancels signature actor mismatch before processing a forged Follow" do
|
||||||
|
_alice = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
followed = insert(:user)
|
||||||
|
|
||||||
|
follow = %{
|
||||||
|
"type" => "Follow",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/forged-follow",
|
||||||
|
"to" => [followed.ap_id],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => followed.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_mismatched_signature_cancelled(follow)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cancels signature actor mismatch before processing a forged Undo" do
|
||||||
|
_alice = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||||
|
bob = insert(:user, local: false, ap_id: "https://example.com/users/bob")
|
||||||
|
|
||||||
|
undo = %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"actor" => bob.ap_id,
|
||||||
|
"id" => "https://example.com/activities/forged-undo",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => "https://example.com/activities/existing-bob-activity"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_mismatched_signature_cancelled(undo)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue