total rebase
[anni] / test / pleroma / web / activity_pub / mrf / emoji_policy_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicyTest do
6   use Pleroma.DataCase
7
8   require Pleroma.Constants
9
10   alias Pleroma.Web.ActivityPub.MRF
11   alias Pleroma.Web.ActivityPub.MRF.EmojiPolicy
12
13   setup do: clear_config(:mrf_emoji)
14
15   setup do
16     clear_config([:mrf_emoji], %{
17       remove_url: [],
18       remove_shortcode: [],
19       federated_timeline_removal_url: [],
20       federated_timeline_removal_shortcode: []
21     })
22   end
23
24   @emoji_tags [
25     %{
26       "icon" => %{
27         "type" => "Image",
28         "url" => "https://example.org/emoji/biribiri/mikoto_smile2.png"
29       },
30       "id" => "https://example.org/emoji/biribiri/mikoto_smile2.png",
31       "name" => ":mikoto_smile2:",
32       "type" => "Emoji",
33       "updated" => "1970-01-01T00:00:00Z"
34     },
35     %{
36       "icon" => %{
37         "type" => "Image",
38         "url" => "https://example.org/emoji/biribiri/mikoto_smile3.png"
39       },
40       "id" => "https://example.org/emoji/biribiri/mikoto_smile3.png",
41       "name" => ":mikoto_smile3:",
42       "type" => "Emoji",
43       "updated" => "1970-01-01T00:00:00Z"
44     },
45     %{
46       "icon" => %{
47         "type" => "Image",
48         "url" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
49       },
50       "id" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png",
51       "name" => ":nekomimi_girl_emoji_007:",
52       "type" => "Emoji",
53       "updated" => "1970-01-01T00:00:00Z"
54     },
55     %{
56       "icon" => %{
57         "type" => "Image",
58         "url" => "https://example.org/test.png"
59       },
60       "id" => "https://example.org/test.png",
61       "name" => ":test:",
62       "type" => "Emoji",
63       "updated" => "1970-01-01T00:00:00Z"
64     }
65   ]
66
67   @misc_tags [%{"type" => "Placeholder"}]
68
69   @user_data %{
70     "type" => "Person",
71     "id" => "https://example.org/placeholder",
72     "name" => "lol",
73     "tag" => @emoji_tags ++ @misc_tags
74   }
75
76   @status_data %{
77     "type" => "Create",
78     "object" => %{
79       "type" => "Note",
80       "id" => "https://example.org/placeholder",
81       "content" => "lol",
82       "tag" => @emoji_tags ++ @misc_tags,
83       "emoji" => %{
84         "mikoto_smile2" => "https://example.org/emoji/biribiri/mikoto_smile2.png",
85         "mikoto_smile3" => "https://example.org/emoji/biribiri/mikoto_smile3.png",
86         "nekomimi_girl_emoji_007" =>
87           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png",
88         "test" => "https://example.org/test.png"
89       },
90       "to" => ["https://example.org/self", Pleroma.Constants.as_public()],
91       "cc" => ["https://example.org/someone"]
92     },
93     "to" => ["https://example.org/self", Pleroma.Constants.as_public()],
94     "cc" => ["https://example.org/someone"]
95   }
96
97   @status_data_with_history %{
98     "type" => "Create",
99     "object" =>
100       @status_data["object"]
101       |> Map.merge(%{
102         "formerRepresentations" => %{
103           "type" => "OrderedCollection",
104           "orderedItems" => [@status_data["object"] |> Map.put("content", "older")],
105           "totalItems" => 1
106         }
107       }),
108     "to" => ["https://example.org/self", Pleroma.Constants.as_public()],
109     "cc" => ["https://example.org/someone"]
110   }
111
112   @emoji_react_data %{
113     "type" => "EmojiReact",
114     "tag" => [@emoji_tags |> Enum.at(3)],
115     "object" => "https://example.org/someobject",
116     "to" => ["https://example.org/self"],
117     "cc" => ["https://example.org/someone"]
118   }
119
120   @emoji_react_data_matching_regex %{
121     "type" => "EmojiReact",
122     "tag" => [@emoji_tags |> Enum.at(1)],
123     "object" => "https://example.org/someobject",
124     "to" => ["https://example.org/self"],
125     "cc" => ["https://example.org/someone"]
126   }
127
128   @emoji_react_data_matching_nothing %{
129     "type" => "EmojiReact",
130     "tag" => [@emoji_tags |> Enum.at(2)],
131     "object" => "https://example.org/someobject",
132     "to" => ["https://example.org/self"],
133     "cc" => ["https://example.org/someone"]
134   }
135
136   @emoji_react_data_unicode %{
137     "type" => "EmojiReact",
138     "content" => "😍",
139     "object" => "https://example.org/someobject",
140     "to" => ["https://example.org/self"],
141     "cc" => ["https://example.org/someone"]
142   }
143
144   describe "remove_url" do
145     setup do
146       clear_config([:mrf_emoji, :remove_url], [
147         "https://example.org/test.png",
148         ~r{/biribiri/mikoto_smile[23]\.png},
149         "nekomimi_girl_emoji"
150       ])
151
152       :ok
153     end
154
155     test "processes user" do
156       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data)
157
158       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
159
160       assert %{"tag" => ^expected_tags} = filtered
161     end
162
163     test "processes status" do
164       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
165
166       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
167
168       expected_emoji = %{
169         "nekomimi_girl_emoji_007" =>
170           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
171       }
172
173       assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered
174     end
175
176     test "processes status with history" do
177       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data_with_history)
178
179       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
180
181       expected_emoji = %{
182         "nekomimi_girl_emoji_007" =>
183           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
184       }
185
186       assert %{
187                "object" => %{
188                  "tag" => ^expected_tags,
189                  "emoji" => ^expected_emoji,
190                  "formerRepresentations" => %{"orderedItems" => [item]}
191                }
192              } = filtered
193
194       assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
195     end
196
197     test "processes updates" do
198       {:ok, filtered} =
199         MRF.filter_one(EmojiPolicy, @status_data_with_history |> Map.put("type", "Update"))
200
201       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
202
203       expected_emoji = %{
204         "nekomimi_girl_emoji_007" =>
205           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
206       }
207
208       assert %{
209                "object" => %{
210                  "tag" => ^expected_tags,
211                  "emoji" => ^expected_emoji,
212                  "formerRepresentations" => %{"orderedItems" => [item]}
213                }
214              } = filtered
215
216       assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
217     end
218
219     test "processes EmojiReact" do
220       assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
221                MRF.filter_one(EmojiPolicy, @emoji_react_data)
222
223       assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
224                MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_regex)
225
226       assert {:ok, @emoji_react_data_matching_nothing} ==
227                MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_nothing)
228
229       assert {:ok, @emoji_react_data_unicode} ==
230                MRF.filter_one(EmojiPolicy, @emoji_react_data_unicode)
231     end
232   end
233
234   describe "remove_shortcode" do
235     setup do
236       clear_config([:mrf_emoji, :remove_shortcode], [
237         "test",
238         ~r{mikoto_s},
239         "nekomimi_girl_emoji"
240       ])
241
242       :ok
243     end
244
245     test "processes user" do
246       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data)
247
248       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
249
250       assert %{"tag" => ^expected_tags} = filtered
251     end
252
253     test "processes status" do
254       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
255
256       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
257
258       expected_emoji = %{
259         "nekomimi_girl_emoji_007" =>
260           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
261       }
262
263       assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered
264     end
265
266     test "processes status with history" do
267       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data_with_history)
268
269       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
270
271       expected_emoji = %{
272         "nekomimi_girl_emoji_007" =>
273           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
274       }
275
276       assert %{
277                "object" => %{
278                  "tag" => ^expected_tags,
279                  "emoji" => ^expected_emoji,
280                  "formerRepresentations" => %{"orderedItems" => [item]}
281                }
282              } = filtered
283
284       assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
285     end
286
287     test "processes updates" do
288       {:ok, filtered} =
289         MRF.filter_one(EmojiPolicy, @status_data_with_history |> Map.put("type", "Update"))
290
291       expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
292
293       expected_emoji = %{
294         "nekomimi_girl_emoji_007" =>
295           "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
296       }
297
298       assert %{
299                "object" => %{
300                  "tag" => ^expected_tags,
301                  "emoji" => ^expected_emoji,
302                  "formerRepresentations" => %{"orderedItems" => [item]}
303                }
304              } = filtered
305
306       assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
307     end
308
309     test "processes EmojiReact" do
310       assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
311                MRF.filter_one(EmojiPolicy, @emoji_react_data)
312
313       assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
314                MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_regex)
315
316       assert {:ok, @emoji_react_data_matching_nothing} ==
317                MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_nothing)
318
319       assert {:ok, @emoji_react_data_unicode} ==
320                MRF.filter_one(EmojiPolicy, @emoji_react_data_unicode)
321     end
322   end
323
324   describe "federated_timeline_removal_url" do
325     setup do
326       clear_config([:mrf_emoji, :federated_timeline_removal_url], [
327         "https://example.org/test.png",
328         ~r{/biribiri/mikoto_smile[23]\.png},
329         "nekomimi_girl_emoji"
330       ])
331
332       :ok
333     end
334
335     test "processes status" do
336       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
337
338       expected_tags = @status_data["object"]["tag"]
339       expected_emoji = @status_data["object"]["emoji"]
340
341       expected_to = ["https://example.org/self"]
342       expected_cc = [Pleroma.Constants.as_public(), "https://example.org/someone"]
343
344       assert %{
345                "to" => ^expected_to,
346                "cc" => ^expected_cc,
347                "object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}
348              } = filtered
349     end
350
351     test "ignore updates" do
352       {:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data |> Map.put("type", "Update"))
353
354       expected_tags = @status_data["object"]["tag"]
355       expected_emoji = @status_data["object"]["emoji"]
356
357       expected_to = ["https://example.org/self", Pleroma.Constants.as_public()]
358       expected_cc = ["https://example.org/someone"]
359
360       assert %{
361                "to" => ^expected_to,
362                "cc" => ^expected_cc,
363                "object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}
364              } = filtered
365     end
366
367     test "processes status with history" do
368       status =
369         @status_data_with_history
370         |> put_in(["object", "tag"], @misc_tags)
371         |> put_in(["object", "emoji"], %{})
372
373       {:ok, filtered} = MRF.filter_one(EmojiPolicy, status)
374
375       expected_tags = @status_data["object"]["tag"]
376       expected_emoji = @status_data["object"]["emoji"]
377
378       expected_to = ["https://example.org/self"]
379       expected_cc = [Pleroma.Constants.as_public(), "https://example.org/someone"]
380
381       assert %{
382                "to" => ^expected_to,
383                "cc" => ^expected_cc,
384                "object" => %{
385                  "formerRepresentations" => %{
386                    "orderedItems" => [%{"tag" => ^expected_tags, "emoji" => ^expected_emoji}]
387                  }
388                }
389              } = filtered
390     end
391   end
392
393   describe "edge cases" do
394     setup do
395       clear_config([:mrf_emoji, :remove_url], [
396         "https://example.org/test.png",
397         ~r{/biribiri/mikoto_smile[23]\.png},
398         "nekomimi_girl_emoji"
399       ])
400
401       :ok
402     end
403
404     test "non-statuses" do
405       answer = @status_data |> put_in(["object", "type"], "Answer")
406       {:ok, filtered} = MRF.filter_one(EmojiPolicy, answer)
407
408       assert filtered == answer
409     end
410
411     test "without tag" do
412       status = @status_data |> Map.put("object", Map.drop(@status_data["object"], ["tag"]))
413       {:ok, filtered} = MRF.filter_one(EmojiPolicy, status)
414
415       refute Map.has_key?(filtered["object"], "tag")
416     end
417
418     test "without emoji" do
419       status = @status_data |> Map.put("object", Map.drop(@status_data["object"], ["emoji"]))
420       {:ok, filtered} = MRF.filter_one(EmojiPolicy, status)
421
422       refute Map.has_key?(filtered["object"], "emoji")
423     end
424   end
425 end