ensure quote fetching obeys max thread distance (#119)

Reviewed-on: AkkomaGang/akkoma#119
This commit is contained in:
floatingghost 2022-07-26 17:28:47 +00:00
parent 2cde2052b8
commit a3501cab86
5 changed files with 171 additions and 94 deletions

View file

@ -156,7 +156,6 @@ defp fix(data) do
|> fix_replies() |> fix_replies()
|> fix_source() |> fix_source()
|> fix_misskey_content() |> fix_misskey_content()
|> Transmogrifier.fix_quote_url()
|> Transmogrifier.fix_attachments() |> Transmogrifier.fix_attachments()
|> Transmogrifier.fix_emoji() |> Transmogrifier.fix_emoji()
|> Transmogrifier.fix_content_map() |> Transmogrifier.fix_content_map()

View file

@ -38,6 +38,7 @@ def fix_object(object, options \\ []) do
|> fix_attachments() |> fix_attachments()
|> fix_context() |> fix_context()
|> fix_in_reply_to(options) |> fix_in_reply_to(options)
|> fix_quote_url(options)
|> fix_emoji() |> fix_emoji()
|> fix_tag() |> fix_tag()
|> fix_content_map() |> fix_content_map()
@ -167,6 +168,50 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
def fix_in_reply_to(object, _options), do: object def fix_in_reply_to(object, _options), do: object
def fix_quote_url(object, options \\ [])
def fix_quote_url(%{"quoteUri" => quote_url} = object, options)
when not is_nil(quote_url) do
depth = (options[:depth] || 0) + 1
if Federator.allowed_thread_distance?(depth) do
with {:ok, quoted_object} <- get_obj_helper(quote_url, options),
%Activity{} <- Activity.get_create_by_object_ap_id(quoted_object.data["id"]) do
object
|> Map.put("quoteUri", quoted_object.data["id"])
else
e ->
Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
object
end
else
object
end
end
# Soapbox
def fix_quote_url(%{"quoteUrl" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
# Old Fedibird (bug)
# https://github.com/fedibird/mastodon/issues/9
def fix_quote_url(%{"quoteURL" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
def fix_quote_url(object, _), do: object
defp prepare_in_reply_to(in_reply_to) do defp prepare_in_reply_to(in_reply_to) do
cond do cond do
is_bitstring(in_reply_to) -> is_bitstring(in_reply_to) ->
@ -424,6 +469,7 @@ def handle_incoming(
|> strip_internal_fields() |> strip_internal_fields()
|> fix_type(fetch_options) |> fix_type(fetch_options)
|> fix_in_reply_to(fetch_options) |> fix_in_reply_to(fetch_options)
|> fix_quote_url(fetch_options)
data = Map.put(data, "object", object) data = Map.put(data, "object", object)
options = Keyword.put(options, :local, false) options = Keyword.put(options, :local, false)
@ -886,43 +932,6 @@ defp strip_internal_tags(%{"tag" => tags} = object) do
defp strip_internal_tags(object), do: object defp strip_internal_tags(object), do: object
def fix_quote_url(object, options \\ [])
def fix_quote_url(%{"quoteUri" => quote_url} = object, options)
when not is_nil(quote_url) do
with {:ok, quoted_object} <- get_obj_helper(quote_url, options),
%Activity{} <- Activity.get_create_by_object_ap_id(quoted_object.data["id"]) do
Map.put(object, "quoteUri", quoted_object.data["id"])
else
e ->
Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
object
end
end
# Soapbox
def fix_quote_url(%{"quoteUrl" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
# Old Fedibird (bug)
# https://github.com/fedibird/mastodon/issues/9
def fix_quote_url(%{"quoteURL" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
def fix_quote_url(object, _), do: object
def perform(:user_upgrade, user) do def perform(:user_upgrade, user) do
# we pass a fake user so that the followers collection is stripped away # we pass a fake user so that the followers collection is stripped away
old_follower_address = User.ap_followers(%User{nickname: user.nickname}) old_follower_address = User.ap_followers(%User{nickname: user.nickname})

View file

@ -0,0 +1,48 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"Hashtag": "as:Hashtag",
"quoteUrl": "as:quoteUrl",
"toot": "http://joinmastodon.org/ns#",
"Emoji": "toot:Emoji",
"featured": "toot:featured",
"discoverable": "toot:discoverable",
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"misskey": "https://misskey-hub.net/ns#",
"_misskey_content": "misskey:_misskey_content",
"_misskey_quote": "misskey:_misskey_quote",
"_misskey_reaction": "misskey:_misskey_reaction",
"_misskey_votes": "misskey:_misskey_votes",
"_misskey_talk": "misskey:_misskey_talk",
"isCat": "misskey:isCat",
"vcard": "http://www.w3.org/2006/vcard/ns#"
}
],
"id": "https://misskey.io/notes/934gok3482",
"type": "Note",
"attributedTo": "https://misskey.io/users/93492q0ip0",
"summary": null,
"content": "<p><span>i quompt u</p>",
"_misskey_content": "i quompt u",
"source": {
"content": "i quompt u",
"mediaType": "text/x.misskeymarkdown"
},
"_misskey_quote": "https://misskey.io/notes/934gok3482",
"quoteUrl": "https://misskey.io/notes/934gok3482",
"published": "2022-07-25T15:21:48.208Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [],
"inReplyTo": null,
"attachment": [],
"sensitive": false,
"tag": []
}

View file

@ -143,61 +143,5 @@ test "a misskey MFM status with a _misskey_content field should work and be link
} }
} = ArticleNotePageValidator.cast_and_validate(note) } = ArticleNotePageValidator.cast_and_validate(note)
end end
test "a misskey quote should work", _ do
Tesla.Mock.mock(fn %{
method: :get,
url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/quoted_status.json"),
headers: HttpRequestMock.activitypub_object_headers()
}
end)
insert(:user, %{ap_id: "https://misskey.io/users/93492q0ip0"})
insert(:user, %{ap_id: "https://example.com/users/user"})
note =
"test/fixtures/misskey/quote.json"
|> File.read!()
|> Jason.decode!()
%{
valid?: true,
changes: %{
quoteUri: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
}
} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a fedibird quote should work", _ do
Tesla.Mock.mock(fn %{
method: :get,
url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/quoted_status.json"),
headers: HttpRequestMock.activitypub_object_headers()
}
end)
insert(:user, %{ap_id: "https://fedibird.com/users/akkoma_ap_integration_tester"})
insert(:user, %{ap_id: "https://example.com/users/user"})
note =
"test/fixtures/fedibird/quote.json"
|> File.read!()
|> Jason.decode!()
%{
valid?: true,
changes: %{
quoteUri: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
}
} = ArticleNotePageValidator.cast_and_validate(note)
end
end end
end end

View file

@ -707,4 +707,81 @@ test "take_emoji_tags/1" do
} }
] ]
end end
describe "fix_quote_url/1" do
test "a misskey quote should work", _ do
Tesla.Mock.mock(fn %{
method: :get,
url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/quoted_status.json"),
headers: HttpRequestMock.activitypub_object_headers()
}
end)
insert(:user, %{ap_id: "https://misskey.io/users/93492q0ip0"})
insert(:user, %{ap_id: "https://example.com/users/user"})
note =
"test/fixtures/misskey/quote.json"
|> File.read!()
|> Jason.decode!()
%{"quoteUri" => "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"} =
Transmogrifier.fix_quote_url(note)
end
test "a fedibird quote should work", _ do
Tesla.Mock.mock(fn %{
method: :get,
url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/quoted_status.json"),
headers: HttpRequestMock.activitypub_object_headers()
}
end)
insert(:user, %{ap_id: "https://fedibird.com/users/akkoma_ap_integration_tester"})
insert(:user, %{ap_id: "https://example.com/users/user"})
note =
"test/fixtures/fedibird/quote.json"
|> File.read!()
|> Jason.decode!()
%{
"quoteUri" => "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"
} = Transmogrifier.fix_quote_url(note)
end
test "quote fetching should stop after n levels", _ do
clear_config([:instance, :federation_incoming_replies_max_depth], 1)
Tesla.Mock.mock(fn %{
method: :get,
url: "https://misskey.io/notes/934gok3482"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/misskey/recursive_quote.json"),
headers: HttpRequestMock.activitypub_object_headers()
}
end)
insert(:user, %{ap_id: "https://misskey.io/users/93492q0ip0"})
note =
"test/fixtures/misskey/recursive_quote.json"
|> File.read!()
|> Jason.decode!()
%{
"quoteUri" => "https://misskey.io/notes/934gok3482"
} = Transmogrifier.fix_quote_url(note)
end
end
end end