0a0f51bdbb8c91bf27dc903f216579414e900887
[akkoma] / test / pleroma / web / activity_pub / mrf / simple_policy_test.exs
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
5 defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
6 use Pleroma.DataCase
7 import Pleroma.Factory
8 alias Pleroma.Web.ActivityPub.MRF.SimplePolicy
9 alias Pleroma.Web.CommonAPI
10
11 setup do:
12 clear_config(:mrf_simple,
13 media_removal: [],
14 media_nsfw: [],
15 federated_timeline_removal: [],
16 report_removal: [],
17 reject: [],
18 followers_only: [],
19 accept: [],
20 avatar_removal: [],
21 banner_removal: [],
22 reject_deletes: []
23 )
24
25 describe "when :media_removal" do
26 test "is empty" do
27 clear_config([:mrf_simple, :media_removal], [])
28 media_message = build_media_message()
29 local_message = build_local_message()
30
31 assert SimplePolicy.filter(media_message) == {:ok, media_message}
32 assert SimplePolicy.filter(local_message) == {:ok, local_message}
33 end
34
35 test "has a matching host" do
36 clear_config([:mrf_simple, :media_removal], [{"remote.instance", "Some reason"}])
37 media_message = build_media_message()
38 local_message = build_local_message()
39
40 assert SimplePolicy.filter(media_message) ==
41 {:ok,
42 media_message
43 |> Map.put("object", Map.delete(media_message["object"], "attachment"))}
44
45 assert SimplePolicy.filter(local_message) == {:ok, local_message}
46 end
47
48 test "match with wildcard domain" do
49 clear_config([:mrf_simple, :media_removal], [{"*.remote.instance", "Whatever reason"}])
50 media_message = build_media_message()
51 local_message = build_local_message()
52
53 assert SimplePolicy.filter(media_message) ==
54 {:ok,
55 media_message
56 |> Map.put("object", Map.delete(media_message["object"], "attachment"))}
57
58 assert SimplePolicy.filter(local_message) == {:ok, local_message}
59 end
60 end
61
62 describe "when :media_nsfw" do
63 test "is empty" do
64 clear_config([:mrf_simple, :media_nsfw], [])
65 media_message = build_media_message()
66 local_message = build_local_message()
67
68 assert SimplePolicy.filter(media_message) == {:ok, media_message}
69 assert SimplePolicy.filter(local_message) == {:ok, local_message}
70 end
71
72 test "has a matching host" do
73 clear_config([:mrf_simple, :media_nsfw], [{"remote.instance", "Whetever"}])
74 media_message = build_media_message()
75 local_message = build_local_message()
76
77 assert SimplePolicy.filter(media_message) ==
78 {:ok, put_in(media_message, ["object", "sensitive"], true)}
79
80 assert SimplePolicy.filter(local_message) == {:ok, local_message}
81 end
82
83 test "match with wildcard domain" do
84 clear_config([:mrf_simple, :media_nsfw], [{"*.remote.instance", "yeah yeah"}])
85 media_message = build_media_message()
86 local_message = build_local_message()
87
88 assert SimplePolicy.filter(media_message) ==
89 {:ok, put_in(media_message, ["object", "sensitive"], true)}
90
91 assert SimplePolicy.filter(local_message) == {:ok, local_message}
92 end
93 end
94
95 defp build_media_message do
96 %{
97 "actor" => "https://remote.instance/users/bob",
98 "type" => "Create",
99 "object" => %{
100 "attachment" => [%{}],
101 "tag" => ["foo"],
102 "sensitive" => false
103 }
104 }
105 end
106
107 describe "when :report_removal" do
108 test "is empty" do
109 clear_config([:mrf_simple, :report_removal], [])
110 report_message = build_report_message()
111 local_message = build_local_message()
112
113 assert SimplePolicy.filter(report_message) == {:ok, report_message}
114 assert SimplePolicy.filter(local_message) == {:ok, local_message}
115 end
116
117 test "has a matching host" do
118 clear_config([:mrf_simple, :report_removal], [{"remote.instance", "muh"}])
119 report_message = build_report_message()
120 local_message = build_local_message()
121
122 assert {:reject, _} = SimplePolicy.filter(report_message)
123 assert SimplePolicy.filter(local_message) == {:ok, local_message}
124 end
125
126 test "match with wildcard domain" do
127 clear_config([:mrf_simple, :report_removal], [{"*.remote.instance", "suya"}])
128 report_message = build_report_message()
129 local_message = build_local_message()
130
131 assert {:reject, _} = SimplePolicy.filter(report_message)
132 assert SimplePolicy.filter(local_message) == {:ok, local_message}
133 end
134 end
135
136 defp build_report_message do
137 %{
138 "actor" => "https://remote.instance/users/bob",
139 "type" => "Flag"
140 }
141 end
142
143 describe "when :federated_timeline_removal" do
144 test "is empty" do
145 clear_config([:mrf_simple, :federated_timeline_removal], [])
146 {_, ftl_message} = build_ftl_actor_and_message()
147 local_message = build_local_message()
148
149 assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
150 assert SimplePolicy.filter(local_message) == {:ok, local_message}
151 end
152
153 test "has a matching host" do
154 {actor, ftl_message} = build_ftl_actor_and_message()
155
156 ftl_message_actor_host =
157 ftl_message
158 |> Map.fetch!("actor")
159 |> URI.parse()
160 |> Map.fetch!(:host)
161
162 clear_config([:mrf_simple, :federated_timeline_removal], [{ftl_message_actor_host, "uwu"}])
163 local_message = build_local_message()
164
165 assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
166 assert actor.follower_address in ftl_message["to"]
167 refute actor.follower_address in ftl_message["cc"]
168 refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
169 assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
170
171 assert SimplePolicy.filter(local_message) == {:ok, local_message}
172 end
173
174 test "match with wildcard domain" do
175 {actor, ftl_message} = build_ftl_actor_and_message()
176
177 ftl_message_actor_host =
178 ftl_message
179 |> Map.fetch!("actor")
180 |> URI.parse()
181 |> Map.fetch!(:host)
182
183 clear_config([:mrf_simple, :federated_timeline_removal], [
184 {"*." <> ftl_message_actor_host, "owo"}
185 ])
186
187 local_message = build_local_message()
188
189 assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
190 assert actor.follower_address in ftl_message["to"]
191 refute actor.follower_address in ftl_message["cc"]
192 refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
193 assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
194
195 assert SimplePolicy.filter(local_message) == {:ok, local_message}
196 end
197
198 test "has a matching host but only as:Public in to" do
199 {_actor, ftl_message} = build_ftl_actor_and_message()
200
201 ftl_message_actor_host =
202 ftl_message
203 |> Map.fetch!("actor")
204 |> URI.parse()
205 |> Map.fetch!(:host)
206
207 ftl_message = Map.put(ftl_message, "cc", [])
208
209 clear_config([:mrf_simple, :federated_timeline_removal], [
210 {ftl_message_actor_host, "spiderwaifu goes 88w88"}
211 ])
212
213 assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
214 refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
215 assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
216 end
217 end
218
219 defp build_ftl_actor_and_message do
220 actor = insert(:user)
221
222 {actor,
223 %{
224 "actor" => actor.ap_id,
225 "to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"],
226 "cc" => [actor.follower_address, "http://foo.bar/qux"]
227 }}
228 end
229
230 describe "when :reject" do
231 test "is empty" do
232 clear_config([:mrf_simple, :reject], [])
233
234 remote_message = build_remote_message()
235
236 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
237 end
238
239 test "activity has a matching host" do
240 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
241
242 remote_message = build_remote_message()
243
244 assert {:reject, _} = SimplePolicy.filter(remote_message)
245 end
246
247 test "activity matches with wildcard domain" do
248 clear_config([:mrf_simple, :reject], [{"*.remote.instance", ""}])
249
250 remote_message = build_remote_message()
251
252 assert {:reject, _} = SimplePolicy.filter(remote_message)
253 end
254
255 test "actor has a matching host" do
256 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
257
258 remote_user = build_remote_user()
259
260 assert {:reject, _} = SimplePolicy.filter(remote_user)
261 end
262
263 test "reject Announce when object would be rejected" do
264 clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
265
266 announce = %{
267 "type" => "Announce",
268 "actor" => "https://okay.tld/users/alice",
269 "object" => %{"type" => "Note", "actor" => "https://blocked.tld/users/bob"}
270 }
271
272 assert {:reject, _} = SimplePolicy.filter(announce)
273 end
274
275 test "reject by URI object" do
276 clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
277
278 announce = %{
279 "type" => "Announce",
280 "actor" => "https://okay.tld/users/alice",
281 "object" => "https://blocked.tld/activities/1"
282 }
283
284 assert {:reject, _} = SimplePolicy.filter(announce)
285 end
286 end
287
288 describe "when :followers_only" do
289 test "is empty" do
290 clear_config([:mrf_simple, :followers_only], [])
291 {_, ftl_message} = build_ftl_actor_and_message()
292 local_message = build_local_message()
293
294 assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
295 assert SimplePolicy.filter(local_message) == {:ok, local_message}
296 end
297
298 test "has a matching host" do
299 actor = insert(:user)
300 following_user = insert(:user)
301 non_following_user = insert(:user)
302
303 {:ok, _, _, _} = CommonAPI.follow(following_user, actor)
304
305 activity = %{
306 "actor" => actor.ap_id,
307 "to" => [
308 "https://www.w3.org/ns/activitystreams#Public",
309 following_user.ap_id,
310 non_following_user.ap_id
311 ],
312 "cc" => [actor.follower_address, "http://foo.bar/qux"]
313 }
314
315 dm_activity = %{
316 "actor" => actor.ap_id,
317 "to" => [
318 following_user.ap_id,
319 non_following_user.ap_id
320 ],
321 "cc" => []
322 }
323
324 actor_domain =
325 activity
326 |> Map.fetch!("actor")
327 |> URI.parse()
328 |> Map.fetch!(:host)
329
330 clear_config([:mrf_simple, :followers_only], [{actor_domain, ""}])
331
332 assert {:ok, new_activity} = SimplePolicy.filter(activity)
333 assert actor.follower_address in new_activity["cc"]
334 assert following_user.ap_id in new_activity["to"]
335 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"]
336 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"]
337 refute non_following_user.ap_id in new_activity["to"]
338 refute non_following_user.ap_id in new_activity["cc"]
339
340 assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity)
341 assert new_dm_activity["to"] == [following_user.ap_id]
342 assert new_dm_activity["cc"] == []
343 end
344 end
345
346 describe "when :accept" do
347 test "is empty" do
348 clear_config([:mrf_simple, :accept], [])
349
350 local_message = build_local_message()
351 remote_message = build_remote_message()
352
353 assert SimplePolicy.filter(local_message) == {:ok, local_message}
354 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
355 end
356
357 test "is not empty but activity doesn't have a matching host" do
358 clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
359
360 local_message = build_local_message()
361 remote_message = build_remote_message()
362
363 assert SimplePolicy.filter(local_message) == {:ok, local_message}
364 assert {:reject, _} = SimplePolicy.filter(remote_message)
365 end
366
367 test "activity has a matching host" do
368 clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
369
370 local_message = build_local_message()
371 remote_message = build_remote_message()
372
373 assert SimplePolicy.filter(local_message) == {:ok, local_message}
374 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
375 end
376
377 test "activity matches with wildcard domain" do
378 clear_config([:mrf_simple, :accept], [{"*.remote.instance", ""}])
379
380 local_message = build_local_message()
381 remote_message = build_remote_message()
382
383 assert SimplePolicy.filter(local_message) == {:ok, local_message}
384 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
385 end
386
387 test "actor has a matching host" do
388 clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
389
390 remote_user = build_remote_user()
391
392 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
393 end
394 end
395
396 describe "when :avatar_removal" do
397 test "is empty" do
398 clear_config([:mrf_simple, :avatar_removal], [])
399
400 remote_user = build_remote_user()
401
402 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
403 end
404
405 test "is not empty but it doesn't have a matching host" do
406 clear_config([:mrf_simple, :avatar_removal], [{"non.matching.remote", ""}])
407
408 remote_user = build_remote_user()
409
410 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
411 end
412
413 test "has a matching host" do
414 clear_config([:mrf_simple, :avatar_removal], [{"remote.instance", ""}])
415
416 remote_user = build_remote_user()
417 {:ok, filtered} = SimplePolicy.filter(remote_user)
418
419 refute filtered["icon"]
420 end
421
422 test "match with wildcard domain" do
423 clear_config([:mrf_simple, :avatar_removal], [{"*.remote.instance", ""}])
424
425 remote_user = build_remote_user()
426 {:ok, filtered} = SimplePolicy.filter(remote_user)
427
428 refute filtered["icon"]
429 end
430 end
431
432 describe "when :banner_removal" do
433 test "is empty" do
434 clear_config([:mrf_simple, :banner_removal], [])
435
436 remote_user = build_remote_user()
437
438 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
439 end
440
441 test "is not empty but it doesn't have a matching host" do
442 clear_config([:mrf_simple, :banner_removal], [{"non.matching.remote", ""}])
443
444 remote_user = build_remote_user()
445
446 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
447 end
448
449 test "has a matching host" do
450 clear_config([:mrf_simple, :banner_removal], [{"remote.instance", ""}])
451
452 remote_user = build_remote_user()
453 {:ok, filtered} = SimplePolicy.filter(remote_user)
454
455 refute filtered["image"]
456 end
457
458 test "match with wildcard domain" do
459 clear_config([:mrf_simple, :banner_removal], [{"*.remote.instance", ""}])
460
461 remote_user = build_remote_user()
462 {:ok, filtered} = SimplePolicy.filter(remote_user)
463
464 refute filtered["image"]
465 end
466 end
467
468 describe "when :reject_deletes is empty" do
469 setup do: clear_config([:mrf_simple, :reject_deletes], [])
470
471 test "it accepts deletions even from rejected servers" do
472 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
473
474 deletion_message = build_remote_deletion_message()
475
476 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
477 end
478
479 test "it accepts deletions even from non-whitelisted servers" do
480 clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
481
482 deletion_message = build_remote_deletion_message()
483
484 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
485 end
486 end
487
488 describe "when :reject_deletes is not empty but it doesn't have a matching host" do
489 setup do: clear_config([:mrf_simple, :reject_deletes], [{"non.matching.remote", ""}])
490
491 test "it accepts deletions even from rejected servers" do
492 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
493
494 deletion_message = build_remote_deletion_message()
495
496 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
497 end
498
499 test "it accepts deletions even from non-whitelisted servers" do
500 clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
501
502 deletion_message = build_remote_deletion_message()
503
504 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
505 end
506 end
507
508 describe "when :reject_deletes has a matching host" do
509 setup do: clear_config([:mrf_simple, :reject_deletes], [{"remote.instance", ""}])
510
511 test "it rejects the deletion" do
512 deletion_message = build_remote_deletion_message()
513
514 assert {:reject, _} = SimplePolicy.filter(deletion_message)
515 end
516 end
517
518 describe "when :reject_deletes match with wildcard domain" do
519 setup do: clear_config([:mrf_simple, :reject_deletes], [{"*.remote.instance", ""}])
520
521 test "it rejects the deletion" do
522 deletion_message = build_remote_deletion_message()
523
524 assert {:reject, _} = SimplePolicy.filter(deletion_message)
525 end
526 end
527
528 defp build_local_message do
529 %{
530 "actor" => "#{Pleroma.Web.Endpoint.url()}/users/alice",
531 "to" => [],
532 "cc" => []
533 }
534 end
535
536 defp build_remote_message do
537 %{"actor" => "https://remote.instance/users/bob"}
538 end
539
540 defp build_remote_user do
541 %{
542 "id" => "https://remote.instance/users/bob",
543 "icon" => %{
544 "url" => "http://example.com/image.jpg",
545 "type" => "Image"
546 },
547 "image" => %{
548 "url" => "http://example.com/image.jpg",
549 "type" => "Image"
550 },
551 "type" => "Person"
552 }
553 end
554
555 defp build_remote_deletion_message do
556 %{
557 "type" => "Delete",
558 "actor" => "https://remote.instance/users/bob"
559 }
560 end
561 end