\n"
"Language-Team: Indonesian \n"
+"pleroma-backend-domain-errors/id/>\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.6.2\n"
+"X-Generator: Weblate 4.13.1\n"
## This file is a PO Template file.
##
@@ -41,7 +41,7 @@ msgstr "memiliki format yang tidak valid"
## From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
-msgstr ""
+msgstr "ada yang tidak valid"
## From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
@@ -49,7 +49,7 @@ msgstr ""
## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
-msgstr ""
+msgstr "tidak sama dengan konfirmasi"
## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated with this entry"
@@ -61,19 +61,19 @@ msgstr ""
## From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
-msgstr[0] "harus memiliki %{count} karakter"
+msgstr[0] "harus ada %{count} karakter"
msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
-msgstr[0] "harus memiliki %{count} item"
+msgstr[0] "harus ada %{count} item"
msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
-msgstr[0] "harus memiliki sekurang-kurangnya %{count} karakter"
+msgstr[0] "harus ada sekurang-kurangnya %{count} karakter"
msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
-msgstr[0] "harus memiliki sekurang-kurangnya %{count} item"
+msgstr[0] "harus ada sekurang-kurangnya %{count} item"
msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
@@ -112,7 +112,7 @@ msgstr "Sudah memilih"
#: lib/pleroma/web/oauth/oauth_controller.ex:359
#, elixir-format
msgid "Bad request"
-msgstr "Permintaan buruk (bad request)"
+msgstr "Permintaan buruk"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426
#, elixir-format
@@ -133,7 +133,7 @@ msgstr "Tidak dapat mencari pengguna"
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61
#, elixir-format
msgid "Can't get favorites"
-msgstr "Tidak dapat mendapatkan favorit"
+msgstr "Tidak dapat mengambil favorit"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438
#, elixir-format
@@ -143,7 +143,7 @@ msgstr "Tidak dapat menyukai objek"
#: lib/pleroma/web/common_api/utils.ex:563
#, elixir-format
msgid "Cannot post an empty status without attachments"
-msgstr "Tidak dapat memposting status kosong tanpa lampiran"
+msgstr "Tidak dapat mempos status kosong tanpa lampiran"
#: lib/pleroma/web/common_api/utils.ex:511
#, elixir-format
@@ -206,7 +206,7 @@ msgstr "CAPTCHA tidak valid"
#: lib/pleroma/web/oauth/oauth_controller.ex:568
#, elixir-format
msgid "Invalid credentials"
-msgstr "Kredensian tidak valid"
+msgstr "Kredensial tidak valid"
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
#, elixir-format
@@ -279,12 +279,12 @@ msgstr ""
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
#, elixir-format
msgid "Something went wrong"
-msgstr "Sesuatu yang salah terjadi"
+msgstr "Ada sesuatu yang salah"
#: lib/pleroma/web/common_api/activity_draft.ex:107
#, elixir-format
msgid "The message visibility must be direct"
-msgstr "Visibilitas pesan harus langsung"
+msgstr "Ketampakan pesan harus langsung"
#: lib/pleroma/web/common_api/utils.ex:573
#, elixir-format
@@ -294,7 +294,7 @@ msgstr "Status lebih dari batas karakter"
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
#, elixir-format
msgid "This resource requires authentication."
-msgstr ""
+msgstr "Autentikasi diperlukan untuk hal ini."
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
#, elixir-format
@@ -314,7 +314,7 @@ msgstr ""
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485
#, elixir-format
msgid "You can't revoke your own admin status."
-msgstr ""
+msgstr "Anda tidak dapat mencabut status admin Anda."
#: lib/pleroma/web/oauth/oauth_controller.ex:221
#: lib/pleroma/web/oauth/oauth_controller.ex:308
@@ -382,7 +382,7 @@ msgstr "Gagal"
#: lib/pleroma/web/oauth/oauth_controller.ex:410
#, elixir-format
msgid "Failed to authenticate: %{message}."
-msgstr "Gagal mengotentikasi: %{message}."
+msgstr "Gagal mengautentikasi: %{message}."
#: lib/pleroma/web/oauth/oauth_controller.ex:441
#, elixir-format
@@ -392,7 +392,7 @@ msgstr "Gagal menyiapkan akun pengguna."
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
#, elixir-format
msgid "Insufficient permissions: %{permissions}."
-msgstr ""
+msgstr "Izin tidak cukup: %{permissions}."
#: lib/pleroma/plugs/uploaded_media.ex:104
#, elixir-format
@@ -418,12 +418,12 @@ msgstr ""
#: lib/pleroma/web/oauth/oauth_controller.ex:172
#, elixir-format
msgid "This action is outside the authorized scopes"
-msgstr ""
+msgstr "Tindakan ini diluar jangkauan yang terotorisasi"
#: lib/pleroma/web/oauth/fallback_controller.ex:14
#, elixir-format
msgid "Unknown error, please check the details and try again."
-msgstr "Kesalahan tidak dikenal, harap periksa keterangannya dan coba lagi."
+msgstr "Kesalahan tidak dikenal, harap periksa detailnya dan coba lagi."
#: lib/pleroma/web/oauth/oauth_controller.ex:119
#: lib/pleroma/web/oauth/oauth_controller.ex:158
@@ -444,7 +444,7 @@ msgstr ""
#: lib/pleroma/web/uploader_controller.ex:23
#, elixir-format
msgid "bad request"
-msgstr "permintaan buruk (bad request)"
+msgstr "permintaan buruk"
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
#, elixir-format
@@ -469,7 +469,7 @@ msgstr "CAPTCHA Tidak Valid (Parameter kurang: %{name})"
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
#, elixir-format
msgid "List not found"
-msgstr ""
+msgstr "Daftar tidak ditemukan"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
#, elixir-format
@@ -480,7 +480,7 @@ msgstr "Parameter kurang: %{name}"
#: lib/pleroma/web/oauth/oauth_controller.ex:321
#, elixir-format
msgid "Password reset is required"
-msgstr ""
+msgstr "Diperlukan atur ulang kata sandi"
#: lib/pleroma/tests/auth_test_controller.ex:9
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
@@ -522,7 +522,7 @@ msgstr ""
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
#, elixir-format
msgid "Two-factor authentication enabled, you must use a access token."
-msgstr "Otentikasi dua-faktor diaktifkan, Anda harus menggunakan token akses."
+msgstr "Autentikasi dua langkah diaktifkan, Anda harus menggunakan token akses."
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
#, elixir-format
@@ -552,7 +552,7 @@ msgstr ""
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
#, elixir-format
msgid "Web push subscription is disabled on this Pleroma instance"
-msgstr ""
+msgstr "Langganan push web dinonaktifkan untuk peladen Pleroma ini"
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
#, elixir-format
@@ -562,7 +562,7 @@ msgstr "Anda tidak bisa mencabut status admin/moderator Anda sendiri."
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
#, elixir-format
msgid "authorization required for timeline view"
-msgstr ""
+msgstr "Otorisasi diperlukan untuk tampilan linimasa"
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
#, elixir-format
@@ -572,7 +572,7 @@ msgstr "Akses ditolak"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
#, elixir-format
msgid "This API requires an authenticated user"
-msgstr ""
+msgstr "API ini memerlukan pengguna terautentikasi"
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
#, elixir-format
diff --git a/priv/gettext/id/LC_MESSAGES/posix_errors.po b/priv/gettext/id/LC_MESSAGES/posix_errors.po
new file mode 100644
index 000000000..f5ea93285
--- /dev/null
+++ b/priv/gettext/id/LC_MESSAGES/posix_errors.po
@@ -0,0 +1,163 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-08-11 18:40+0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Translate Toolkit 3.7.2\n"
+
+## This file is a PO Template file.
+##
+## `msgid`s here are often extracted from source code.
+## Add new translations manually only if they're dynamic
+## translations that can't be statically extracted.
+##
+## Run `mix gettext.extract` to bring this file up to
+## date. Leave `msgstr`s empty as changing them here as no
+## effect: edit them in PO (`.po`) files instead.
+msgid "eperm"
+msgstr ""
+
+msgid "eacces"
+msgstr ""
+
+msgid "eagain"
+msgstr ""
+
+msgid "ebadf"
+msgstr ""
+
+msgid "ebadmsg"
+msgstr ""
+
+msgid "ebusy"
+msgstr ""
+
+msgid "edeadlk"
+msgstr ""
+
+msgid "edeadlock"
+msgstr ""
+
+msgid "edquot"
+msgstr ""
+
+msgid "eexist"
+msgstr ""
+
+msgid "efault"
+msgstr ""
+
+msgid "efbig"
+msgstr ""
+
+msgid "eftype"
+msgstr ""
+
+msgid "eintr"
+msgstr ""
+
+msgid "einval"
+msgstr ""
+
+msgid "eio"
+msgstr ""
+
+msgid "eisdir"
+msgstr ""
+
+msgid "eloop"
+msgstr ""
+
+msgid "emfile"
+msgstr ""
+
+msgid "emlink"
+msgstr ""
+
+msgid "emultihop"
+msgstr ""
+
+msgid "enametoolong"
+msgstr ""
+
+msgid "enfile"
+msgstr ""
+
+msgid "enobufs"
+msgstr ""
+
+msgid "enodev"
+msgstr ""
+
+msgid "enolck"
+msgstr ""
+
+msgid "enolink"
+msgstr ""
+
+msgid "enoent"
+msgstr ""
+
+msgid "enomem"
+msgstr ""
+
+msgid "enospc"
+msgstr ""
+
+msgid "enosr"
+msgstr ""
+
+msgid "enostr"
+msgstr ""
+
+msgid "enosys"
+msgstr ""
+
+msgid "enotblk"
+msgstr ""
+
+msgid "enotdir"
+msgstr ""
+
+msgid "enotsup"
+msgstr ""
+
+msgid "enxio"
+msgstr ""
+
+msgid "eopnotsupp"
+msgstr ""
+
+msgid "eoverflow"
+msgstr ""
+
+msgid "epipe"
+msgstr ""
+
+msgid "erange"
+msgstr ""
+
+msgid "erofs"
+msgstr ""
+
+msgid "espipe"
+msgstr ""
+
+msgid "esrch"
+msgstr ""
+
+msgid "estale"
+msgstr ""
+
+msgid "etxtbsy"
+msgstr ""
+
+msgid "exdev"
+msgstr ""
diff --git a/priv/gettext/id/LC_MESSAGES/static_pages.po b/priv/gettext/id/LC_MESSAGES/static_pages.po
new file mode 100644
index 000000000..e2565888a
--- /dev/null
+++ b/priv/gettext/id/LC_MESSAGES/static_pages.po
@@ -0,0 +1,574 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-08-11 18:40+0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Translate Toolkit 3.7.2\n"
+
+## This file is a PO Template file.
+##
+## "msgid"s here are often extracted from source code.
+## Add new translations manually only if they're dynamic
+## translations that can't be statically extracted.
+##
+## Run "mix gettext.extract" to bring this file up to
+## date. Leave "msgstr"s empty as changing them here as no
+## effect: edit them in PO (.po) files instead.
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9
+#, elixir-autogen, elixir-format
+msgctxt "remote follow authorization button"
+msgid "Authorize"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "remote follow error"
+msgid "Error fetching user"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "remote follow header"
+msgid "Remote follow"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "placeholder text for auth code entry"
+msgid "Authentication code"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:10
+#, elixir-autogen, elixir-format
+msgctxt "placeholder text for password entry"
+msgid "Password"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "placeholder text for username entry"
+msgid "Username"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:13
+#, elixir-autogen, elixir-format
+msgctxt "remote follow authorization button for login"
+msgid "Authorize"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:12
+#, elixir-autogen, elixir-format
+msgctxt "remote follow authorization button for mfa"
+msgid "Authorize"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "remote follow error"
+msgid "Error following account"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "remote follow header, need login"
+msgid "Log in to follow"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "remote follow mfa header"
+msgid "Two-factor authentication"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "remote follow success"
+msgid "Account followed!"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:7
+#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7
+#, elixir-autogen, elixir-format
+msgctxt "placeholder text for account id"
+msgid "Your account ID, e.g. lain@quitter.se"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "remote follow authorization button for following with a remote account"
+msgid "Follow"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "remote follow error"
+msgid "Error: %{error}"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "remote follow header"
+msgid "Remotely follow %{nickname}"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12
+#, elixir-autogen, elixir-format
+msgctxt "password reset button"
+msgid "Reset"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "password reset failed homepage link"
+msgid "Homepage"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "password reset failed message"
+msgid "Password reset failed"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "password reset form confirm password prompt"
+msgid "Confirmation"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "password reset form password prompt"
+msgid "Password"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "password reset invalid token message"
+msgid "Invalid Token"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "password reset successful homepage link"
+msgid "Homepage"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "password reset successful message"
+msgid "Password changed!"
+msgstr ""
+
+#: lib/pleroma/web/templates/feed/feed/tag.atom.eex:12
+#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "tag feed description"
+msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse."
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "oauth authorization exists page title"
+msgid "Authorization exists"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32
+#, elixir-autogen, elixir-format
+msgctxt "oauth authorize approve button"
+msgid "Approve"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30
+#, elixir-autogen, elixir-format
+msgctxt "oauth authorize cancel button"
+msgid "Cancel"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23
+#, elixir-autogen, elixir-format
+msgctxt "oauth authorize message"
+msgid "Application %{client_name} is requesting access to your account."
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "oauth authorized page title"
+msgid "Successfully authorized"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "oauth external provider page title"
+msgid "Sign in with external provider"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:13
+#, elixir-autogen, elixir-format
+msgctxt "oauth external provider sign in button"
+msgid "Sign in with %{strategy}"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54
+#, elixir-autogen, elixir-format
+msgctxt "oauth login button"
+msgid "Log In"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51
+#, elixir-autogen, elixir-format
+msgctxt "oauth login password prompt"
+msgid "Password"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47
+#, elixir-autogen, elixir-format
+msgctxt "oauth login username prompt"
+msgid "Username"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:39
+#, elixir-autogen, elixir-format
+msgctxt "oauth register nickname prompt"
+msgid "Pleroma Handle"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:37
+#, elixir-autogen, elixir-format
+msgctxt "oauth register nickname unchangeable warning"
+msgid "Choose carefully! You won't be able to change this later. You will be able to change your display name, though."
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:18
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page email prompt"
+msgid "Email"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:10
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page fill form prompt"
+msgid "If you'd like to register a new account, please provide the details below."
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:35
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page login button"
+msgid "Proceed as existing user"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:31
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page login password prompt"
+msgid "Password"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:24
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page login prompt"
+msgid "Alternatively, sign in to connect to existing account."
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:27
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page login username prompt"
+msgid "Name or email"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:14
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page nickname prompt"
+msgid "Nickname"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:22
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page register button"
+msgid "Proceed as new user"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page title"
+msgid "Registration Details"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:36
+#, elixir-autogen, elixir-format
+msgctxt "oauth register page title"
+msgid "This is the first time you visit! Please enter your Pleroma handle."
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "oauth scopes message"
+msgid "The following permissions will be granted"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:2
+#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "oauth token code message"
+msgid "Token code is
%{token}"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12
+#, elixir-autogen, elixir-format
+msgctxt "mfa auth code prompt"
+msgid "Authentication code"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "mfa auth page title"
+msgid "Two-factor authentication"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:23
+#, elixir-autogen, elixir-format
+msgctxt "mfa auth page use recovery code link"
+msgid "Enter a two-factor recovery code"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20
+#, elixir-autogen, elixir-format
+msgctxt "mfa auth verify code button"
+msgid "Verify"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "mfa recover page title"
+msgid "Two-factor recovery"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:12
+#, elixir-autogen, elixir-format
+msgctxt "mfa recover recovery code prompt"
+msgid "Recovery code"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:23
+#, elixir-autogen, elixir-format
+msgctxt "mfa recover use 2fa code link"
+msgid "Enter a two-factor code"
+msgstr ""
+
+#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:20
+#, elixir-autogen, elixir-format
+msgctxt "mfa recover verify recovery code button"
+msgid "Verify"
+msgstr ""
+
+#: lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "static fe profile page remote follow button"
+msgid "Remote follow"
+msgstr ""
+
+#: lib/pleroma/web/templates/email/digest.html.eex:163
+#, elixir-autogen, elixir-format
+msgctxt "digest email header line"
+msgid "Hey %{nickname}, here is what you've missed!"
+msgstr ""
+
+#: lib/pleroma/web/templates/email/digest.html.eex:544
+#, elixir-autogen, elixir-format
+msgctxt "digest email receiver address"
+msgid "The email address you are subscribed as is %{email}. "
+msgstr ""
+
+#: lib/pleroma/web/templates/email/digest.html.eex:538
+#, elixir-autogen, elixir-format
+msgctxt "digest email sending reason"
+msgid "You have received this email because you have signed up to receive digest emails from %{instance} Pleroma instance."
+msgstr ""
+
+#: lib/pleroma/web/templates/email/digest.html.eex:547
+#, elixir-autogen, elixir-format
+msgctxt "digest email unsubscribe action"
+msgid "To unsubscribe, please go %{here}."
+msgstr ""
+
+#: lib/pleroma/web/templates/email/digest.html.eex:547
+#, elixir-autogen, elixir-format
+msgctxt "digest email unsubscribe action link text"
+msgid "here"
+msgstr ""
+
+#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "mailer unsubscribe failed message"
+msgid "UNSUBSCRIBE FAILURE"
+msgstr ""
+
+#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1
+#, elixir-autogen, elixir-format
+msgctxt "mailer unsubscribe successful message"
+msgid "UNSUBSCRIBE SUCCESSFUL"
+msgstr ""
+
+#: lib/pleroma/web/templates/email/digest.html.eex:385
+#, elixir-format
+msgctxt "new followers count header"
+msgid "%{count} New Follower"
+msgid_plural "%{count} New Followers"
+msgstr[0] ""
+msgstr[1] ""
+
+#: lib/pleroma/emails/user_email.ex:356
+#, elixir-autogen, elixir-format
+msgctxt "account archive email body - self-requested"
+msgid "You requested a full backup of your Pleroma account. It's ready for download:
\n%{download_url}
\n"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:384
+#, elixir-autogen, elixir-format
+msgctxt "account archive email subject"
+msgid "Your account archive is ready"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:188
+#, elixir-autogen, elixir-format
+msgctxt "approval pending email body"
+msgid "Awaiting Approval
\nYour account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.
\n"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:202
+#, elixir-autogen, elixir-format
+msgctxt "approval pending email subject"
+msgid "Your account is awaiting approval"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:158
+#, elixir-autogen, elixir-format
+msgctxt "confirmation email body"
+msgid "Thank you for registering on %{instance_name}
\nEmail confirmation is required to activate the account.
\nPlease click the following link to activate your account.
\n"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:174
+#, elixir-autogen, elixir-format
+msgctxt "confirmation email subject"
+msgid "%{instance_name} account confirmation"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:310
+#, elixir-autogen, elixir-format
+msgctxt "digest email subject"
+msgid "Your digest from %{instance_name}"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:81
+#, elixir-autogen, elixir-format
+msgctxt "password reset email body"
+msgid "Reset your password at %{instance_name}
\nSomeone has requested password change for your account at %{instance_name}.
\nIf it was you, visit the following link to proceed: reset password.
\nIf it was someone else, nothing to worry about: your data is secure and your password has not been changed.
\n"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:98
+#, elixir-autogen, elixir-format
+msgctxt "password reset email subject"
+msgid "Password reset"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:215
+#, elixir-autogen, elixir-format
+msgctxt "successful registration email body"
+msgid "Hello @%{nickname},
\nYour account at %{instance_name} has been registered successfully.
\nNo further action is required to activate your account.
\n"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:231
+#, elixir-autogen, elixir-format
+msgctxt "successful registration email subject"
+msgid "Account registered on %{instance_name}"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:119
+#, elixir-autogen, elixir-format
+msgctxt "user invitation email body"
+msgid "You are invited to %{instance_name}
\n%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.
\nClick the following link to register: accept invitation.
\n"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:136
+#, elixir-autogen, elixir-format
+msgctxt "user invitation email subject"
+msgid "Invitation to %{instance_name}"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:53
+#, elixir-autogen, elixir-format
+msgctxt "welcome email html body"
+msgid "Welcome to %{instance_name}!"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:41
+#, elixir-autogen, elixir-format
+msgctxt "welcome email subject"
+msgid "Welcome to %{instance_name}!"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:65
+#, elixir-autogen, elixir-format
+msgctxt "welcome email text body"
+msgid "Welcome to %{instance_name}!"
+msgstr ""
+
+#: lib/pleroma/emails/user_email.ex:368
+#, elixir-autogen, elixir-format
+msgctxt "account archive email body - admin requested"
+msgid "Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:
\n%{download_url}
\n"
+msgstr ""
+
+#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:123
+#, elixir-autogen, elixir-format
+msgctxt "remote follow error message - unknown error"
+msgid "Something went wrong."
+msgstr ""
+
+#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:67
+#, elixir-autogen, elixir-format
+msgctxt "remote follow error message - user not found"
+msgid "Could not find user"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:8
+#, elixir-autogen, elixir-format
+msgctxt "status interact authorization button"
+msgid "Interact"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:2
+#, elixir-autogen, elixir-format
+msgctxt "status interact error"
+msgid "Error: %{error}"
+msgstr ""
+
+#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:95
+#, elixir-autogen, elixir-format
+msgctxt "status interact error message - status not found"
+msgid "Could not find status"
+msgstr ""
+
+#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:144
+#, elixir-autogen, elixir-format
+msgctxt "status interact error message - unknown error"
+msgid "Something went wrong."
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "status interact header"
+msgid "Interacting with %{nickname}'s %{status_link}"
+msgstr ""
+
+#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:4
+#, elixir-autogen, elixir-format
+msgctxt "status interact header - status link text"
+msgid "status"
+msgstr ""
diff --git a/priv/scrubbers/default.ex b/priv/scrubbers/default.ex
index 342ef9944..8963d99e8 100644
--- a/priv/scrubbers/default.ex
+++ b/priv/scrubbers/default.ex
@@ -83,48 +83,55 @@ defmodule Pleroma.HTML.Scrubber.Default do
"quote-inline",
"invisible",
"ellipsis",
- "mfm-center",
- "mfm-flip",
- "mfm-font",
- "mfm-blur",
- "mfm-rotate",
- "mfm-x2",
- "mfm-x3",
- "mfm-x4",
- "mfm-position",
- "mfm-scale",
- "mfm-fg",
- "mfm-bg",
+ "mfm-tada",
"mfm-jelly",
"mfm-twitch",
"mfm-shake",
"mfm-spin",
"mfm-jump",
"mfm-bounce",
+ "mfm-flip",
+ "mfm-x2",
+ "mfm-x3",
+ "mfm-x4",
+ "mfm-scale",
+ "mfm-position",
+ "mfm-fg",
+ "mfm-bg",
+ "mfm-border",
+ "mfm-font",
+ "mfm-blur",
"mfm-rainbow",
- "mfm-tada",
- "mfm-sparkle"
+ "mfm-sparkle",
+ "mfm-rotate",
+ "mfm-ruby",
+ "mfm-unixtime"
])
Meta.allow_tag_with_this_attribute_values(:p, "class", ["quote-inline"])
Meta.allow_tag_with_these_attributes(:span, [
"lang",
- "data-mfm-h",
- "data-mfm-v",
+ "data-mfm-speed",
+ "data-mfm-delay",
+ "data-mfm-left",
+ "data-mfm-alternate",
"data-mfm-x",
"data-mfm-y",
- "data-mfm-alternate",
- "data-mfm-speed",
- "data-mfm-deg",
- "data-mfm-left",
+ "data-mfm-h",
+ "data-mfm-v",
+ "data-mfm-color",
+ "data-mfm-width",
+ "data-mfm-style",
+ "data-mfm-radius",
+ "data-mfm-noclip",
"data-mfm-serif",
"data-mfm-monospace",
"data-mfm-cursive",
"data-mfm-fantasy",
"data-mfm-emoji",
"data-mfm-math",
- "data-mfm-color"
+ "data-mfm-deg"
])
Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"])
diff --git a/test/pleroma/dependency_version_test.exs b/test/pleroma/dependency_version_test.exs
new file mode 100644
index 000000000..0c9c1e939
--- /dev/null
+++ b/test/pleroma/dependency_version_test.exs
@@ -0,0 +1,16 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.DependencyVersionTest do
+ use ExUnit.Case, async: true
+
+ test "uses majic 1.2" do
+ majic_version =
+ :majic
+ |> Application.spec(:vsn)
+ |> to_string()
+
+ assert Version.match?(majic_version, "~> 1.2")
+ end
+end
diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs
index 47f6f5f76..078bb643c 100644
--- a/test/pleroma/integration/mastodon_websocket_test.exs
+++ b/test/pleroma/integration/mastodon_websocket_test.exs
@@ -279,6 +279,26 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
end)
end
+ test "prefers sec-websocket-protocol token over query access_token", %{
+ token: token,
+ user: user
+ } do
+ assert {:ok, state} =
+ Pleroma.Web.MastodonAPI.WebsocketHandler.connect(%{
+ params: %{"stream" => "user", "access_token" => "invalid"},
+ connect_info: %{
+ sec_websocket_headers: [
+ {"sec-websocket-version", "13"},
+ {"sec-websocket-protocol", token.token}
+ ]
+ }
+ })
+
+ assert state.user.id == user.id
+ assert state.oauth_token.id == token.id
+ assert state.topics != []
+ end
+
test "accepts valid token on client-sent event", %{token: token} do
assert {:ok, pid} = start_socket()
diff --git a/test/pleroma/signature_test.exs b/test/pleroma/signature_test.exs
index 572d7acc3..0c7c4c840 100644
--- a/test/pleroma/signature_test.exs
+++ b/test/pleroma/signature_test.exs
@@ -11,6 +11,7 @@ defmodule Pleroma.SignatureTest do
import Mock
alias Pleroma.Signature
+ alias Pleroma.StubbedHTTPSignaturesMock, as: HTTPSignaturesMock
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -103,6 +104,18 @@ defmodule Pleroma.SignatureTest do
end
end
+ describe "validate_signature/1" do
+ test "treats HTTP signature errors as failed validation" do
+ conn = %Plug.Conn{method: "GET", request_path: "/inbox", req_headers: []}
+
+ Mox.expect(HTTPSignaturesMock, :validate_conn, fn _conn ->
+ {:error, :request_target_header}
+ end)
+
+ assert Signature.validate_signature(conn) == false
+ end
+ end
+
describe "key_id_to_actor_id/1" do
test "it properly deduces the actor id for misskey" do
assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") ==
diff --git a/test/pleroma/user/search_test.exs b/test/pleroma/user/search_test.exs
new file mode 100644
index 000000000..c1aca90bc
--- /dev/null
+++ b/test/pleroma/user/search_test.exs
@@ -0,0 +1,195 @@
+# Pleroma: A lightweight social networking server
+# Copyright © Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.SearchTest do
+ use Pleroma.DataCase, async: false
+
+ import Pleroma.Factory
+
+ alias Pleroma.Instances
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ describe "search/2 mention suggestions" do
+ test "prioritizes followed/follower users before others" do
+ user = insert(:user)
+
+ related =
+ insert(:user,
+ local: false,
+ nickname: "hj@real.example",
+ ap_id: "https://real.example/users/hj",
+ last_status_at: ~N[2020-01-01 00:00:00]
+ )
+
+ other = insert(:user, nickname: "hj", last_status_at: ~N[2020-01-02 00:00:00])
+
+ {:ok, _related, _user} = User.follow(related, user)
+
+ results = User.search("hj", for_user: user) |> Enum.map(& &1.id)
+
+ assert results == [related.id, other.id]
+ end
+
+ test "orders followed/follower users by most recent activity" do
+ user = insert(:user)
+
+ older =
+ insert(:user,
+ local: false,
+ nickname: "ali@remote.example",
+ ap_id: "https://remote.example/users/ali",
+ last_status_at: ~N[2020-01-01 00:00:00]
+ )
+
+ newer =
+ insert(:user,
+ local: false,
+ nickname: "alia@remote.example",
+ ap_id: "https://remote.example/users/alia",
+ last_status_at: ~N[2020-01-02 00:00:00]
+ )
+
+ {:ok, _user, _older} = User.follow(user, older)
+ {:ok, _user, _newer} = User.follow(user, newer)
+
+ assert [newer.id, older.id] ==
+ User.search("ali", for_user: user)
+ |> Enum.map(& &1.id)
+ end
+
+ test "groups followed/follower users first and sorts them by recency" do
+ user = insert(:user)
+
+ following_newest =
+ insert(:user,
+ local: false,
+ nickname: "mentiontesta@related.example",
+ ap_id: "https://related.example/users/mentiontesta",
+ last_status_at: ~N[2020-01-03 00:00:00]
+ )
+
+ follower_middle =
+ insert(:user,
+ local: false,
+ nickname: "mentiontestb@related.example",
+ ap_id: "https://related.example/users/mentiontestb",
+ last_status_at: ~N[2020-01-02 00:00:00]
+ )
+
+ mutual_oldest =
+ insert(:user,
+ local: false,
+ nickname: "mentiontestc@related.example",
+ ap_id: "https://related.example/users/mentiontestc",
+ last_status_at: ~N[2020-01-01 00:00:00]
+ )
+
+ unrelated_newer =
+ insert(:user,
+ local: false,
+ nickname: "mentiontestd@unrelated.example",
+ ap_id: "https://unrelated.example/users/mentiontestd",
+ last_status_at: ~N[2020-01-04 00:00:00]
+ )
+
+ {:ok, _user, _following_newest} = User.follow(user, following_newest)
+ {:ok, _follower_middle, _user} = User.follow(follower_middle, user)
+
+ {:ok, _user, _mutual_oldest} = User.follow(user, mutual_oldest)
+ {:ok, _mutual_oldest, _user} = User.follow(mutual_oldest, user)
+
+ results = User.search("mentiontest", for_user: user)
+
+ assert Enum.map(results, & &1.id) ==
+ [following_newest.id, follower_middle.id, mutual_oldest.id, unrelated_newer.id]
+ end
+
+ test "uses last_active_at when last_status_at is missing" do
+ user = insert(:user)
+
+ older =
+ insert(:user,
+ local: false,
+ nickname: "activefallbacka@remote.example",
+ ap_id: "https://remote.example/users/activefallbacka",
+ last_status_at: nil,
+ last_active_at: ~N[2020-01-01 00:00:00]
+ )
+
+ newer =
+ insert(:user,
+ local: false,
+ nickname: "activefallbackb@remote.example",
+ ap_id: "https://remote.example/users/activefallbackb",
+ last_status_at: nil,
+ last_active_at: ~N[2020-01-02 00:00:00]
+ )
+
+ {:ok, _user, _older} = User.follow(user, older)
+ {:ok, _user, _newer} = User.follow(user, newer)
+
+ assert [newer.id, older.id] ==
+ User.search("activefallback", for_user: user)
+ |> Enum.map(& &1.id)
+ end
+
+ test "does not return deactivated users even if related" do
+ user = insert(:user)
+
+ active =
+ insert(:user,
+ local: false,
+ nickname: "deactivatedtesta@remote.example",
+ ap_id: "https://remote.example/users/deactivatedtesta",
+ last_status_at: ~N[2020-01-02 00:00:00]
+ )
+
+ deactivated =
+ insert(:user,
+ local: false,
+ nickname: "deactivatedtestb@remote.example",
+ ap_id: "https://remote.example/users/deactivatedtestb",
+ last_status_at: ~N[2020-01-03 00:00:00]
+ )
+
+ {:ok, _user, _active} = User.follow(user, active)
+ {:ok, _user, _deactivated} = User.follow(user, deactivated)
+ Repo.update!(Ecto.Changeset.change(deactivated, is_active: false))
+
+ results = User.search("deactivatedtest", for_user: user) |> Enum.map(& &1.id)
+
+ assert results == [active.id]
+ end
+
+ test "does not return users from unreachable instances" do
+ user = insert(:user)
+
+ {:ok, _instance} = Instances.set_unreachable("dead.example")
+
+ dead =
+ insert(:user,
+ local: false,
+ nickname: "ali@dead.example",
+ ap_id: "https://dead.example/users/ali",
+ last_status_at: ~N[2020-01-02 00:00:00]
+ )
+
+ alive =
+ insert(:user,
+ local: false,
+ nickname: "ali@alive.example",
+ ap_id: "https://alive.example/users/ali",
+ last_status_at: ~N[2020-01-02 00:00:00]
+ )
+
+ {:ok, _user, _alive} = User.follow(user, alive)
+ {:ok, _user, _dead} = User.follow(user, dead)
+
+ results = User.search("ali", for_user: user) |> Enum.map(& &1.id)
+
+ assert results == [alive.id]
+ end
+ end
+end
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
index 3988c3912..68e39b3a0 100644
--- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -950,6 +950,50 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
refute Activity.get_by_ap_id(data["id"])
end
+ test "does not process post with Host header not for us", %{conn: conn} do
+ alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
+ object_id = "https://one.com/objects/inbox-forged-note"
+
+ data = %{
+ "type" => "Create",
+ "actor" => alice.ap_id,
+ "id" => "https://one.com/activities/inbox-forged-create",
+ "context" => "https://one.com/contexts/inbox-forged-create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "object" => %{
+ "type" => "Note",
+ "id" => object_id,
+ "actor" => alice.ap_id,
+ "attributedTo" => alice.ap_id,
+ "context" => "https://one.com/contexts/inbox-forged-create",
+ "content" => "forged post",
+ "published" => "2024-07-25T13:33:31Z",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => []
+ }
+ }
+
+ # Plug will complain when replacing raw host header with put_req_header.
+ # The Plug way is updating conn.host, but that isn't the raw header
+ # and that isn't used in the EnsureHostMatchesPlug, because it doesn't include the port.
+ conn =
+ conn
+ |> assign_valid_signature_for_actor(alice)
+ |> delete_req_header("host")
+ |> put_req_header("content-type", "application/activity+json")
+
+ conn = %{conn | req_headers: conn.req_headers ++ [{"host", "invalid.example.com"}]}
+ conn = post(conn, "/inbox", data)
+
+ assert "Host header does not match this instance" == conn.resp_body
+ assert 400 == conn.status
+ assert true == conn.halted
+
+ refute Activity.get_by_ap_id(data["id"])
+ refute Object.get_by_ap_id(object_id)
+ end
+
test "accept follow activity", %{conn: conn} do
clear_config([:instance, :federating], true)
relay = Relay.get_actor()
diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs
index c1e01557d..bedb466a4 100644
--- a/test/pleroma/web/activity_pub/transmogrifier_test.exs
+++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs
@@ -86,6 +86,43 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert activity.data["cc"] == [user.ap_id]
end
+ test "it rejects Flag activities when both reporter and reported account are remote" do
+ reporter = insert(:user, local: false, domain: "mastodon.cat")
+ reported = insert(:user, local: false, domain: "nicecrew.digital")
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "actor" => reporter.ap_id,
+ "content" => "blocked AND reported!!!",
+ "object" => [reported.ap_id, "https://nicecrew.digital/objects/report-status"],
+ "type" => "Flag"
+ }
+
+ assert {:reject, reason} = Transmogrifier.handle_incoming(message)
+ assert reason =~ "third-party report"
+ refute "Flag" |> Pleroma.Activity.Queries.by_type() |> Pleroma.Repo.one()
+ end
+
+ test "it accepts Flag activities with just actor id as object" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "cc" => [user.ap_id],
+ "object" => user.ap_id,
+ "type" => "Flag",
+ "content" => "blocked AND reported!!!",
+ "actor" => other_user.ap_id
+ }
+
+ assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+ assert activity.data["content"] == "blocked AND reported!!!"
+ assert activity.data["actor"] == other_user.ap_id
+ assert activity.data["cc"] == [user.ap_id]
+ end
+
test "it accepts Move activities" do
old_user = insert(:user)
new_user = insert(:user)
diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs
index ea1795c0b..5d0ad4572 100644
--- a/test/pleroma/web/common_api_test.exs
+++ b/test/pleroma/web/common_api_test.exs
@@ -740,7 +740,7 @@ defmodule Pleroma.Web.CommonAPITest do
{:ok, activity} =
CommonAPI.post(user, %{
- status: "$[spin.speed=1s=boom malformed]",
+ status: "$[spin malformed",
content_type: "text/x.misskeymarkdown"
})
diff --git a/test/pleroma/web/plugs/ensure_host_matches_plug_test.exs b/test/pleroma/web/plugs/ensure_host_matches_plug_test.exs
new file mode 100644
index 000000000..8ace74dfb
--- /dev/null
+++ b/test/pleroma/web/plugs/ensure_host_matches_plug_test.exs
@@ -0,0 +1,121 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.EnsureHostMatchesPlugTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.Plugs.EnsureHostMatchesPlug
+
+ import Plug.Conn
+ import Tesla.Mock
+
+ setup do
+ mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ defp set_host(conn, host), do: %{conn | req_headers: conn.req_headers ++ [{"host", host}]}
+
+ describe "EnsureHostMatchesPlug" do
+ setup do
+ conn = build_conn(:post, "/cofe") |> assign(:valid_signature, true)
+ [conn: conn]
+ end
+
+ test "gracefully handles no Host header", %{conn: conn} do
+ conn = EnsureHostMatchesPlug.call(conn, %{})
+
+ assert conn.status == 400
+ assert conn.halted == true
+ assert conn.resp_body == "Host header not provided"
+ end
+
+ test "gracefully handles empty Host header", %{conn: conn} do
+ conn =
+ conn
+ |> set_host("")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.status == 400
+ assert conn.halted == true
+ assert conn.resp_body == "Host header not provided"
+ end
+
+ test "it rejects Host header not matching Endpoint URL", %{conn: conn} do
+ conn =
+ conn
+ |> set_host("invalid.example.com")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.status == 400
+ assert conn.halted == true
+ assert conn.resp_body == "Host header does not match this instance"
+ end
+
+ test "it rejects Host header not matching Endpoint with port", %{conn: conn} do
+ endpoint = URI.parse(Endpoint.url())
+
+ conn =
+ conn
+ |> set_host("invalid.example.com:#{endpoint.port}")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.status == 400
+ assert conn.halted == true
+ assert conn.resp_body == "Host header does not match this instance"
+ end
+
+ test "it rejects Host header not matching Endpoint port", %{conn: conn} do
+ endpoint = URI.parse(Endpoint.url())
+
+ conn =
+ conn
+ |> set_host("#{endpoint.host}:25")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.status == 400
+ assert conn.halted == true
+ assert conn.resp_body == "Host header does not match this instance"
+ end
+
+ test "it rejects multiple Host headers", %{conn: conn} do
+ conn =
+ conn
+ |> set_host("host1.example.com")
+ |> set_host("host2.example.com")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.status == 400
+ assert conn.halted == true
+ assert conn.resp_body == "More than one Host header provided"
+ end
+
+ test "it works for Host header without port", %{conn: conn} do
+ endpoint = URI.parse(Endpoint.url())
+
+ conn =
+ conn
+ |> set_host("#{endpoint.host}")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.halted == false
+ assert Map.get(conn.assigns, :valid_host_header, nil)
+ end
+
+ test "it works for Host header with port same as Endpoint", %{
+ conn: conn
+ } do
+ endpoint = URI.parse(Endpoint.url())
+
+ conn =
+ conn
+ |> set_host("#{endpoint.host}:#{endpoint.port}")
+ |> EnsureHostMatchesPlug.call(%{})
+
+ assert conn.halted == false
+ assert Map.get(conn.assigns, :valid_host_header, nil)
+ end
+ end
+end
diff --git a/test/pleroma/web/plugs/remote_ip_test.exs b/test/pleroma/web/plugs/remote_ip_test.exs
index 37b751370..19e786f8a 100644
--- a/test/pleroma/web/plugs/remote_ip_test.exs
+++ b/test/pleroma/web/plugs/remote_ip_test.exs
@@ -106,4 +106,38 @@ defmodule Pleroma.Web.Plugs.RemoteIpTest do
assert conn.remote_ip == {1, 1, 1, 1}
end
+
+ test "reserved ranges are configurable" do
+ clear_config([RemoteIp, :reserved], [])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "1.1.1.1, 10.0.0.3")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {10, 0, 0, 3}
+ end
+
+ test "clients override reserved ranges" do
+ clear_config([RemoteIp, :clients], ["10.0.0.0/8"])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "1.1.1.1, 10.0.0.3")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {10, 0, 0, 3}
+ end
+
+ test "clients override proxies" do
+ clear_config([RemoteIp, :clients], ["10.0.0.3"])
+ clear_config([RemoteIp, :proxies], ["10.0.0.0/8"])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "1.1.1.1, 10.0.0.3")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {10, 0, 0, 3}
+ end
end
diff --git a/test/pleroma/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs
index 2fae83305..68ded6906 100644
--- a/test/pleroma/web/static_fe/static_fe_controller_test.exs
+++ b/test/pleroma/web/static_fe/static_fe_controller_test.exs
@@ -28,6 +28,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
assert html_response(conn, 200) =~ user.nickname
end
+ test "renders profile HTML inside the default app layout", %{conn: conn, user: user} do
+ conn = get(conn, "/users/#{user.nickname}")
+
+ html = html_response(conn, 200)
+ assert html =~ ""
+ assert html =~ ~s(class="instance-header")
+ assert html =~ ~s()
+ end
+
test "404 when user not found", %{conn: conn} do
conn = get(conn, "/users/limpopo")
diff --git a/test/pleroma/workers/signature_retry_worker_test.exs b/test/pleroma/workers/signature_retry_worker_test.exs
index 94dd5f6c1..3806ecac9 100644
--- a/test/pleroma/workers/signature_retry_worker_test.exs
+++ b/test/pleroma/workers/signature_retry_worker_test.exs
@@ -16,12 +16,13 @@ defmodule Pleroma.Workers.SignatureRetryWorkerTest do
alias Pleroma.Signature
alias Pleroma.User
alias Pleroma.Web.ActivityPub.UserView
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.Federator
alias Pleroma.Workers.SignatureRetryWorker
defp signature_headers_for(%User{} = signer) do
[
- {"host", "local.test"},
+ {"host", "#{URI.parse(Endpoint.url()).host}"},
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
{"digest", "SHA-256=fake-digest"},
{"content-type", "application/activity+json"},
@@ -245,6 +246,66 @@ defmodule Pleroma.Workers.SignatureRetryWorkerTest do
refute Activity.get_by_ap_id(create["id"])
end
+ test "cancels when the Host header does not match Endpoint" 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" => []
+ }
+ }
+
+ expect_signature_from(alice)
+
+ headers =
+ [
+ {"host", "invalid.example.com"},
+ {"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
+ {"digest", "SHA-256=fake-digest"},
+ {"content-type", "application/activity+json"},
+ {
+ "signature",
+ "keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
+ }
+ ]
+
+ assert {:ok, oban_job} =
+ Federator.incoming_failed_signature_ap_doc(%{
+ method: "POST",
+ req_headers: headers,
+ request_path: "/inbox",
+ params: create,
+ query_string: ""
+ })
+
+ log =
+ capture_log([level: :warning], fn ->
+ assert {:cancel, :host_header_mismatch} = SignatureRetryWorker.perform(oban_job)
+ end)
+
+ assert log =~ "Failed-signature inbox retry rejected"
+ assert log =~ "reason=:host_header_mismatch"
+ assert log =~ "payload_actor=\"https://one.com/users/alice\""
+ assert log =~ "signature_actor=\"https://one.com/users/alice\""
+ assert log =~ "activity_id=\"https://one.com/activities/invalid-signature-create\""
+ assert log =~ "type=\"Create\""
+ assert log =~ "request_path=\"/inbox\""
+
+ 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")
@@ -309,11 +370,11 @@ defmodule Pleroma.Workers.SignatureRetryWorkerTest do
"content-type" => "application/activity+json",
date: date,
digest: digest,
- host: "local.test"
+ host: "#{URI.parse(Endpoint.url()).host}"
})
req_headers = [
- ["host", "local.test"],
+ ["host", "#{URI.parse(Endpoint.url()).host}"],
["date", date],
["digest", digest],
["content-type", "application/activity+json"],
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index f010fec33..c01516169 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -119,7 +119,10 @@ defmodule Pleroma.Web.ConnCase do
DataCase.stub_pipeline()
Mox.verify_on_exit!()
+ endpoint = URI.parse(Pleroma.Web.Endpoint.url())
+ conn = Phoenix.ConnTest.build_conn()
+ conn = %{conn | req_headers: [{"host", "#{endpoint.host}:#{endpoint.port}"}]}
- {:ok, conn: Phoenix.ConnTest.build_conn()}
+ {:ok, conn: conn}
end
end