Merge branch 'develop' into 'default-hellthread'

# Conflicts:
#   config/soapbox.exs
This commit is contained in:
Alex Gleason 2022-02-07 05:33:38 +00:00
commit 44723db71a
18 changed files with 364 additions and 7 deletions

View file

@ -259,7 +259,8 @@
privileged_staff: false,
max_endorsed_users: 20,
birthday_required: false,
birthday_min_age: 0
birthday_min_age: 0,
max_media_attachments: 1_000
config :pleroma, :welcome,
direct_message: [

View file

@ -552,6 +552,14 @@
100_000
]
},
%{
key: :max_media_attachments,
type: :integer,
description: "Maximum number of post media attachments",
suggestions: [
1_000_000
]
},
%{
key: :upload_limit,
type: :integer,

View file

@ -41,3 +41,6 @@
config :pleroma, :mrf_hellthread,
delist_threshold: 15,
reject_threshold: 100
# Sane default media attachment limit
config :pleroma, :instance, max_media_attachments: 20

View file

@ -117,7 +117,12 @@ defp full_payload(%{status: status, summary: summary} = draft) do
defp attachments(%{params: params} = draft) do
attachments = Utils.attachments_from_ids(params)
%__MODULE__{draft | attachments: attachments}
draft = %__MODULE__{draft | attachments: attachments}
case Utils.validate_attachments_count(attachments) do
:ok -> draft
{:error, message} -> add_error(draft, message)
end
end
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft

View file

@ -492,4 +492,19 @@ def validate_character_limit(full_payload, _attachments) do
{:error, dgettext("errors", "The status is over the character limit")}
end
end
def validate_attachments_count([] = _attachments) do
:ok
end
def validate_attachments_count(attachments) do
limit = Config.get([:instance, :max_media_attachments])
count = length(attachments)
if count <= limit do
:ok
else
{:error, dgettext("errors", "Too many attachments")}
end
end
end

View file

@ -31,6 +31,7 @@ def render("show.json", _) do
approval_required: Keyword.get(instance, :account_approval_required),
# Extra (not present in Mastodon):
max_toot_chars: Keyword.get(instance, :limit),
max_media_attachments: Keyword.get(instance, :max_media_attachments),
poll_limits: Keyword.get(instance, :poll_limits),
upload_limit: Keyword.get(instance, :upload_limit),
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),

View file

@ -0,0 +1,109 @@
{
"@context":[
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"zot":"https://macgirvin.com/apschema#",
"toot":"http://joinmastodon.org/ns#",
"ostatus":"http://ostatus.org#",
"schema":"http://schema.org#",
"litepub":"http://litepub.social/ns#",
"sm":"http://smithereen.software/ns#",
"conversation":"ostatus:conversation",
"manuallyApprovesFollowers":"as:manuallyApprovesFollowers",
"oauthRegistrationEndpoint":"litepub:oauthRegistrationEndpoint",
"sensitive":"as:sensitive",
"movedTo":"as:movedTo",
"copiedTo":"as:copiedTo",
"alsoKnownAs":"as:alsoKnownAs",
"EmojiReact":"as:EmojiReact",
"commentPolicy":"zot:commentPolicy",
"topicalCollection":"zot:topicalCollection",
"eventRepeat":"zot:eventRepeat",
"emojiReaction":"zot:emojiReaction",
"expires":"zot:expires",
"directMessage":"zot:directMessage",
"Category":"zot:Category",
"replyTo":"zot:replyTo",
"PropertyValue":"schema:PropertyValue",
"value":"schema:value",
"discoverable":"toot:discoverable",
"wall":"sm:wall",
"capabilities":"litepub:capabilities",
"acceptsJoins":"litepub:acceptsJoins"
}
],
"type":"Create",
"id":"https://macgirvin.com/activity/ce9ce740-e1cb-4a75-aa4e-9e923555870d",
"published":"2022-02-02T04:41:46Z",
"context":"https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb",
"conversation":"https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb",
"actor":"https://macgirvin.com/channel/mike",
"replyTo":"https://macgirvin.com/channel/mike",
"url":"https://macgirvin.com/activity/ce9ce740-e1cb-4a75-aa4e-9e923555870d",
"object":{
"type":"Note",
"id":"https://macgirvin.com/item/ce9ce740-e1cb-4a75-aa4e-9e923555870d",
"published":"2022-02-02T04:41:46Z",
"attributedTo":"https://macgirvin.com/channel/mike",
"inReplyTo":"https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb",
"context":"https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb",
"conversation":"https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb",
"content":"The Accepts should get through now. Now to figure out why the comments are failing.",
"source":{
"content":"The Accepts should get through now. Now to figure out why the comments are failing.",
"mediaType":"text/x-multicode"
},
"replyTo":"https://macgirvin.com/channel/mike",
"url":"https://macgirvin.com/item/ce9ce740-e1cb-4a75-aa4e-9e923555870d",
"tag":[
{
"type":"Mention",
"href":"https://gleasonator.com/users/macgirvin",
"name":"@macgirvin@gleasonator.com"
},
{
"type":"Mention",
"href":"https://gleasonator.com/users/alex",
"name":"@alex@gleasonator.com"
}
],
"to":[
"https://www.w3.org/ns/activitystreams#Public",
"https://gleasonator.com/users/alex",
"https://gleasonator.com/users/macgirvin"
],
"cc":[
"https://macgirvin.com/followers/mike",
"https://gleasonator.com/users/macgirvin/followers"
]
},
"tag":[
{
"type":"Mention",
"href":"https://gleasonator.com/users/macgirvin",
"name":"@macgirvin@gleasonator.com"
},
{
"type":"Mention",
"href":"https://gleasonator.com/users/alex",
"name":"@alex@gleasonator.com"
}
],
"to":[
"https://www.w3.org/ns/activitystreams#Public",
"https://gleasonator.com/users/alex",
"https://gleasonator.com/users/macgirvin"
],
"cc":[
"https://macgirvin.com/followers/mike",
"https://gleasonator.com/users/macgirvin/followers"
],
"signature":{
"type":"RsaSignature2017",
"nonce":"544080164a412f0592f8257094a870a0177276f4a04cc4410974f5e8fa277a14",
"creator":"https://macgirvin.com/channel/mike",
"created":"2022-02-02T04:41:46Z",
"signatureValue":"BkHEdRkki/DGHctiI6BWyQdn0i9ip+7rFiqqlLgotgQzwA8wzcmyvFIRm8Z+3OqrbzkNyQLCmx4qHvkqWyNrR0eSSJPaBryYvTLpLBz5F5PSpHOI5x4kRGVaI2S1Po+pUMWgchyyQ8ylqdLVHe0FnqId7vFVo9uj4jWydU5wPGlbb5nvnRMQGGyPqzCJ69lopMgCRCUNgbOz6hYVU0Mhqgi9BDjs1crbRLoGexz87tu7FeGEmtyfz8/SBGrDy+X+U3ahykwqd7ggcptsYVu5BY7BfREOLNZE8BwapUCg+QSw9PJv7dLJytdh0kUrzpuAPNbugx7y662FALmHZlxcIC1IVwbPOwEiosDm6wXsOiIyTvnNFcvDre/B1corB2yt5wmO3Cu5jINvp4+aBTheGIKMxBvpJXywpWe6C0VBqfNZSreJtDwp9lHd6D1+L4V6hUCOqiCcqmLT/GI5TOR+EhSpJ02TOkuu+/8hyIfO6ec3uK98y8suyidckbfM60jrbvZh2s/kF9WlDsE9K+Jlyd9Xx3mI5jU+4MXo1MDA57zYpsXw0S+v++rvnOw8CkQr/opVdIRSCG+UB3VRinpgjwW1UQcdAI+fiVaAwqr45MgnIEaQdk13skJUfUl06D9IxKxhu7yNW1tR621w/HN+358qVQWWBoj+1ZhCG3Thjug="
}
}

View file

@ -0,0 +1,35 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://gleasonator.com/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"actor": "https://gleasonator.com/users/macgirvin",
"attachment": [],
"attributedTo": "https://gleasonator.com/users/macgirvin",
"cc": [
"https://gleasonator.com/users/macgirvin/followers"
],
"content": "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"9v5bmRalQvjOy0ECcC\" href=\"https://gleasonator.com/users/alex\" rel=\"ugc\">@<span>alex</span></a></span> Any idea why my posts are failing? I sent an Accept/Follow from <a href=\"http://macgirvin.com\" rel=\"ugc\">macgirvin.com</a> at 2022-02-02T04:06:01Z and it vanished into space. As do all my comments to you. <br><br>2022-02-02T04:06:01Z:LOG_INFO:d5c4aa7f6a:Queue.php:435:deliver: deliver: queue post returned 200 from <a href=\"https://gleasonator.com/users/macgirvin/inbox\" rel=\"ugc\">https://gleasonator.com/users/macgirvin/inbox</a><br><br>It&#39;s OK if I&#39;m blocked, but if that&#39;s the case, I shouldn&#39;t be able to send a follow to that address should I?",
"context": "https://gleasonator.com/contexts/b7f01f94-bc92-4d89-a085-0ffc211b0e42",
"conversation": "https://gleasonator.com/contexts/b7f01f94-bc92-4d89-a085-0ffc211b0e42",
"id": "https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb",
"published": "2022-02-02T04:14:10.965833Z",
"sensitive": false,
"source": "@alex Any idea why my posts are failing? I sent an Accept/Follow from macgirvin.com at 2022-02-02T04:06:01Z and it vanished into space. As do all my comments to you. \n\n2022-02-02T04:06:01Z:LOG_INFO:d5c4aa7f6a:Queue.php:435:deliver: deliver: queue post returned 200 from https://gleasonator.com/users/macgirvin/inbox\n\nIt's OK if I'm blocked, but if that's the case, I shouldn't be able to send a follow to that address should I?",
"summary": "",
"tag": [
{
"href": "https://gleasonator.com/users/alex",
"name": "@alex",
"type": "Mention"
}
],
"to": [
"https://www.w3.org/ns/activitystreams#Public",
"https://gleasonator.com/users/alex"
],
"type": "Note"
}

View file

@ -0,0 +1,41 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://gleasonator.com/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"alsoKnownAs": [],
"attachment": [],
"capabilities": {
"acceptsChatMessages": true
},
"discoverable": false,
"endpoints": {
"oauthAuthorizationEndpoint": "https://gleasonator.com/oauth/authorize",
"oauthRegistrationEndpoint": "https://gleasonator.com/api/v1/apps",
"oauthTokenEndpoint": "https://gleasonator.com/oauth/token",
"sharedInbox": "https://gleasonator.com/inbox",
"uploadMedia": "https://gleasonator.com/api/ap/upload_media"
},
"featured": "https://gleasonator.com/users/macgirvin/collections/featured",
"followers": "https://gleasonator.com/users/macgirvin/followers",
"following": "https://gleasonator.com/users/macgirvin/following",
"id": "https://gleasonator.com/users/macgirvin",
"inbox": "https://gleasonator.com/users/macgirvin/inbox",
"manuallyApprovesFollowers": false,
"name": "macgirvin",
"outbox": "https://gleasonator.com/users/macgirvin/outbox",
"preferredUsername": "macgirvin",
"publicKey": {
"id": "https://gleasonator.com/users/macgirvin#main-key",
"owner": "https://gleasonator.com/users/macgirvin",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0AUMgsQu87tLwoJjZfZu\nsj7NV0qt/THdK8aw4Ha2GZfNkwrep99AZ8gmCI+rr+N5vyETAARzG5/Qzr5bNTUx\nsc1fxFemhhi6sxzAv4qZ5AgvWQ4YPFWizSp5ZY1jpPHLOeF2IftMf8CwVI82PtD0\n7m7T6iUYA4vfvMp9LxVrzQA+CAtpsQxAejTGCt37yM9T2mEWqfmJQQHRIQ4brKBL\nI82sNbzk1cbTwCfH7vRNS/l1ND+vaUGGkDKtpRl56BLmt4picYL0avc+8oO7ebpc\n/zUoS8OOi+mpEzjv7TBrSirYEIGvIh3TKHWSPrpHpQTqj9xBQBy+AxXTWahQEO2M\ndQIDAQAB\n-----END PUBLIC KEY-----\n\n"
},
"summary": "",
"tag": [],
"type": "Person",
"url": "https://gleasonator.com/users/macgirvin",
"vcard:bday": null
}

View file

@ -130,7 +130,7 @@ test "max_body_length returns error if streaming body more than that option", %{
assert capture_log(fn ->
ReverseProxy.call(conn, "/stream-bytes/50", max_body_length: 30)
end) =~
"[warn] Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large"
"Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large"
end
end

View file

@ -62,4 +62,15 @@ test "Misskey quote post" do
assert cng.valid?
assert cng.changes.quoteUrl == "https://misskey.io/notes/8vs6wxufd0"
end
test "a Note from Roadhouse validates" do
insert(:user, ap_id: "https://macgirvin.com/channel/mike")
%{"object" => note} =
"test/fixtures/roadhouse-create-activity.json"
|> File.read!()
|> Jason.decode!()
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
end

View file

@ -0,0 +1,59 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidatorTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
alias Pleroma.Web.ActivityPub.Utils
import Pleroma.Factory
test "a Create/Note from Roadhouse validates" do
insert(:user, ap_id: "https://macgirvin.com/channel/mike")
note_activity =
"test/fixtures/roadhouse-create-activity.json"
|> File.read!()
|> Jason.decode!()
# Build metadata
{:ok, object_data} = ObjectValidator.cast_and_apply(note_activity["object"])
meta = [object_data: ObjectValidator.stringify_keys(object_data)]
%{valid?: true} = CreateGenericValidator.cast_and_validate(note_activity, meta)
end
test "a Create/Note with mismatched context is invalid" do
user = insert(:user)
note = %{
"id" => Utils.generate_object_id(),
"type" => "Note",
"actor" => user.ap_id,
"to" => [user.follower_address],
"cc" => [],
"content" => "Hello world",
"context" => Utils.generate_context_id()
}
note_activity = %{
"id" => Utils.generate_activity_id(),
"type" => "Create",
"actor" => note["actor"],
"to" => note["to"],
"cc" => note["cc"],
"object" => note,
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => Utils.generate_context_id()
}
# Build metadata
{:ok, object_data} = ObjectValidator.cast_and_apply(note_activity["object"])
meta = [object_data: ObjectValidator.stringify_keys(object_data)]
%{valid?: false} = CreateGenericValidator.cast_and_validate(note_activity, meta)
end
end

View file

@ -129,6 +129,17 @@ test "it accepts quote posts" do
# It fetched the quoted post
assert Object.normalize("https://misskey.io/notes/8vs6wxufd0")
end
test "a reply with mismatched context is rejected" do
insert(:user, ap_id: "https://macgirvin.com/channel/mike")
note_activity =
"test/fixtures/roadhouse-create-activity.json"
|> File.read!()
|> Jason.decode!()
assert {:error, _} = Transmogrifier.handle_incoming(note_activity)
end
end
describe "prepare outgoing" do

View file

@ -355,6 +355,7 @@ test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
} = note
end
@tag :erratic
test "it returns reports with notes", %{conn: conn, admin: admin} do
conn = get(conn, "/api/pleroma/admin/reports")

View file

@ -309,7 +309,7 @@ test "when date is a binary in wrong format" do
assert capture_log(fn ->
assert Utils.date_to_asctime(date) == expected
end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601"
end) =~ "Date #{date} in wrong format, must be ISO 8601"
end
test "when date is a Unix timestamp" do
@ -319,7 +319,7 @@ test "when date is a Unix timestamp" do
assert capture_log(fn ->
assert Utils.date_to_asctime(date) == expected
end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601"
end) =~ "Date #{date} in wrong format, must be ISO 8601"
end
test "when date is nil" do
@ -327,13 +327,13 @@ test "when date is nil" do
assert capture_log(fn ->
assert Utils.date_to_asctime(nil) == expected
end) =~ "[warn] Date in wrong format, must be ISO 8601"
end) =~ "Date in wrong format, must be ISO 8601"
end
test "when date is a random string" do
assert capture_log(fn ->
assert Utils.date_to_asctime("foo") == ""
end) =~ "[warn] Date foo in wrong format, must be ISO 8601"
end) =~ "Date foo in wrong format, must be ISO 8601"
end
end

View file

@ -683,6 +683,32 @@ test "it validates character limits are correctly enforced" do
assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"})
end
test "it validates media attachment limits are correctly enforced" do
clear_config([:instance, :max_media_attachments], 4)
user = insert(:user)
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
assert {:error, "Too many attachments"} =
CommonAPI.post(user, %{
status: "",
media_ids: List.duplicate(upload.id, 5)
})
assert {:ok, _activity} =
CommonAPI.post(user, %{
status: "",
media_ids: [upload.id]
})
end
test "it can handle activities that expire" do
user = insert(:user)

View file

@ -177,6 +177,7 @@ test "common" do
assert response["whole_word"] == true
end
@tag :erratic
test "with adding expires_at", %{conn: conn, user: user} do
filter = insert(:filter, user: user)
in_seconds = 600

View file

@ -1329,6 +1329,36 @@ def get("https://misskey.io/notes/8vs6wxufd0", _, _, _) do
}}
end
def get("https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/gleasonator-AG3RzWfwEKKrY63qj2.json"),
headers: activitypub_object_headers()
}}
end
def get("https://gleasonator.com/users/macgirvin", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/macgirvin@gleasonator.com.json"),
headers: activitypub_object_headers()
}}
end
def get("https://gleasonator.com/users/macgirvin/collections/featured", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body:
File.read!("test/fixtures/users_mock/masto_featured.json")
|> String.replace("{{domain}}", "gleasonator.com")
|> String.replace("{{nickname}}", "macgirvin"),
headers: activitypub_object_headers()
}}
end
def get(url, query, body, headers) do
{:error,
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}