diff options
Diffstat (limited to 'test')
37 files changed, 1492 insertions, 117 deletions
diff --git a/test/fixtures/fep-e232.json b/test/fixtures/fep-e232.json new file mode 100644 index 000000000..e9d12ae35 --- /dev/null +++ b/test/fixtures/fep-e232.json @@ -0,0 +1,31 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "type": "Create", + "actor": "https://example.org/users/alice", + "object": { + "id": "https://example.org/objects/10", + "type": "Note", + "attributedTo": "https://example.org/users/alice", + "content": "<p>test <a href=\"https://example.org/objects/9\">https://example.org/objects/9</a></p>", + "published": "2022-10-01T21:30:05.211215Z", + "tag": [ + { + "name": "@bob@example.net", + "type": "Mention", + "href": "https://example.net/users/bob" + }, + { + "name": "https://example.org/objects/9", + "type": "Link", + "href": "https://example.org/objects/9", + "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://example.org/users/alice/followers" + ] + } +} diff --git a/test/fixtures/mastodon-nodeinfo20.json b/test/fixtures/mastodon-nodeinfo20.json new file mode 100644 index 000000000..35010fdf0 --- /dev/null +++ b/test/fixtures/mastodon-nodeinfo20.json @@ -0,0 +1 @@ +{"version":"2.0","software":{"name":"mastodon","version":"4.1.0"},"protocols":["activitypub"],"services":{"outbound":[],"inbound":[]},"usage":{"users":{"total":971090,"activeMonth":167218,"activeHalfyear":384808},"localPosts":52071541},"openRegistrations":true,"metadata":{}}
\ No newline at end of file diff --git a/test/fixtures/mastodon-well-known-nodeinfo.json b/test/fixtures/mastodon-well-known-nodeinfo.json new file mode 100644 index 000000000..237d5462a --- /dev/null +++ b/test/fixtures/mastodon-well-known-nodeinfo.json @@ -0,0 +1 @@ +{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"https://mastodon.example.org/nodeinfo/2.0"}]}
\ No newline at end of file diff --git a/test/fixtures/wildebeest-nodeinfo21.json b/test/fixtures/wildebeest-nodeinfo21.json new file mode 100644 index 000000000..c6af474bf --- /dev/null +++ b/test/fixtures/wildebeest-nodeinfo21.json @@ -0,0 +1 @@ +{"version":"2.1","software":{"name":"wildebeest","version":"0.0.1","repository":"https://github.com/cloudflare/wildebeest"},"protocols":["activitypub"],"usage":{"users":{"total":1,"activeMonth":1,"activeHalfyear":1}},"openRegistrations":false,"metadata":{"upstream":{"name":"mastodon","version":"3.5.1"}}}
\ No newline at end of file diff --git a/test/fixtures/wildebeest-well-known-nodeinfo.json b/test/fixtures/wildebeest-well-known-nodeinfo.json new file mode 100644 index 000000000..c7ddb43af --- /dev/null +++ b/test/fixtures/wildebeest-well-known-nodeinfo.json @@ -0,0 +1 @@ +{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"https://wildebeest.example.org/nodeinfo/2.0"},{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.1","href":"https://wildebeest.example.org/nodeinfo/2.1"}]}
\ No newline at end of file diff --git a/test/fixtures/xml_billion_laughs.xml b/test/fixtures/xml_billion_laughs.xml new file mode 100644 index 000000000..75fb24cae --- /dev/null +++ b/test/fixtures/xml_billion_laughs.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!DOCTYPE lolz [ + <!ENTITY lol "lol"> + <!ELEMENT lolz (#PCDATA)> + <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> + <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> + <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> + <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> + <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> + <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> + <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> + <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> + <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> +]> +<lolz>&lol9;</lolz> diff --git a/test/fixtures/xml_external_entities.xml b/test/fixtures/xml_external_entities.xml new file mode 100644 index 000000000..d5ff87134 --- /dev/null +++ b/test/fixtures/xml_external_entities.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> +<stockCheck><productId>&xxe;</productId></stockCheck> diff --git a/test/pleroma/config/release_runtime_provider_test.exs b/test/pleroma/config/release_runtime_provider_test.exs index 4e0d4c838..8d2a93d6c 100644 --- a/test/pleroma/config/release_runtime_provider_test.exs +++ b/test/pleroma/config/release_runtime_provider_test.exs @@ -10,13 +10,15 @@ defmodule Pleroma.Config.ReleaseRuntimeProviderTest do describe "load/2" do test "loads release defaults config and warns about non-existent runtime config" do ExUnit.CaptureIO.capture_io(fn -> - merged = ReleaseRuntimeProvider.load([], []) + merged = ReleaseRuntimeProvider.load([], config_path: "/var/empty/config.exs") assert merged == Pleroma.Config.Holder.release_defaults() end) =~ "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" end test "merged runtime config" do + assert :ok == File.chmod!("test/fixtures/config/temp.secret.exs", 0o640) + merged = ReleaseRuntimeProvider.load([], config_path: "test/fixtures/config/temp.secret.exs") @@ -25,6 +27,8 @@ defmodule Pleroma.Config.ReleaseRuntimeProviderTest do end test "merged exported config" do + assert :ok == File.chmod!("test/fixtures/config/temp.exported_from_db.secret.exs", 0o640) + ExUnit.CaptureIO.capture_io(fn -> merged = ReleaseRuntimeProvider.load([], @@ -37,6 +41,9 @@ defmodule Pleroma.Config.ReleaseRuntimeProviderTest do end test "runtime config is merged with exported config" do + assert :ok == File.chmod!("test/fixtures/config/temp.secret.exs", 0o640) + assert :ok == File.chmod!("test/fixtures/config/temp.exported_from_db.secret.exs", 0o640) + merged = ReleaseRuntimeProvider.load([], config_path: "test/fixtures/config/temp.secret.exs", diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.ex b/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.ex new file mode 100644 index 000000000..226383c3c --- /dev/null +++ b/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.ex @@ -0,0 +1,25 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUriTest do + use Pleroma.DataCase, async: true + + alias Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri + + test "diaspora://" do + text = "diaspora://alice@fediverse.example/post/deadbeefdeadbeefdeadbeefdeadbeef" + assert {:ok, text} = BareUri.cast(text) + end + + test "nostr:" do + text = "nostr:note1gwdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + assert {:ok, text} = BareUri.cast(text) + end + + test "errors for non-URIs" do + assert :error == SafeText.cast(1) + assert :error == SafeText.cast("foo") + assert :error == SafeText.cast("foo bar") + end +end diff --git a/test/pleroma/emoji/pack_test.exs b/test/pleroma/emoji/pack_test.exs index 18b99da75..00001abfc 100644 --- a/test/pleroma/emoji/pack_test.exs +++ b/test/pleroma/emoji/pack_test.exs @@ -90,4 +90,8 @@ defmodule Pleroma.Emoji.PackTest do assert updated_pack.files_count == 1 end + + test "load_pack/1 ignores path traversal in a forged pack name", %{pack: pack} do + assert {:ok, ^pack} = Pack.load_pack("../../../../../dump_pack") + end end diff --git a/test/pleroma/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs index 861519bce..a769f9362 100644 --- a/test/pleroma/instances/instance_test.exs +++ b/test/pleroma/instances/instance_test.exs @@ -161,6 +161,66 @@ defmodule Pleroma.Instances.InstanceTest do end end + describe "get_or_update_metadata/1" do + test "Scrapes Wildebeest NodeInfo" do + Tesla.Mock.mock(fn + %{url: "https://wildebeest.example.org/.well-known/nodeinfo"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/wildebeest-well-known-nodeinfo.json") + } + + %{url: "https://wildebeest.example.org/nodeinfo/2.1"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/wildebeest-nodeinfo21.json") + } + end) + + expected = %{ + software_name: "wildebeest", + software_repository: "https://github.com/cloudflare/wildebeest", + software_version: "0.0.1" + } + + assert expected == + Instance.get_or_update_metadata(URI.parse("https://wildebeest.example.org/")) + + expected = %Pleroma.Instances.Instance.Pleroma.Instances.Metadata{ + software_name: "wildebeest", + software_repository: "https://github.com/cloudflare/wildebeest", + software_version: "0.0.1" + } + + assert expected == + Repo.get_by(Pleroma.Instances.Instance, %{host: "wildebeest.example.org"}).metadata + end + + test "Scrapes Mastodon NodeInfo" do + Tesla.Mock.mock(fn + %{url: "https://mastodon.example.org/.well-known/nodeinfo"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/mastodon-well-known-nodeinfo.json") + } + + %{url: "https://mastodon.example.org/nodeinfo/2.0"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/mastodon-nodeinfo20.json") + } + end) + + expected = %{ + software_name: "mastodon", + software_version: "4.1.0" + } + + assert expected == + Instance.get_or_update_metadata(URI.parse("https://mastodon.example.org/")) + end + end + test "delete_users_and_activities/1 deletes remote instance users and activities" do [mario, luigi, _peach, wario] = users = [ diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs index c8ad66ddb..53c9277d6 100644 --- a/test/pleroma/object/fetcher_test.exs +++ b/test/pleroma/object/fetcher_test.exs @@ -9,8 +9,12 @@ defmodule Pleroma.Object.FetcherTest do alias Pleroma.Instances alias Pleroma.Object alias Pleroma.Object.Fetcher + alias Pleroma.Web.ActivityPub.ObjectValidator + + require Pleroma.Constants import Mock + import Pleroma.Factory import Tesla.Mock setup do @@ -284,6 +288,8 @@ defmodule Pleroma.Object.FetcherTest do describe "refetching" do setup do + insert(:user, ap_id: "https://mastodon.social/users/emelie") + object1 = %{ "id" => "https://mastodon.social/1", "actor" => "https://mastodon.social/users/emelie", @@ -293,10 +299,14 @@ defmodule Pleroma.Object.FetcherTest do "bcc" => [], "bto" => [], "cc" => [], - "to" => [], - "summary" => "" + "to" => [Pleroma.Constants.as_public()], + "summary" => "", + "published" => "2023-05-08 23:43:20Z", + "updated" => "2023-05-09 23:43:20Z" } + {:ok, local_object1, _} = ObjectValidator.validate(object1, []) + object2 = %{ "id" => "https://mastodon.social/2", "actor" => "https://mastodon.social/users/emelie", @@ -306,8 +316,10 @@ defmodule Pleroma.Object.FetcherTest do "bcc" => [], "bto" => [], "cc" => [], - "to" => [], + "to" => [Pleroma.Constants.as_public()], "summary" => "", + "published" => "2023-05-08 23:43:20Z", + "updated" => "2023-05-09 23:43:25Z", "formerRepresentations" => %{ "type" => "OrderedCollection", "orderedItems" => [ @@ -319,14 +331,18 @@ defmodule Pleroma.Object.FetcherTest do "bcc" => [], "bto" => [], "cc" => [], - "to" => [], - "summary" => "" + "to" => [Pleroma.Constants.as_public()], + "summary" => "", + "published" => "2023-05-08 23:43:20Z", + "updated" => "2023-05-09 23:43:21Z" } ], "totalItems" => 1 } } + {:ok, local_object2, _} = ObjectValidator.validate(object2, []) + mock(fn %{ method: :get, @@ -335,7 +351,7 @@ defmodule Pleroma.Object.FetcherTest do %Tesla.Env{ status: 200, headers: [{"content-type", "application/activity+json"}], - body: Jason.encode!(object1) + body: Jason.encode!(object1 |> Map.put("updated", "2023-05-09 23:44:20Z")) } %{ @@ -345,7 +361,7 @@ defmodule Pleroma.Object.FetcherTest do %Tesla.Env{ status: 200, headers: [{"content-type", "application/activity+json"}], - body: Jason.encode!(object2) + body: Jason.encode!(object2 |> Map.put("updated", "2023-05-09 23:44:20Z")) } %{ @@ -370,7 +386,7 @@ defmodule Pleroma.Object.FetcherTest do apply(HttpRequestMock, :request, [env]) end) - %{object1: object1, object2: object2} + %{object1: local_object1, object2: local_object2} end test "it keeps formerRepresentations if remote does not have this attr", %{object1: object1} do @@ -388,8 +404,9 @@ defmodule Pleroma.Object.FetcherTest do "bcc" => [], "bto" => [], "cc" => [], - "to" => [], - "summary" => "" + "to" => [Pleroma.Constants.as_public()], + "summary" => "", + "published" => "2023-05-08 23:43:20Z" } ], "totalItems" => 1 @@ -467,6 +484,53 @@ defmodule Pleroma.Object.FetcherTest do } } = refetched.data end + + test "it keeps the history intact if only updated time has changed", + %{object1: object1} do + full_object1 = + object1 + |> Map.merge(%{ + "updated" => "2023-05-08 23:43:47Z", + "formerRepresentations" => %{ + "type" => "OrderedCollection", + "orderedItems" => [ + %{"type" => "Note", "content" => "mew mew 1"} + ], + "totalItems" => 1 + } + }) + + {:ok, o} = Object.create(full_object1) + + assert {:ok, refetched} = Fetcher.refetch_object(o) + + assert %{ + "content" => "test 1", + "formerRepresentations" => %{ + "orderedItems" => [ + %{"content" => "mew mew 1"} + ], + "totalItems" => 1 + } + } = refetched.data + end + + test "it goes through ObjectValidator and MRF", %{object2: object2} do + with_mock Pleroma.Web.ActivityPub.MRF, [:passthrough], + filter: fn + %{"type" => "Note"} = object -> + {:ok, Map.put(object, "content", "MRFd content")} + + arg -> + passthrough([arg]) + end do + {:ok, o} = Object.create(object2) + + assert {:ok, refetched} = Fetcher.refetch_object(o) + + assert %{"content" => "MRFd content"} = refetched.data + end + end end describe "fetch with history" do diff --git a/test/pleroma/upload/filter/only_media_test.exs b/test/pleroma/upload/filter/only_media_test.exs new file mode 100644 index 000000000..75be070a1 --- /dev/null +++ b/test/pleroma/upload/filter/only_media_test.exs @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.OnlyMediaTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Upload + alias Pleroma.Upload.Filter.OnlyMedia + + test "Allows media Content-Type" do + ["audio/mpeg", "image/jpeg", "video/mp4"] + |> Enum.each(fn type -> + upload = %Upload{ + content_type: type + } + + assert {:ok, :noop} = OnlyMedia.filter(upload) + end) + end + + test "Disallows non-media Content-Type" do + ["application/javascript", "application/pdf", "text/html"] + |> Enum.each(fn type -> + upload = %Upload{ + content_type: type + } + + assert {:error, _} = OnlyMedia.filter(upload) + end) + end +end diff --git a/test/pleroma/user/backup_test.exs b/test/pleroma/user/backup_test.exs index 5c9b94000..066bf6ba8 100644 --- a/test/pleroma/user/backup_test.exs +++ b/test/pleroma/user/backup_test.exs @@ -39,7 +39,7 @@ defmodule Pleroma.User.BackupTest do assert_enqueued(worker: BackupWorker, args: args) backup = Backup.get(args["backup_id"]) - assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup + assert %Backup{user_id: ^user_id, processed: false, file_size: 0, state: :pending} = backup end test "it return an error if the export limit is over" do @@ -59,7 +59,30 @@ defmodule Pleroma.User.BackupTest do assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user) assert {:ok, backup} = perform_job(BackupWorker, args) assert backup.file_size > 0 - assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup + assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id, state: :complete} = backup + + delete_job_args = %{"op" => "delete", "backup_id" => backup_id} + + assert_enqueued(worker: BackupWorker, args: delete_job_args) + assert {:ok, backup} = perform_job(BackupWorker, delete_job_args) + refute Backup.get(backup_id) + + email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup) + + assert_email_sent( + to: {user.name, user.email}, + html_body: email.html_body + ) + end + + test "it updates states of the backup" do + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + %{id: user_id} = user = insert(:user) + + assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user) + assert {:ok, backup} = perform_job(BackupWorker, args) + assert backup.file_size > 0 + assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id, state: :complete} = backup delete_job_args = %{"op" => "delete", "backup_id" => backup_id} @@ -148,7 +171,7 @@ defmodule Pleroma.User.BackupTest do Bookmark.create(user.id, status3.id) assert {:ok, backup} = user |> Backup.new() |> Repo.insert() - assert {:ok, path} = Backup.export(backup) + assert {:ok, path} = Backup.export(backup, self()) assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory]) assert {:ok, {'actor.json', json}} = :zip.zip_get('actor.json', zipfile) @@ -230,6 +253,73 @@ defmodule Pleroma.User.BackupTest do File.rm!(path) end + test "it counts the correct number processed" do + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + Enum.map(1..120, fn i -> + {:ok, status} = CommonAPI.post(user, %{status: "status #{i}"}) + CommonAPI.favorite(user, status.id) + Bookmark.create(user.id, status.id) + end) + + assert {:ok, backup} = user |> Backup.new() |> Repo.insert() + {:ok, backup} = Backup.process(backup) + + assert backup.processed_number == 1 + 120 + 120 + 120 + + Backup.delete(backup) + end + + test "it handles errors" do + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + Enum.map(1..120, fn i -> + {:ok, _status} = CommonAPI.post(user, %{status: "status #{i}"}) + end) + + assert {:ok, backup} = user |> Backup.new() |> Repo.insert() + + with_mock Pleroma.Web.ActivityPub.Transmogrifier, + [:passthrough], + prepare_outgoing: fn data -> + object = + data["object"] + |> Pleroma.Object.normalize(fetch: false) + |> Map.get(:data) + + data = data |> Map.put("object", object) + + if String.contains?(data["object"]["content"], "119"), + do: raise(%Postgrex.Error{}), + else: {:ok, data} + end do + {:ok, backup} = Backup.process(backup) + assert backup.processed + assert backup.state == :complete + assert backup.processed_number == 1 + 119 + + Backup.delete(backup) + end + end + + test "it handles unrecoverable exceptions" do + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + assert {:ok, backup} = user |> Backup.new() |> Repo.insert() + + with_mock Backup, [:passthrough], do_process: fn _, _ -> raise "mock exception" end do + {:error, %{backup: backup, reason: :exit}} = Backup.process(backup) + + assert backup.state == :failed + end + + with_mock Backup, [:passthrough], do_process: fn _, _ -> Process.sleep(:timer.seconds(32)) end do + {:error, %{backup: backup, reason: :timeout}} = Backup.process(backup) + + assert backup.state == :failed + end + end + describe "it uploads and deletes a backup archive" do setup do clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") @@ -246,7 +336,7 @@ defmodule Pleroma.User.BackupTest do Bookmark.create(user.id, status3.id) assert {:ok, backup} = user |> Backup.new() |> Repo.insert() - assert {:ok, path} = Backup.export(backup) + assert {:ok, path} = Backup.export(backup, self()) [path: path, backup: backup] end diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index c16312a65..7f60b959a 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -1844,7 +1844,6 @@ defmodule Pleroma.UserTest do confirmation_token: "qqqq", domain_blocks: ["lain.com"], is_active: false, - ap_enabled: true, is_moderator: true, is_admin: true, mascot: %{"a" => "b"}, @@ -1885,7 +1884,6 @@ defmodule Pleroma.UserTest do confirmation_token: nil, domain_blocks: [], is_active: false, - ap_enabled: false, is_moderator: false, is_admin: false, mascot: nil, @@ -2473,8 +2471,7 @@ defmodule Pleroma.UserTest do insert(:user, local: false, follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following", - ap_enabled: true + following_address: "http://localhost:4001/users/masto_closed/following" ) assert other_user.following_count == 0 @@ -2495,8 +2492,7 @@ defmodule Pleroma.UserTest do insert(:user, local: false, follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following", - ap_enabled: true + following_address: "http://localhost:4001/users/masto_closed/following" ) assert other_user.following_count == 0 @@ -2517,8 +2513,7 @@ defmodule Pleroma.UserTest do insert(:user, local: false, follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following", - ap_enabled: true + following_address: "http://localhost:4001/users/masto_closed/following" ) assert other_user.following_count == 0 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 ef91066c1..62eb9b5a3 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -575,7 +575,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do user = insert(:user, ap_id: "https://mastodon.example.org/users/raymoo", - ap_enabled: true, local: false, last_refreshed_at: nil ) diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index a6f8b6152..1e8c14043 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -174,7 +174,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) assert user.ap_id == user_id assert user.nickname == "admin@mastodon.example.org" - assert user.ap_enabled assert user.follower_address == "http://mastodon.example.org/users/admin/followers" end @@ -2653,4 +2652,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew") assert user.name == " " end + + test "pin_data_from_featured_collection will ignore unsupported values" do + assert %{} == + ActivityPub.pin_data_from_featured_collection(%{ + "type" => "OrderedCollection", + "first" => "https://social.example/users/alice/collections/featured?page=true" + }) + end end diff --git a/test/pleroma/web/activity_pub/mrf/emoji_policy_test.exs b/test/pleroma/web/activity_pub/mrf/emoji_policy_test.exs new file mode 100644 index 000000000..7350800f0 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/emoji_policy_test.exs @@ -0,0 +1,425 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicyTest do + use Pleroma.DataCase + + require Pleroma.Constants + + alias Pleroma.Web.ActivityPub.MRF + alias Pleroma.Web.ActivityPub.MRF.EmojiPolicy + + setup do: clear_config(:mrf_emoji) + + setup do + clear_config([:mrf_emoji], %{ + remove_url: [], + remove_shortcode: [], + federated_timeline_removal_url: [], + federated_timeline_removal_shortcode: [] + }) + end + + @emoji_tags [ + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://example.org/emoji/biribiri/mikoto_smile2.png" + }, + "id" => "https://example.org/emoji/biribiri/mikoto_smile2.png", + "name" => ":mikoto_smile2:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + }, + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://example.org/emoji/biribiri/mikoto_smile3.png" + }, + "id" => "https://example.org/emoji/biribiri/mikoto_smile3.png", + "name" => ":mikoto_smile3:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + }, + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + }, + "id" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png", + "name" => ":nekomimi_girl_emoji_007:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + }, + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://example.org/test.png" + }, + "id" => "https://example.org/test.png", + "name" => ":test:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + + @misc_tags [%{"type" => "Placeholder"}] + + @user_data %{ + "type" => "Person", + "id" => "https://example.org/placeholder", + "name" => "lol", + "tag" => @emoji_tags ++ @misc_tags + } + + @status_data %{ + "type" => "Create", + "object" => %{ + "type" => "Note", + "id" => "https://example.org/placeholder", + "content" => "lol", + "tag" => @emoji_tags ++ @misc_tags, + "emoji" => %{ + "mikoto_smile2" => "https://example.org/emoji/biribiri/mikoto_smile2.png", + "mikoto_smile3" => "https://example.org/emoji/biribiri/mikoto_smile3.png", + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png", + "test" => "https://example.org/test.png" + }, + "to" => ["https://example.org/self", Pleroma.Constants.as_public()], + "cc" => ["https://example.org/someone"] + }, + "to" => ["https://example.org/self", Pleroma.Constants.as_public()], + "cc" => ["https://example.org/someone"] + } + + @status_data_with_history %{ + "type" => "Create", + "object" => + @status_data["object"] + |> Map.merge(%{ + "formerRepresentations" => %{ + "type" => "OrderedCollection", + "orderedItems" => [@status_data["object"] |> Map.put("content", "older")], + "totalItems" => 1 + } + }), + "to" => ["https://example.org/self", Pleroma.Constants.as_public()], + "cc" => ["https://example.org/someone"] + } + + @emoji_react_data %{ + "type" => "EmojiReact", + "tag" => [@emoji_tags |> Enum.at(3)], + "object" => "https://example.org/someobject", + "to" => ["https://example.org/self"], + "cc" => ["https://example.org/someone"] + } + + @emoji_react_data_matching_regex %{ + "type" => "EmojiReact", + "tag" => [@emoji_tags |> Enum.at(1)], + "object" => "https://example.org/someobject", + "to" => ["https://example.org/self"], + "cc" => ["https://example.org/someone"] + } + + @emoji_react_data_matching_nothing %{ + "type" => "EmojiReact", + "tag" => [@emoji_tags |> Enum.at(2)], + "object" => "https://example.org/someobject", + "to" => ["https://example.org/self"], + "cc" => ["https://example.org/someone"] + } + + @emoji_react_data_unicode %{ + "type" => "EmojiReact", + "content" => "😍", + "object" => "https://example.org/someobject", + "to" => ["https://example.org/self"], + "cc" => ["https://example.org/someone"] + } + + describe "remove_url" do + setup do + clear_config([:mrf_emoji, :remove_url], [ + "https://example.org/test.png", + ~r{/biribiri/mikoto_smile[23]\.png}, + "nekomimi_girl_emoji" + ]) + + :ok + end + + test "processes user" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + assert %{"tag" => ^expected_tags} = filtered + end + + test "processes status" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + expected_emoji = %{ + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + } + + assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered + end + + test "processes status with history" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data_with_history) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + expected_emoji = %{ + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + } + + assert %{ + "object" => %{ + "tag" => ^expected_tags, + "emoji" => ^expected_emoji, + "formerRepresentations" => %{"orderedItems" => [item]} + } + } = filtered + + assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item + end + + test "processes updates" do + {:ok, filtered} = + MRF.filter_one(EmojiPolicy, @status_data_with_history |> Map.put("type", "Update")) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + expected_emoji = %{ + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + } + + assert %{ + "object" => %{ + "tag" => ^expected_tags, + "emoji" => ^expected_emoji, + "formerRepresentations" => %{"orderedItems" => [item]} + } + } = filtered + + assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item + end + + test "processes EmojiReact" do + assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} == + MRF.filter_one(EmojiPolicy, @emoji_react_data) + + assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} == + MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_regex) + + assert {:ok, @emoji_react_data_matching_nothing} == + MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_nothing) + + assert {:ok, @emoji_react_data_unicode} == + MRF.filter_one(EmojiPolicy, @emoji_react_data_unicode) + end + end + + describe "remove_shortcode" do + setup do + clear_config([:mrf_emoji, :remove_shortcode], [ + "test", + ~r{mikoto_s}, + "nekomimi_girl_emoji" + ]) + + :ok + end + + test "processes user" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + assert %{"tag" => ^expected_tags} = filtered + end + + test "processes status" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + expected_emoji = %{ + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + } + + assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered + end + + test "processes status with history" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data_with_history) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + expected_emoji = %{ + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + } + + assert %{ + "object" => %{ + "tag" => ^expected_tags, + "emoji" => ^expected_emoji, + "formerRepresentations" => %{"orderedItems" => [item]} + } + } = filtered + + assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item + end + + test "processes updates" do + {:ok, filtered} = + MRF.filter_one(EmojiPolicy, @status_data_with_history |> Map.put("type", "Update")) + + expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags + + expected_emoji = %{ + "nekomimi_girl_emoji_007" => + "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png" + } + + assert %{ + "object" => %{ + "tag" => ^expected_tags, + "emoji" => ^expected_emoji, + "formerRepresentations" => %{"orderedItems" => [item]} + } + } = filtered + + assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item + end + + test "processes EmojiReact" do + assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} == + MRF.filter_one(EmojiPolicy, @emoji_react_data) + + assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} == + MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_regex) + + assert {:ok, @emoji_react_data_matching_nothing} == + MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_nothing) + + assert {:ok, @emoji_react_data_unicode} == + MRF.filter_one(EmojiPolicy, @emoji_react_data_unicode) + end + end + + describe "federated_timeline_removal_url" do + setup do + clear_config([:mrf_emoji, :federated_timeline_removal_url], [ + "https://example.org/test.png", + ~r{/biribiri/mikoto_smile[23]\.png}, + "nekomimi_girl_emoji" + ]) + + :ok + end + + test "processes status" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data) + + expected_tags = @status_data["object"]["tag"] + expected_emoji = @status_data["object"]["emoji"] + + expected_to = ["https://example.org/self"] + expected_cc = [Pleroma.Constants.as_public(), "https://example.org/someone"] + + assert %{ + "to" => ^expected_to, + "cc" => ^expected_cc, + "object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} + } = filtered + end + + test "ignore updates" do + {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data |> Map.put("type", "Update")) + + expected_tags = @status_data["object"]["tag"] + expected_emoji = @status_data["object"]["emoji"] + + expected_to = ["https://example.org/self", Pleroma.Constants.as_public()] + expected_cc = ["https://example.org/someone"] + + assert %{ + "to" => ^expected_to, + "cc" => ^expected_cc, + "object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} + } = filtered + end + + test "processes status with history" do + status = + @status_data_with_history + |> put_in(["object", "tag"], @misc_tags) + |> put_in(["object", "emoji"], %{}) + + {:ok, filtered} = MRF.filter_one(EmojiPolicy, status) + + expected_tags = @status_data["object"]["tag"] + expected_emoji = @status_data["object"]["emoji"] + + expected_to = ["https://example.org/self"] + expected_cc = [Pleroma.Constants.as_public(), "https://example.org/someone"] + + assert %{ + "to" => ^expected_to, + "cc" => ^expected_cc, + "object" => %{ + "formerRepresentations" => %{ + "orderedItems" => [%{"tag" => ^expected_tags, "emoji" => ^expected_emoji}] + } + } + } = filtered + end + end + + describe "edge cases" do + setup do + clear_config([:mrf_emoji, :remove_url], [ + "https://example.org/test.png", + ~r{/biribiri/mikoto_smile[23]\.png}, + "nekomimi_girl_emoji" + ]) + + :ok + end + + test "non-statuses" do + answer = @status_data |> put_in(["object", "type"], "Answer") + {:ok, filtered} = MRF.filter_one(EmojiPolicy, answer) + + assert filtered == answer + end + + test "without tag" do + status = @status_data |> Map.put("object", Map.drop(@status_data["object"], ["tag"])) + {:ok, filtered} = MRF.filter_one(EmojiPolicy, status) + + refute Map.has_key?(filtered["object"], "tag") + end + + test "without emoji" do + status = @status_data |> Map.put("object", Map.drop(@status_data["object"], ["emoji"])) + {:ok, filtered} = MRF.filter_one(EmojiPolicy, status) + + refute Map.has_key?(filtered["object"], "emoji") + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/force_mentions_in_content_test.exs b/test/pleroma/web/activity_pub/mrf/force_mentions_in_content_test.exs index b349a4bb7..811ef105c 100644 --- a/test/pleroma/web/activity_pub/mrf/force_mentions_in_content_test.exs +++ b/test/pleroma/web/activity_pub/mrf/force_mentions_in_content_test.exs @@ -256,4 +256,55 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContentTest do } }} = MRF.filter_one(ForceMentionsInContent, activity) end + + test "don't add duplicate mentions for mastodon or misskey posts" do + [zero, rogerick, greg] = [ + insert(:user, + ap_id: "https://pleroma.example.com/users/zero", + uri: "https://pleroma.example.com/users/zero", + nickname: "zero@pleroma.example.com", + local: false + ), + insert(:user, + ap_id: "https://misskey.example.com/users/104ab42f11", + uri: "https://misskey.example.com/@rogerick", + nickname: "rogerick@misskey.example.com", + local: false + ), + insert(:user, + ap_id: "https://mastodon.example.com/users/greg", + uri: "https://mastodon.example.com/@greg", + nickname: "greg@mastodon.example.com", + local: false + ) + ] + + {:ok, post} = CommonAPI.post(rogerick, %{status: "eugh"}) + + inline_mentions = [ + "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{rogerick.id}\" href=\"#{rogerick.ap_id}\" rel=\"ugc\">@<span>rogerick</span></a></span>", + "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{greg.id}\" href=\"#{greg.uri}\" rel=\"ugc\">@<span>greg</span></a></span>" + ] + + activity = %{ + "type" => "Create", + "actor" => zero.ap_id, + "object" => %{ + "type" => "Note", + "actor" => zero.ap_id, + "content" => "#{Enum.at(inline_mentions, 0)} #{Enum.at(inline_mentions, 1)} erm", + "to" => [ + rogerick.ap_id, + greg.ap_id, + Constants.as_public() + ], + "inReplyTo" => Object.normalize(post).data["id"] + } + } + + {:ok, %{"object" => %{"content" => filtered}}} = ForceMentionsInContent.filter(activity) + + assert filtered == + "#{Enum.at(inline_mentions, 0)} #{Enum.at(inline_mentions, 1)} erm" + end end diff --git a/test/pleroma/web/activity_pub/mrf/utils_test.exs b/test/pleroma/web/activity_pub/mrf/utils_test.exs new file mode 100644 index 000000000..3bbc2cfd3 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/utils_test.exs @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.UtilsTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Web.ActivityPub.MRF.Utils + + describe "describe_regex_or_string/1" do + test "describes regex" do + assert "~r/foo/i" == Utils.describe_regex_or_string(~r/foo/i) + end + + test "returns string as-is" do + assert "foo" == Utils.describe_regex_or_string("foo") + end + end +end diff --git a/test/pleroma/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs index e2db3d575..c5137cbb7 100644 --- a/test/pleroma/web/activity_pub/publisher_test.exs +++ b/test/pleroma/web/activity_pub/publisher_test.exs @@ -276,8 +276,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do follower = insert(:user, %{ local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true + inbox: "https://domain.com/users/nick1/inbox" }) actor = insert(:user, follower_address: follower.ap_id) @@ -313,8 +312,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do follower = insert(:user, %{ local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true + inbox: "https://domain.com/users/nick1/inbox" }) actor = insert(:user, follower_address: follower.ap_id) @@ -348,8 +346,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do follower = insert(:user, %{ local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true + inbox: "https://domain.com/users/nick1/inbox" }) actor = insert(:user, follower_address: follower.ap_id) @@ -382,15 +379,13 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do fetcher = insert(:user, local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true + inbox: "https://domain.com/users/nick1/inbox" ) another_fetcher = insert(:user, local: false, - inbox: "https://domain2.com/users/nick1/inbox", - ap_enabled: true + inbox: "https://domain2.com/users/nick1/inbox" ) actor = insert(:user) diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 6b4636d22..3e0c8dc65 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do alias Pleroma.Activity alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils @@ -123,6 +122,20 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert activity.data["context"] == object.data["context"] end + + test "it drops link tags" do + insert(:user, ap_id: "https://example.org/users/alice") + + message = File.read!("test/fixtures/fep-e232.json") |> Jason.decode!() + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + object = Object.normalize(activity) + assert length(object.data["tag"]) == 1 + + tag = object.data["tag"] |> List.first() + assert tag["type"] == "Mention" + end end describe "prepare outgoing" do @@ -339,69 +352,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do end end - describe "user upgrade" do - test "it upgrades a user to activitypub" do - user = - insert(:user, %{ - nickname: "rye@niu.moe", - local: false, - ap_id: "https://niu.moe/users/rye", - follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) - }) - - user_two = insert(:user) - Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) - - {:ok, activity} = CommonAPI.post(user, %{status: "test"}) - {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"}) - assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients - - user = User.get_cached_by_id(user.id) - assert user.note_count == 1 - - {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye") - ObanHelpers.perform_all() - - assert user.ap_enabled - assert user.note_count == 1 - assert user.follower_address == "https://niu.moe/users/rye/followers" - assert user.following_address == "https://niu.moe/users/rye/following" - - user = User.get_cached_by_id(user.id) - assert user.note_count == 1 - - activity = Activity.get_by_id(activity.id) - assert user.follower_address in activity.recipients - - assert %{ - "url" => [ - %{ - "href" => - "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" - } - ] - } = user.avatar - - assert %{ - "url" => [ - %{ - "href" => - "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" - } - ] - } = user.banner - - refute "..." in activity.recipients - - unrelated_activity = Activity.get_by_id(unrelated_activity.id) - refute user.follower_address in unrelated_activity.recipients - - user_two = User.get_cached_by_id(user_two.id) - assert User.following?(user_two, user) - refute "..." in User.following(user_two) - end - end - describe "actor rewriting" do test "it fixes the actor URL property to be a proper URI" do data = %{ diff --git a/test/pleroma/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs index c141cf69d..fb2579a3d 100644 --- a/test/pleroma/web/admin_api/controllers/report_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/report_controller_test.exs @@ -123,6 +123,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do }) %{ + reporter: reporter, id: report_id, second_report_id: second_report_id } @@ -266,6 +267,26 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do assert ModerationLog.get_log_entry_message(second_log_entry) == "@#{admin.nickname} updated report ##{second_report_id} (on user @#{second_activity.user_actor.nickname}) with 'closed' state" end + + test "works if reporter is deactivated", %{ + conn: conn, + id: id, + reporter: reporter + } do + Pleroma.User.set_activation(reporter, false) + + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "resolved", "id" => id} + ] + }) + |> json_response_and_validate_schema(:no_content) + + activity = Activity.get_by_id_with_user_actor(id) + assert activity.data["state"] == "resolved" + end end describe "GET /api/pleroma/admin/reports" do diff --git a/test/pleroma/web/api_spec/scopes/compiler_test.exs b/test/pleroma/web/api_spec/scopes/compiler_test.exs new file mode 100644 index 000000000..99e1d343a --- /dev/null +++ b/test/pleroma/web/api_spec/scopes/compiler_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Scopes.CompilerTest do + use ExUnit.Case, async: true + + alias Pleroma.Web.ApiSpec.Scopes.Compiler + + @dummy_response %{} + + @data %{ + paths: %{ + "/mew" => %OpenApiSpex.PathItem{ + post: %OpenApiSpex.Operation{ + security: [%{"oAuth" => ["a:b:c"]}], + responses: @dummy_response + }, + get: %OpenApiSpex.Operation{security: nil, responses: @dummy_response} + }, + "/mew2" => %OpenApiSpex.PathItem{ + post: %OpenApiSpex.Operation{ + security: [%{"oAuth" => ["d:e", "f:g"]}], + responses: @dummy_response + }, + get: %OpenApiSpex.Operation{security: nil, responses: @dummy_response} + } + } + } + + describe "process_scope/1" do + test "gives all higher-level scopes" do + scopes = Compiler.process_scope("admin:read:accounts") + + assert [_, _, _] = scopes + assert "admin" in scopes + assert "admin:read" in scopes + assert "admin:read:accounts" in scopes + end + end + + describe "extract_all_scopes_from/1" do + test "extracts scopes" do + scopes = Compiler.extract_all_scopes_from(@data) + + assert [_, _, _, _, _, _, _] = scopes + assert "a" in scopes + assert "a:b" in scopes + assert "a:b:c" in scopes + assert "d" in scopes + assert "d:e" in scopes + assert "f" in scopes + assert "f:g" in scopes + end + end +end diff --git a/test/pleroma/web/common_api/utils_test.exs b/test/pleroma/web/common_api/utils_test.exs index d309c6ded..ca5b92683 100644 --- a/test/pleroma/web/common_api/utils_test.exs +++ b/test/pleroma/web/common_api/utils_test.exs @@ -592,7 +592,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do end test "returns list attachments with desc" do - object = insert(:note) + object = insert(:attachment) desc = Jason.encode!(%{object.id => "test-desc"}) assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [ @@ -603,7 +603,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do describe "attachments_from_ids/1" do test "returns attachments with descs" do - object = insert(:note) + object = insert(:attachment) desc = Jason.encode!(%{object.id => "test-desc"}) assert Utils.attachments_from_ids(%{ @@ -615,13 +615,18 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do end test "returns attachments without descs" do - object = insert(:note) + object = insert(:attachment) assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data] end test "returns [] when not pass media_ids" do assert Utils.attachments_from_ids(%{}) == [] end + + test "checks that the object is of upload type" do + object = insert(:note) + assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [] + end end describe "maybe_add_list_data/3" do diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index 5c9103e9f..968e11a14 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -393,6 +393,20 @@ defmodule Pleroma.Web.CommonAPITest do refute Activity.get_by_id(post.id) end + + test "it allows privileged users to delete banned user's posts" do + clear_config([:instance, :moderator_privileges], [:messages_delete]) + user = insert(:user) + moderator = insert(:user, is_moderator: true) + + {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) + User.set_activation(user, false) + + assert {:ok, delete} = CommonAPI.delete(post.id, moderator) + assert delete.local + + refute Activity.get_by_id(post.id) + end end test "favoriting race condition" do @@ -527,6 +541,17 @@ defmodule Pleroma.Web.CommonAPITest do assert Object.tags(object) == ["ساٴينس"] end + test "allows lang attribute" do + user = insert(:user) + text = ~s{<span lang="en">something</span><p lang="diaetuitech_rpyhpgc">random</p>} + + {:ok, activity} = CommonAPI.post(user, %{status: text, content_type: "text/html"}) + + object = Object.normalize(activity, fetch: false) + + assert object.data["content"] == text + end + test "double dot in link is allowed" do user = insert(:user) text = "https://example.to/something..mp3" @@ -1328,7 +1353,7 @@ defmodule Pleroma.Web.CommonAPITest do test "cancels a pending follow for a remote user" do follower = insert(:user) - followed = insert(:user, is_locked: true, local: false, ap_enabled: true) + followed = insert(:user, is_locked: true, local: false) assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) diff --git a/test/pleroma/web/federator_test.exs b/test/pleroma/web/federator_test.exs index 1ffe6aae1..6826e6c2f 100644 --- a/test/pleroma/web/federator_test.exs +++ b/test/pleroma/web/federator_test.exs @@ -78,16 +78,14 @@ defmodule Pleroma.Web.FederatorTest do local: false, nickname: "nick1@domain.com", ap_id: "https://domain.com/users/nick1", - inbox: inbox1, - ap_enabled: true + inbox: inbox1 }) insert(:user, %{ local: false, nickname: "nick2@domain2.com", ap_id: "https://domain2.com/users/nick2", - inbox: inbox2, - ap_enabled: true + inbox: inbox2 }) dt = NaiveDateTime.utc_now() diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index 1e8979127..76c289ee7 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -771,6 +771,49 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, local: local, remote: remote} end + defp local_and_remote_context_activities do + local_user_1 = insert(:user) + local_user_2 = insert(:user) + remote_user = insert(:user, local: false) + + {:ok, %{id: id1, data: %{"context" => context}}} = + CommonAPI.post(local_user_1, %{status: "post"}) + + {:ok, %{id: id2} = post} = + CommonAPI.post(local_user_2, %{status: "local reply", in_reply_to_status_id: id1}) + + params = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => remote_user.ap_id, + "type" => "Create", + "context" => context, + "id" => "#{remote_user.ap_id}/activities/1", + "inReplyTo" => post.data["id"], + "object" => %{ + "type" => "Note", + "content" => "remote reply", + "context" => context, + "id" => "#{remote_user.ap_id}/objects/1", + "attributedTo" => remote_user.ap_id, + "to" => [ + local_user_1.ap_id, + local_user_2.ap_id, + "https://www.w3.org/ns/activitystreams#Public" + ] + }, + "to" => [ + local_user_1.ap_id, + local_user_2.ap_id, + "https://www.w3.org/ns/activitystreams#Public" + ] + } + + {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) + {:ok, remote_activity} = ObanHelpers.perform(job) + + %{locals: [id1, id2], remote: remote_activity.id, context: context} + end + describe "status with restrict unauthenticated activities for local and remote" do setup do: local_and_remote_activities() @@ -957,6 +1000,230 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end end + describe "getting status contexts restricted unauthenticated for local and remote" do + setup do: local_and_remote_context_activities() + + setup do: clear_config([:restrict_unauthenticated, :activities, :local], true) + + setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) + + test "if user is unauthenticated", %{conn: conn, locals: [post_id, _]} do + res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") + + assert json_response_and_validate_schema(res_conn, 200) == %{ + "ancestors" => [], + "descendants" => [] + } + end + + test "if user is unauthenticated reply", %{conn: conn, locals: [_, reply_id]} do + res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context") + + assert json_response_and_validate_schema(res_conn, 200) == %{ + "ancestors" => [], + "descendants" => [] + } + end + + test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do + %{conn: conn} = oauth_access(["read"]) + res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") + + %{"ancestors" => [], "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert reply_id in descendant_ids + assert remote_reply_id in descendant_ids + end + + test "if user is authenticated reply", %{locals: [post_id, reply_id], remote: remote_reply_id} do + %{conn: conn} = oauth_access(["read"]) + res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context") + + %{"ancestors" => ancestors, "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + ancestor_ids = + ancestors + |> Enum.map(& &1["id"]) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert post_id in ancestor_ids + assert remote_reply_id in descendant_ids + end + end + + describe "getting status contexts restricted unauthenticated for local" do + setup do: local_and_remote_context_activities() + + setup do: clear_config([:restrict_unauthenticated, :activities, :local], true) + + setup do: clear_config([:restrict_unauthenticated, :activities, :remote], false) + + test "if user is unauthenticated", %{ + conn: conn, + locals: [post_id, reply_id], + remote: remote_reply_id + } do + res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") + + %{"ancestors" => [], "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert reply_id not in descendant_ids + assert remote_reply_id in descendant_ids + end + + test "if user is unauthenticated reply", %{ + conn: conn, + locals: [post_id, reply_id], + remote: remote_reply_id + } do + res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context") + + %{"ancestors" => ancestors, "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + ancestor_ids = + ancestors + |> Enum.map(& &1["id"]) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert post_id not in ancestor_ids + assert remote_reply_id in descendant_ids + end + + test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do + %{conn: conn} = oauth_access(["read"]) + res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") + + %{"ancestors" => [], "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert reply_id in descendant_ids + assert remote_reply_id in descendant_ids + end + + test "if user is authenticated reply", %{locals: [post_id, reply_id], remote: remote_reply_id} do + %{conn: conn} = oauth_access(["read"]) + res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context") + + %{"ancestors" => ancestors, "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + ancestor_ids = + ancestors + |> Enum.map(& &1["id"]) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert post_id in ancestor_ids + assert remote_reply_id in descendant_ids + end + end + + describe "getting status contexts restricted unauthenticated for remote" do + setup do: local_and_remote_context_activities() + + setup do: clear_config([:restrict_unauthenticated, :activities, :local], false) + + setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) + + test "if user is unauthenticated", %{ + conn: conn, + locals: [post_id, reply_id], + remote: remote_reply_id + } do + res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") + + %{"ancestors" => [], "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert reply_id in descendant_ids + assert remote_reply_id not in descendant_ids + end + + test "if user is unauthenticated reply", %{ + conn: conn, + locals: [post_id, reply_id], + remote: remote_reply_id + } do + res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context") + + %{"ancestors" => ancestors, "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + ancestor_ids = + ancestors + |> Enum.map(& &1["id"]) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert post_id in ancestor_ids + assert remote_reply_id not in descendant_ids + end + + test "if user is authenticated", %{locals: [post_id, reply_id], remote: remote_reply_id} do + %{conn: conn} = oauth_access(["read"]) + res_conn = get(conn, "/api/v1/statuses/#{post_id}/context") + + %{"ancestors" => [], "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + reply_ids = + descendants + |> Enum.map(& &1["id"]) + + assert reply_id in reply_ids + assert remote_reply_id in reply_ids + end + + test "if user is authenticated reply", %{locals: [post_id, reply_id], remote: remote_reply_id} do + %{conn: conn} = oauth_access(["read"]) + res_conn = get(conn, "/api/v1/statuses/#{reply_id}/context") + + %{"ancestors" => ancestors, "descendants" => descendants} = + json_response_and_validate_schema(res_conn, 200) + + ancestor_ids = + ancestors + |> Enum.map(& &1["id"]) + + descendant_ids = + descendants + |> Enum.map(& &1["id"]) + + assert post_id in ancestor_ids + assert remote_reply_id in descendant_ids + end + end + describe "deleting a status" do test "when you created it" do %{user: author, conn: conn} = oauth_access(["write:statuses"]) @@ -1018,6 +1285,27 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do refute Activity.get_by_id(activity.id) end + + test "when you're privileged and the user is banned", %{conn: conn} do + clear_config([:instance, :moderator_privileges], [:messages_delete]) + posting_user = insert(:user, is_active: false) + refute posting_user.is_active + activity = insert(:note_activity, user: posting_user) + user = insert(:user, is_moderator: true) + + res_conn = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:statuses"])) + |> delete("/api/v1/statuses/#{activity.id}") + + assert %{} = json_response_and_validate_schema(res_conn, 200) + + assert ModerationLog |> Repo.one() |> ModerationLog.get_log_entry_message() == + "@#{user.nickname} deleted status ##{activity.id}" + + refute Activity.get_by_id(activity.id) + end end describe "reblogging" do diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index 6c63d53c2..45412bb34 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -97,6 +97,42 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do assert user.raw_bio == raw_bio end + test "updating bio honours bio limit", %{conn: conn} do + bio_limit = Config.get([:instance, :user_bio_length], 5000) + + raw_bio = String.duplicate(".", bio_limit + 1) + + conn = patch(conn, "/api/v1/accounts/update_credentials", %{"note" => raw_bio}) + + assert %{"error" => "Bio is too long"} = json_response_and_validate_schema(conn, 413) + end + + test "updating name honours name limit", %{conn: conn} do + name_limit = Config.get([:instance, :user_name_length], 100) + + name = String.duplicate(".", name_limit + 1) + + conn = patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => name}) + + assert %{"error" => "Name is too long"} = json_response_and_validate_schema(conn, 413) + end + + test "when both name and bio exceeds the limit, display name error", %{conn: conn} do + name_limit = Config.get([:instance, :user_name_length], 100) + bio_limit = Config.get([:instance, :user_bio_length], 5000) + + name = String.duplicate(".", name_limit + 1) + raw_bio = String.duplicate(".", bio_limit + 1) + + conn = + patch(conn, "/api/v1/accounts/update_credentials", %{ + "display_name" => name, + "note" => raw_bio + }) + + assert %{"error" => "Name is too long"} = json_response_and_validate_schema(conn, 413) + end + test "updates the user's locking status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"}) @@ -595,17 +631,17 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do fields = [%{"name" => "foo", "value" => long_value}] - assert %{"error" => "Invalid request"} == + assert %{"error" => "One or more field entries are too long"} == conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response_and_validate_schema(403) + |> json_response_and_validate_schema(413) fields = [%{"name" => long_name, "value" => "bar"}] - assert %{"error" => "Invalid request"} == + assert %{"error" => "One or more field entries are too long"} == conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response_and_validate_schema(403) + |> json_response_and_validate_schema(413) clear_config([:instance, :max_account_fields], 1) @@ -614,10 +650,10 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do %{"name" => "link", "value" => "cofe.io"} ] - assert %{"error" => "Invalid request"} == + assert %{"error" => "Too many field entries"} == conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response_and_validate_schema(403) + |> json_response_and_validate_schema(413) end end diff --git a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs index 5246bf0c4..9ce092fd8 100644 --- a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs +++ b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs @@ -6,7 +6,9 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do use Pleroma.Web.ConnCase import Mock + import Mox + alias Pleroma.ReverseProxy.ClientMock alias Pleroma.Web.MediaProxy alias Plug.Conn @@ -74,6 +76,20 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url) end end + + test "it applies sandbox CSP to MediaProxy requests", %{conn: conn} do + media_url = "https://lain.com/image.png" + media_proxy_url = MediaProxy.encode_url(media_url) + + ClientMock + |> expect(:request, fn :get, ^media_url, _, _, _ -> + {:ok, 200, [{"content-type", "image/png"}]} + end) + + %Conn{resp_headers: headers} = get(conn, media_proxy_url) + + assert {"content-security-policy", "sandbox;"} in headers + end end describe "Media Preview Proxy" do diff --git a/test/pleroma/web/metadata/providers/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs index be4cfbe7b..f8d01c5c8 100644 --- a/test/pleroma/web/metadata/providers/twitter_card_test.exs +++ b/test/pleroma/web/metadata/providers/twitter_card_test.exs @@ -182,7 +182,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do {:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []}, {:meta, [name: "twitter:description", content: "pleroma in a nutshell"], []}, {:meta, [name: "twitter:card", content: "summary_large_image"], []}, - {:meta, [name: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []}, + {:meta, [name: "twitter:image", content: "https://pleroma.gov/tenshi.png"], []}, + {:meta, [name: "twitter:image:alt", content: ""], []}, {:meta, [name: "twitter:player:width", content: "1280"], []}, {:meta, [name: "twitter:player:height", content: "1024"], []}, {:meta, [name: "twitter:card", content: "player"], []}, diff --git a/test/pleroma/web/pleroma_api/views/backup_view_test.exs b/test/pleroma/web/pleroma_api/views/backup_view_test.exs index a86688bc4..6908463d6 100644 --- a/test/pleroma/web/pleroma_api/views/backup_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/backup_view_test.exs @@ -15,4 +15,43 @@ defmodule Pleroma.Web.PleromaAPI.BackupViewTest do result = BackupView.render("show.json", backup: backup) assert result.id == backup.id end + + test "it renders the state and processed_number" do + user = insert(:user) + backup = Backup.new(user) + + result = BackupView.render("show.json", backup: backup) + assert result.state == to_string(backup.state) + assert result.processed_number == backup.processed_number + end + + test "it renders failed state with legacy records" do + backup = %Backup{ + id: 0, + content_type: "application/zip", + file_name: "dummy", + file_size: 1, + state: :invalid, + processed: true, + processed_number: 1, + inserted_at: NaiveDateTime.utc_now() + } + + result = BackupView.render("show.json", backup: backup) + assert result.state == "complete" + + backup = %Backup{ + id: 0, + content_type: "application/zip", + file_name: "dummy", + file_size: 1, + state: :invalid, + processed: false, + processed_number: 1, + inserted_at: NaiveDateTime.utc_now() + } + + result = BackupView.render("show.json", backup: backup) + assert result.state == "failed" + end end diff --git a/test/pleroma/web/rich_media/parser_test.exs b/test/pleroma/web/rich_media/parser_test.exs index ffdc4e5d7..9064138a6 100644 --- a/test/pleroma/web/rich_media/parser_test.exs +++ b/test/pleroma/web/rich_media/parser_test.exs @@ -129,7 +129,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do }} end - test "parses OEmbed" do + test "parses OEmbed and filters HTML tags" do assert Parser.parse("http://example.com/oembed") == {:ok, %{ @@ -139,7 +139,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do "flickr_type" => "photo", "height" => "768", "html" => - "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by \u202E\u202D\u202Cbees\u202C, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>", + "<a href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by \u202E\u202D\u202Cbees\u202C, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"/></a>", "license" => "All Rights Reserved", "license_id" => 0, "provider_name" => "Flickr", diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs index 8b0c84164..7ab0e379b 100644 --- a/test/pleroma/web/streamer_test.exs +++ b/test/pleroma/web/streamer_test.exs @@ -29,6 +29,26 @@ defmodule Pleroma.Web.StreamerTest do assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil) end + test "rejects local public streams if restricted_unauthenticated is on" do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + + assert {:error, :unauthorized} = Streamer.get_topic("public:local", nil, nil) + assert {:error, :unauthorized} = Streamer.get_topic("public:local:media", nil, nil) + end + + test "rejects remote public streams if restricted_unauthenticated is on" do + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + assert {:error, :unauthorized} = Streamer.get_topic("public", nil, nil) + assert {:error, :unauthorized} = Streamer.get_topic("public:media", nil, nil) + + assert {:error, :unauthorized} = + Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"}) + + assert {:error, :unauthorized} = + Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"}) + end + test "allows instance streams" do assert {:ok, "public:remote:lain.com"} = Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"}) @@ -69,6 +89,63 @@ defmodule Pleroma.Web.StreamerTest do end end + test "allows local public streams if restricted_unauthenticated is on", %{ + user: user, + token: oauth_token + } do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + + %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) + %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) + + assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token) + + assert {:ok, "public:local:media"} = + Streamer.get_topic("public:local:media", user, oauth_token) + + for token <- [read_notifications_token, badly_scoped_token] do + assert {:error, :unauthorized} = Streamer.get_topic("public:local", user, token) + + assert {:error, :unauthorized} = Streamer.get_topic("public:local:media", user, token) + end + end + + test "allows remote public streams if restricted_unauthenticated is on", %{ + user: user, + token: oauth_token + } do + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) + %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) + + assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token) + + assert {:ok, "public:remote:lain.com"} = + Streamer.get_topic("public:remote", user, oauth_token, %{"instance" => "lain.com"}) + + assert {:ok, "public:remote:media:lain.com"} = + Streamer.get_topic("public:remote:media", user, oauth_token, %{ + "instance" => "lain.com" + }) + + for token <- [read_notifications_token, badly_scoped_token] do + assert {:error, :unauthorized} = Streamer.get_topic("public", user, token) + assert {:error, :unauthorized} = Streamer.get_topic("public:media", user, token) + + assert {:error, :unauthorized} = + Streamer.get_topic("public:remote", user, token, %{ + "instance" => "lain.com" + }) + + assert {:error, :unauthorized} = + Streamer.get_topic("public:remote:media", user, token, %{ + "instance" => "lain.com" + }) + end + end + test "allows user streams (with proper OAuth token scopes)", %{ user: user, token: read_oauth_token diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs index fafef54fe..be5e08776 100644 --- a/test/pleroma/web/web_finger_test.exs +++ b/test/pleroma/web/web_finger_test.exs @@ -180,5 +180,28 @@ defmodule Pleroma.Web.WebFingerTest do {:ok, _data} = WebFinger.finger("pekorino@pawoo.net") end + + test "refuses to process XML remote entities" do + Tesla.Mock.mock(fn + %{ + url: "https://pawoo.net/.well-known/webfinger?resource=acct:pekorino@pawoo.net" + } -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/xml_external_entities.xml"), + headers: [{"content-type", "application/xrd+xml"}] + }} + + %{url: "https://pawoo.net/.well-known/host-meta"} -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") + }} + end) + + assert :error = WebFinger.finger("pekorino@pawoo.net") + end end end diff --git a/test/pleroma/web/xml_test.exs b/test/pleroma/web/xml_test.exs new file mode 100644 index 000000000..49306430b --- /dev/null +++ b/test/pleroma/web/xml_test.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Web.XMLTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Web.XML + + test "refuses to parse any entities from XML" do + data = File.read!("test/fixtures/xml_billion_laughs.xml") + assert(:error == XML.parse_document(data)) + end + + test "refuses to load external entities from XML" do + data = File.read!("test/fixtures/xml_external_entities.xml") + assert(:error == XML.parse_document(data)) + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index 09f02458c..d94544717 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -50,7 +50,6 @@ defmodule Pleroma.Factory do last_refreshed_at: NaiveDateTime.utc_now(), notification_settings: %Pleroma.User.NotificationSetting{}, multi_factor_authentication_settings: %Pleroma.MFA.Settings{}, - ap_enabled: true, keys: pem } |