fix(federation): reject actors that point at other hosts
A remote actor could set its inbox or shared inbox to a third-party host, so our signed deliveries went there. It could also claim another actor's followers collection, which the visibility checks trust. Validate fetched actors and incoming actor Updates: inbox, endpoints.sharedInbox, followers, following, featured and the publicKey id must be on the same host as the actor id. Based on Akkoma f07eb4cb5. The Bridgy fixture gets the key id that current Bridgy Fed serves.
This commit is contained in:
parent
d54ff52691
commit
695dd340de
9 changed files with 201 additions and 8 deletions
|
|
@ -244,6 +244,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
}
|
||||
end
|
||||
|
||||
test "rejects actors whose inbox is on another host" do
|
||||
ap_id = "https://example.com/users/evil"
|
||||
|
||||
user_data =
|
||||
"test/fixtures/users_mock/user.json"
|
||||
|> File.read!()
|
||||
|> String.replace("{{nickname}}", "evil")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("inbox", "https://victim.example/inbox")
|
||||
|> Map.put("featured", nil)
|
||||
|> Jason.encode!()
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get, url: ^ap_id} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: user_data,
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
end)
|
||||
|
||||
assert {:error, _} = ActivityPub.make_user_from_ap_id(ap_id)
|
||||
refute User.get_by_ap_id(ap_id)
|
||||
end
|
||||
|
||||
test "fetches user featured collection" do
|
||||
ap_id = "https://example.com/users/lain"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.UserValidatorTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UserValidator
|
||||
|
||||
defp actor do
|
||||
"test/fixtures/users_mock/user.json"
|
||||
|> File.read!()
|
||||
|> String.replace("{{nickname}}", "lain")
|
||||
|> Jason.decode!()
|
||||
end
|
||||
|
||||
test "accepts an actor whose endpoints and collections are on its own host" do
|
||||
data = actor()
|
||||
assert {:ok, ^data, []} = UserValidator.validate(data, [])
|
||||
end
|
||||
|
||||
test "accepts an actor without optional fields" do
|
||||
data = Map.drop(actor(), ["endpoints", "featured", "followers", "following", "publicKey"])
|
||||
assert {:ok, _, _} = UserValidator.validate(data, [])
|
||||
end
|
||||
|
||||
test "accepts all actor fixtures" do
|
||||
for path <- Path.wildcard("test/fixtures/**/*.json"),
|
||||
{:ok, %{"inbox" => _, "id" => id} = data} when is_binary(id) <-
|
||||
[Jason.decode(File.read!(path))] do
|
||||
assert {{:ok, _, _}, ^path} = {UserValidator.validate(data, []), path}
|
||||
end
|
||||
end
|
||||
|
||||
for field <- ["inbox", "followers", "following", "featured"] do
|
||||
test "rejects an actor whose #{field} is on another host" do
|
||||
data = Map.put(actor(), unquote(field), "https://victim.example/users/lain/x")
|
||||
assert {:error, _} = UserValidator.validate(data, [])
|
||||
end
|
||||
end
|
||||
|
||||
test "rejects an actor whose sharedInbox is on another host" do
|
||||
data = put_in(actor(), ["endpoints", "sharedInbox"], "https://victim.example/inbox")
|
||||
assert {:error, _} = UserValidator.validate(data, [])
|
||||
end
|
||||
|
||||
test "rejects an actor whose collection is an object on another host" do
|
||||
data = Map.put(actor(), "followers", %{"id" => "https://victim.example/users/v/followers"})
|
||||
assert {:error, _} = UserValidator.validate(data, [])
|
||||
end
|
||||
|
||||
test "rejects an actor whose key id is on another host" do
|
||||
data = put_in(actor(), ["publicKey", "id"], "https://victim.example/users/lain#main-key")
|
||||
assert {:error, _} = UserValidator.validate(data, [])
|
||||
end
|
||||
|
||||
test "rejects an actor whose key id has no host" do
|
||||
data = put_in(actor(), ["publicKey", "id"], "lain")
|
||||
assert {:error, _} = UserValidator.validate(data, [])
|
||||
end
|
||||
|
||||
test "rejects an actor without an id" do
|
||||
assert {:error, _} = UserValidator.validate(Map.delete(actor(), "id"), [])
|
||||
end
|
||||
end
|
||||
|
|
@ -11,8 +11,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
# matches the host of the actor in mastodon-update.json
|
||||
@gargron "http://mastodon.example.org/users/gargron"
|
||||
|
||||
test "it works for incoming update activities" do
|
||||
user = insert(:user, local: false)
|
||||
user = insert(:user, local: false, ap_id: @gargron)
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
|
|
@ -50,8 +53,28 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
|
|||
assert user.bio == "<p>Some bio</p>"
|
||||
end
|
||||
|
||||
test "it rejects updates that move the shared inbox to another host" do
|
||||
user = insert(:user, local: false, ap_id: @gargron)
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
object =
|
||||
update_data["object"]
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|> Map.put("id", user.ap_id)
|
||||
|> put_in(["endpoints", "sharedInbox"], "https://victim.example/inbox")
|
||||
|
||||
update_data =
|
||||
update_data
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|> Map.put("object", object)
|
||||
|
||||
assert {:error, _} = Transmogrifier.handle_incoming(update_data)
|
||||
assert User.get_cached_by_ap_id(user.ap_id).shared_inbox == user.shared_inbox
|
||||
end
|
||||
|
||||
test "it selects an actor type after an extension type" do
|
||||
user = insert(:user, local: false)
|
||||
user = insert(:user, local: false, ap_id: @gargron)
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
object =
|
||||
|
|
@ -70,7 +93,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
|
|||
end
|
||||
|
||||
test "it works with alsoKnownAs" do
|
||||
%{ap_id: actor} = insert(:user, local: false)
|
||||
%{ap_id: actor} = insert(:user, local: false, ap_id: @gargron)
|
||||
|
||||
assert User.get_cached_by_ap_id(actor).also_known_as == []
|
||||
|
||||
|
|
@ -97,7 +120,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
|
|||
end
|
||||
|
||||
test "it works with custom profile fields" do
|
||||
user = insert(:user, local: false)
|
||||
user = insert(:user, local: false, ap_id: @gargron)
|
||||
|
||||
assert user.fields == []
|
||||
|
||||
|
|
@ -155,7 +178,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
|
|||
end
|
||||
|
||||
test "it works for incoming update activities which lock the account" do
|
||||
user = insert(:user, local: false)
|
||||
user = insert(:user, local: false, ap_id: @gargron)
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue