Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

528 Zeilen
17KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
  5. use Oban.Testing, repo: Pleroma.Repo
  6. use Pleroma.DataCase
  7. alias Pleroma.Activity
  8. alias Pleroma.Object
  9. alias Pleroma.Tests.ObanHelpers
  10. alias Pleroma.User
  11. alias Pleroma.Web.ActivityPub.Transmogrifier
  12. alias Pleroma.Web.ActivityPub.Utils
  13. alias Pleroma.Web.AdminAPI.AccountView
  14. alias Pleroma.Web.CommonAPI
  15. import Mock
  16. import Pleroma.Factory
  17. import ExUnit.CaptureLog
  18. setup_all do
  19. Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
  20. :ok
  21. end
  22. setup do: clear_config([:instance, :max_remote_account_fields])
  23. describe "handle_incoming" do
  24. test "it works for incoming unfollows with an existing follow" do
  25. user = insert(:user)
  26. follow_data =
  27. File.read!("test/fixtures/mastodon-follow-activity.json")
  28. |> Jason.decode!()
  29. |> Map.put("object", user.ap_id)
  30. {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
  31. data =
  32. File.read!("test/fixtures/mastodon-unfollow-activity.json")
  33. |> Jason.decode!()
  34. |> Map.put("object", follow_data)
  35. {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
  36. assert data["type"] == "Undo"
  37. assert data["object"]["type"] == "Follow"
  38. assert data["object"]["object"] == user.ap_id
  39. assert data["actor"] == "http://mastodon.example.org/users/admin"
  40. refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
  41. end
  42. test "it accepts Flag activities" do
  43. user = insert(:user)
  44. other_user = insert(:user)
  45. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  46. object = Object.normalize(activity, fetch: false)
  47. note_obj = %{
  48. "type" => "Note",
  49. "id" => activity.data["id"],
  50. "content" => "test post",
  51. "published" => object.data["published"],
  52. "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true})
  53. }
  54. message = %{
  55. "@context" => "https://www.w3.org/ns/activitystreams",
  56. "cc" => [user.ap_id],
  57. "object" => [user.ap_id, activity.data["id"]],
  58. "type" => "Flag",
  59. "content" => "blocked AND reported!!!",
  60. "actor" => other_user.ap_id
  61. }
  62. assert {:ok, activity} = Transmogrifier.handle_incoming(message)
  63. assert activity.data["object"] == [user.ap_id, note_obj]
  64. assert activity.data["content"] == "blocked AND reported!!!"
  65. assert activity.data["actor"] == other_user.ap_id
  66. assert activity.data["cc"] == [user.ap_id]
  67. end
  68. test "it accepts Move activities" do
  69. old_user = insert(:user)
  70. new_user = insert(:user)
  71. message = %{
  72. "@context" => "https://www.w3.org/ns/activitystreams",
  73. "type" => "Move",
  74. "actor" => old_user.ap_id,
  75. "object" => old_user.ap_id,
  76. "target" => new_user.ap_id
  77. }
  78. assert :error = Transmogrifier.handle_incoming(message)
  79. {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
  80. assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
  81. assert activity.actor == old_user.ap_id
  82. assert activity.data["actor"] == old_user.ap_id
  83. assert activity.data["object"] == old_user.ap_id
  84. assert activity.data["target"] == new_user.ap_id
  85. assert activity.data["type"] == "Move"
  86. end
  87. end
  88. describe "prepare outgoing" do
  89. test "it inlines private announced objects" do
  90. user = insert(:user)
  91. {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
  92. {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
  93. {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
  94. assert modified["object"]["content"] == "hey"
  95. assert modified["object"]["actor"] == modified["object"]["attributedTo"]
  96. end
  97. test "it turns mentions into tags" do
  98. user = insert(:user)
  99. other_user = insert(:user)
  100. {:ok, activity} =
  101. CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"})
  102. with_mock Pleroma.Notification,
  103. get_notified_from_activity: fn _, _ -> [] end do
  104. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  105. object = modified["object"]
  106. expected_mention = %{
  107. "href" => other_user.ap_id,
  108. "name" => "@#{other_user.nickname}",
  109. "type" => "Mention"
  110. }
  111. expected_tag = %{
  112. "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
  113. "type" => "Hashtag",
  114. "name" => "#2hu"
  115. }
  116. refute called(Pleroma.Notification.get_notified_from_activity(:_, :_))
  117. assert Enum.member?(object["tag"], expected_tag)
  118. assert Enum.member?(object["tag"], expected_mention)
  119. end
  120. end
  121. test "it adds the json-ld context and the conversation property" do
  122. user = insert(:user)
  123. {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
  124. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  125. assert modified["@context"] == Utils.make_json_ld_header()["@context"]
  126. assert modified["object"]["conversation"] == modified["context"]
  127. end
  128. test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
  129. user = insert(:user)
  130. {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
  131. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  132. assert modified["object"]["actor"] == modified["object"]["attributedTo"]
  133. end
  134. test "it strips internal hashtag data" do
  135. user = insert(:user)
  136. {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"})
  137. expected_tag = %{
  138. "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
  139. "type" => "Hashtag",
  140. "name" => "#2hu"
  141. }
  142. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  143. assert modified["object"]["tag"] == [expected_tag]
  144. end
  145. test "it strips internal fields" do
  146. user = insert(:user)
  147. {:ok, activity} =
  148. CommonAPI.post(user, %{
  149. status: "#2hu :firefox:",
  150. generator: %{type: "Application", name: "TestClient", url: "https://pleroma.social"}
  151. })
  152. # Ensure injected application data made it into the activity
  153. # as we don't have a Token to derive it from, otherwise it will
  154. # be nil and the test will pass
  155. assert %{
  156. type: "Application",
  157. name: "TestClient",
  158. url: "https://pleroma.social"
  159. } == activity.object.data["generator"]
  160. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  161. assert length(modified["object"]["tag"]) == 2
  162. assert is_nil(modified["object"]["emoji"])
  163. assert is_nil(modified["object"]["like_count"])
  164. assert is_nil(modified["object"]["announcements"])
  165. assert is_nil(modified["object"]["announcement_count"])
  166. assert is_nil(modified["object"]["context_id"])
  167. assert is_nil(modified["object"]["generator"])
  168. end
  169. test "it strips internal fields of article" do
  170. activity = insert(:article_activity)
  171. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  172. assert length(modified["object"]["tag"]) == 2
  173. assert is_nil(modified["object"]["emoji"])
  174. assert is_nil(modified["object"]["like_count"])
  175. assert is_nil(modified["object"]["announcements"])
  176. assert is_nil(modified["object"]["announcement_count"])
  177. assert is_nil(modified["object"]["context_id"])
  178. assert is_nil(modified["object"]["likes"])
  179. end
  180. test "the directMessage flag is present" do
  181. user = insert(:user)
  182. other_user = insert(:user)
  183. {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"})
  184. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  185. assert modified["directMessage"] == false
  186. {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"})
  187. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  188. assert modified["directMessage"] == false
  189. {:ok, activity} =
  190. CommonAPI.post(user, %{
  191. status: "@#{other_user.nickname} :moominmamma:",
  192. visibility: "direct"
  193. })
  194. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  195. assert modified["directMessage"] == true
  196. end
  197. test "it strips BCC field" do
  198. user = insert(:user)
  199. {:ok, list} = Pleroma.List.create("foo", user)
  200. {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
  201. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  202. assert is_nil(modified["bcc"])
  203. end
  204. test "it can handle Listen activities" do
  205. listen_activity = insert(:listen)
  206. {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
  207. assert modified["type"] == "Listen"
  208. user = insert(:user)
  209. {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
  210. {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
  211. end
  212. test "custom emoji urls are URI encoded" do
  213. # :dinosaur: filename has a space -> dino walking.gif
  214. user = insert(:user)
  215. {:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
  216. {:ok, prepared} = Transmogrifier.prepare_outgoing(activity.data)
  217. assert length(prepared["object"]["tag"]) == 1
  218. url = prepared["object"]["tag"] |> List.first() |> Map.get("icon") |> Map.get("url")
  219. assert url == "http://localhost:4001/emoji/dino%20walking.gif"
  220. end
  221. end
  222. describe "user upgrade" do
  223. test "it upgrades a user to activitypub" do
  224. user =
  225. insert(:user, %{
  226. nickname: "rye@niu.moe",
  227. local: false,
  228. ap_id: "https://niu.moe/users/rye",
  229. follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
  230. })
  231. user_two = insert(:user)
  232. Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)
  233. {:ok, activity} = CommonAPI.post(user, %{status: "test"})
  234. {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"})
  235. assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
  236. user = User.get_cached_by_id(user.id)
  237. assert user.note_count == 1
  238. {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
  239. ObanHelpers.perform_all()
  240. assert user.ap_enabled
  241. assert user.note_count == 1
  242. assert user.follower_address == "https://niu.moe/users/rye/followers"
  243. assert user.following_address == "https://niu.moe/users/rye/following"
  244. user = User.get_cached_by_id(user.id)
  245. assert user.note_count == 1
  246. activity = Activity.get_by_id(activity.id)
  247. assert user.follower_address in activity.recipients
  248. assert %{
  249. "url" => [
  250. %{
  251. "href" =>
  252. "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
  253. }
  254. ]
  255. } = user.avatar
  256. assert %{
  257. "url" => [
  258. %{
  259. "href" =>
  260. "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
  261. }
  262. ]
  263. } = user.banner
  264. refute "..." in activity.recipients
  265. unrelated_activity = Activity.get_by_id(unrelated_activity.id)
  266. refute user.follower_address in unrelated_activity.recipients
  267. user_two = User.get_cached_by_id(user_two.id)
  268. assert User.following?(user_two, user)
  269. refute "..." in User.following(user_two)
  270. end
  271. end
  272. describe "actor rewriting" do
  273. test "it fixes the actor URL property to be a proper URI" do
  274. data = %{
  275. "url" => %{"href" => "http://example.com"}
  276. }
  277. rewritten = Transmogrifier.maybe_fix_user_object(data)
  278. assert rewritten["url"] == "http://example.com"
  279. end
  280. end
  281. describe "actor origin containment" do
  282. test "it rejects activities which reference objects with bogus origins" do
  283. data = %{
  284. "@context" => "https://www.w3.org/ns/activitystreams",
  285. "id" => "http://mastodon.example.org/users/admin/activities/1234",
  286. "actor" => "http://mastodon.example.org/users/admin",
  287. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  288. "object" => "https://info.pleroma.site/activity.json",
  289. "type" => "Announce"
  290. }
  291. assert capture_log(fn ->
  292. {:error, _} = Transmogrifier.handle_incoming(data)
  293. end) =~ "Object containment failed"
  294. end
  295. test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
  296. data = %{
  297. "@context" => "https://www.w3.org/ns/activitystreams",
  298. "id" => "http://mastodon.example.org/users/admin/activities/1234",
  299. "actor" => "http://mastodon.example.org/users/admin",
  300. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  301. "object" => "https://info.pleroma.site/activity2.json",
  302. "type" => "Announce"
  303. }
  304. assert capture_log(fn ->
  305. {:error, _} = Transmogrifier.handle_incoming(data)
  306. end) =~ "Object containment failed"
  307. end
  308. test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
  309. data = %{
  310. "@context" => "https://www.w3.org/ns/activitystreams",
  311. "id" => "http://mastodon.example.org/users/admin/activities/1234",
  312. "actor" => "http://mastodon.example.org/users/admin",
  313. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  314. "object" => "https://info.pleroma.site/activity3.json",
  315. "type" => "Announce"
  316. }
  317. assert capture_log(fn ->
  318. {:error, _} = Transmogrifier.handle_incoming(data)
  319. end) =~ "Object containment failed"
  320. end
  321. end
  322. describe "fix_explicit_addressing" do
  323. setup do
  324. user = insert(:user)
  325. [user: user]
  326. end
  327. test "moves non-explicitly mentioned actors to cc", %{user: user} do
  328. explicitly_mentioned_actors = [
  329. "https://pleroma.gold/users/user1",
  330. "https://pleroma.gold/user2"
  331. ]
  332. object = %{
  333. "actor" => user.ap_id,
  334. "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
  335. "cc" => [],
  336. "tag" =>
  337. Enum.map(explicitly_mentioned_actors, fn href ->
  338. %{"type" => "Mention", "href" => href}
  339. end)
  340. }
  341. fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
  342. assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
  343. refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
  344. assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
  345. end
  346. test "does not move actor's follower collection to cc", %{user: user} do
  347. object = %{
  348. "actor" => user.ap_id,
  349. "to" => [user.follower_address],
  350. "cc" => []
  351. }
  352. fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
  353. assert user.follower_address in fixed_object["to"]
  354. refute user.follower_address in fixed_object["cc"]
  355. end
  356. test "removes recipient's follower collection from cc", %{user: user} do
  357. recipient = insert(:user)
  358. object = %{
  359. "actor" => user.ap_id,
  360. "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
  361. "cc" => [user.follower_address, recipient.follower_address]
  362. }
  363. fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
  364. assert user.follower_address in fixed_object["cc"]
  365. refute recipient.follower_address in fixed_object["cc"]
  366. refute recipient.follower_address in fixed_object["to"]
  367. end
  368. end
  369. describe "fix_summary/1" do
  370. test "returns fixed object" do
  371. assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
  372. assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
  373. assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
  374. end
  375. end
  376. describe "fix_url/1" do
  377. test "fixes data for object when url is map" do
  378. object = %{
  379. "url" => %{
  380. "type" => "Link",
  381. "mimeType" => "video/mp4",
  382. "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
  383. }
  384. }
  385. assert Transmogrifier.fix_url(object) == %{
  386. "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
  387. }
  388. end
  389. test "returns non-modified object" do
  390. assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
  391. end
  392. end
  393. describe "get_obj_helper/2" do
  394. test "returns nil when cannot normalize object" do
  395. assert capture_log(fn ->
  396. refute Transmogrifier.get_obj_helper("test-obj-id")
  397. end) =~ "Unsupported URI scheme"
  398. end
  399. @tag capture_log: true
  400. test "returns {:ok, %Object{}} for success case" do
  401. assert {:ok, %Object{}} =
  402. Transmogrifier.get_obj_helper(
  403. "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
  404. )
  405. end
  406. end
  407. end