0569bfed309e8ed2cacbcc7d8950e91f2f528062
[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 describe "describe/1" do
220 test "returns a description of the policy" do
221 clear_config([:mrf_simple, :reject], [
222 {"remote.instance", "did not give my catboy a burg"}
223 ])
224
225 assert {:ok, %{mrf_simple: %{reject: ["remote.instance"]}}} = SimplePolicy.describe()
226 end
227
228 test "excludes domains listed in :transparency_exclusions" do
229 clear_config([:mrf, :transparency_exclusions], [{"remote.instance", ":("}])
230
231 clear_config([:mrf_simple, :reject], [
232 {"remote.instance", "did not give my catboy a burg"}
233 ])
234
235 {:ok, description} = SimplePolicy.describe()
236 assert %{mrf_simple: %{reject: []}} = description
237 assert description[:mrf_simple_info][:reject] == nil
238 end
239
240 test "obfuscates domains listed in :transparency_obfuscate_domains" do
241 clear_config([:mrf, :transparency_obfuscate_domains], ["remote.instance", "a.b"])
242
243 clear_config([:mrf_simple, :reject], [
244 {"remote.instance", "did not give my catboy a burg"},
245 {"a.b", "spam-poked me on facebook in 2006"}
246 ])
247
248 assert {:ok,
249 %{
250 mrf_simple: %{reject: ["rem***.*****nce", "a.b"]},
251 mrf_simple_info: %{reject: %{"rem***.*****nce" => %{}}}
252 }} = SimplePolicy.describe()
253 end
254 end
255
256 defp build_ftl_actor_and_message do
257 actor = insert(:user)
258
259 {actor,
260 %{
261 "actor" => actor.ap_id,
262 "to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"],
263 "cc" => [actor.follower_address, "http://foo.bar/qux"]
264 }}
265 end
266
267 describe "when :reject" do
268 test "is empty" do
269 clear_config([:mrf_simple, :reject], [])
270
271 remote_message = build_remote_message()
272
273 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
274 end
275
276 test "activity has a matching host" do
277 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
278
279 remote_message = build_remote_message()
280
281 assert {:reject, _} = SimplePolicy.filter(remote_message)
282 end
283
284 test "activity matches with wildcard domain" do
285 clear_config([:mrf_simple, :reject], [{"*.remote.instance", ""}])
286
287 remote_message = build_remote_message()
288
289 assert {:reject, _} = SimplePolicy.filter(remote_message)
290 end
291
292 test "actor has a matching host" do
293 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
294
295 remote_user = build_remote_user()
296
297 assert {:reject, _} = SimplePolicy.filter(remote_user)
298 end
299
300 test "reject Announce when object would be rejected" do
301 clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
302
303 announce = %{
304 "type" => "Announce",
305 "actor" => "https://okay.tld/users/alice",
306 "object" => %{"type" => "Note", "actor" => "https://blocked.tld/users/bob"}
307 }
308
309 assert {:reject, _} = SimplePolicy.filter(announce)
310 end
311
312 test "reject by URI object" do
313 clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
314
315 announce = %{
316 "type" => "Announce",
317 "actor" => "https://okay.tld/users/alice",
318 "object" => "https://blocked.tld/activities/1"
319 }
320
321 assert {:reject, _} = SimplePolicy.filter(announce)
322 end
323 end
324
325 describe "when :followers_only" do
326 test "is empty" do
327 clear_config([:mrf_simple, :followers_only], [])
328 {_, ftl_message} = build_ftl_actor_and_message()
329 local_message = build_local_message()
330
331 assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
332 assert SimplePolicy.filter(local_message) == {:ok, local_message}
333 end
334
335 test "has a matching host" do
336 actor = insert(:user)
337 following_user = insert(:user)
338 non_following_user = insert(:user)
339
340 {:ok, _, _, _} = CommonAPI.follow(following_user, actor)
341
342 activity = %{
343 "actor" => actor.ap_id,
344 "to" => [
345 "https://www.w3.org/ns/activitystreams#Public",
346 following_user.ap_id,
347 non_following_user.ap_id
348 ],
349 "cc" => [actor.follower_address, "http://foo.bar/qux"]
350 }
351
352 dm_activity = %{
353 "actor" => actor.ap_id,
354 "to" => [
355 following_user.ap_id,
356 non_following_user.ap_id
357 ],
358 "cc" => []
359 }
360
361 actor_domain =
362 activity
363 |> Map.fetch!("actor")
364 |> URI.parse()
365 |> Map.fetch!(:host)
366
367 clear_config([:mrf_simple, :followers_only], [{actor_domain, ""}])
368
369 assert {:ok, new_activity} = SimplePolicy.filter(activity)
370 assert actor.follower_address in new_activity["cc"]
371 assert following_user.ap_id in new_activity["to"]
372 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"]
373 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"]
374 refute non_following_user.ap_id in new_activity["to"]
375 refute non_following_user.ap_id in new_activity["cc"]
376
377 assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity)
378 assert new_dm_activity["to"] == [following_user.ap_id]
379 assert new_dm_activity["cc"] == []
380 end
381 end
382
383 describe "when :accept" do
384 test "is empty" do
385 clear_config([:mrf_simple, :accept], [])
386
387 local_message = build_local_message()
388 remote_message = build_remote_message()
389
390 assert SimplePolicy.filter(local_message) == {:ok, local_message}
391 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
392 end
393
394 test "is not empty but activity doesn't have a matching host" do
395 clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
396
397 local_message = build_local_message()
398 remote_message = build_remote_message()
399
400 assert SimplePolicy.filter(local_message) == {:ok, local_message}
401 assert {:reject, _} = SimplePolicy.filter(remote_message)
402 end
403
404 test "activity has a matching host" do
405 clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
406
407 local_message = build_local_message()
408 remote_message = build_remote_message()
409
410 assert SimplePolicy.filter(local_message) == {:ok, local_message}
411 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
412 end
413
414 test "activity matches with wildcard domain" do
415 clear_config([:mrf_simple, :accept], [{"*.remote.instance", ""}])
416
417 local_message = build_local_message()
418 remote_message = build_remote_message()
419
420 assert SimplePolicy.filter(local_message) == {:ok, local_message}
421 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
422 end
423
424 test "actor has a matching host" do
425 clear_config([:mrf_simple, :accept], [{"remote.instance", ""}])
426
427 remote_user = build_remote_user()
428
429 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
430 end
431 end
432
433 describe "when :avatar_removal" do
434 test "is empty" do
435 clear_config([:mrf_simple, :avatar_removal], [])
436
437 remote_user = build_remote_user()
438
439 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
440 end
441
442 test "is not empty but it doesn't have a matching host" do
443 clear_config([:mrf_simple, :avatar_removal], [{"non.matching.remote", ""}])
444
445 remote_user = build_remote_user()
446
447 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
448 end
449
450 test "has a matching host" do
451 clear_config([:mrf_simple, :avatar_removal], [{"remote.instance", ""}])
452
453 remote_user = build_remote_user()
454 {:ok, filtered} = SimplePolicy.filter(remote_user)
455
456 refute filtered["icon"]
457 end
458
459 test "match with wildcard domain" do
460 clear_config([:mrf_simple, :avatar_removal], [{"*.remote.instance", ""}])
461
462 remote_user = build_remote_user()
463 {:ok, filtered} = SimplePolicy.filter(remote_user)
464
465 refute filtered["icon"]
466 end
467 end
468
469 describe "when :banner_removal" do
470 test "is empty" do
471 clear_config([:mrf_simple, :banner_removal], [])
472
473 remote_user = build_remote_user()
474
475 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
476 end
477
478 test "is not empty but it doesn't have a matching host" do
479 clear_config([:mrf_simple, :banner_removal], [{"non.matching.remote", ""}])
480
481 remote_user = build_remote_user()
482
483 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
484 end
485
486 test "has a matching host" do
487 clear_config([:mrf_simple, :banner_removal], [{"remote.instance", ""}])
488
489 remote_user = build_remote_user()
490 {:ok, filtered} = SimplePolicy.filter(remote_user)
491
492 refute filtered["image"]
493 end
494
495 test "match with wildcard domain" do
496 clear_config([:mrf_simple, :banner_removal], [{"*.remote.instance", ""}])
497
498 remote_user = build_remote_user()
499 {:ok, filtered} = SimplePolicy.filter(remote_user)
500
501 refute filtered["image"]
502 end
503 end
504
505 describe "when :reject_deletes is empty" do
506 setup do: clear_config([:mrf_simple, :reject_deletes], [])
507
508 test "it accepts deletions even from rejected servers" do
509 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
510
511 deletion_message = build_remote_deletion_message()
512
513 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
514 end
515
516 test "it accepts deletions even from non-whitelisted servers" do
517 clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
518
519 deletion_message = build_remote_deletion_message()
520
521 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
522 end
523 end
524
525 describe "when :reject_deletes is not empty but it doesn't have a matching host" do
526 setup do: clear_config([:mrf_simple, :reject_deletes], [{"non.matching.remote", ""}])
527
528 test "it accepts deletions even from rejected servers" do
529 clear_config([:mrf_simple, :reject], [{"remote.instance", ""}])
530
531 deletion_message = build_remote_deletion_message()
532
533 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
534 end
535
536 test "it accepts deletions even from non-whitelisted servers" do
537 clear_config([:mrf_simple, :accept], [{"non.matching.remote", ""}])
538
539 deletion_message = build_remote_deletion_message()
540
541 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
542 end
543 end
544
545 describe "when :reject_deletes has a matching host" do
546 setup do: clear_config([:mrf_simple, :reject_deletes], [{"remote.instance", ""}])
547
548 test "it rejects the deletion" do
549 deletion_message = build_remote_deletion_message()
550
551 assert {:reject, _} = SimplePolicy.filter(deletion_message)
552 end
553 end
554
555 describe "when :reject_deletes match with wildcard domain" do
556 setup do: clear_config([:mrf_simple, :reject_deletes], [{"*.remote.instance", ""}])
557
558 test "it rejects the deletion" do
559 deletion_message = build_remote_deletion_message()
560
561 assert {:reject, _} = SimplePolicy.filter(deletion_message)
562 end
563 end
564
565 defp build_local_message do
566 %{
567 "actor" => "#{Pleroma.Web.Endpoint.url()}/users/alice",
568 "to" => [],
569 "cc" => []
570 }
571 end
572
573 defp build_remote_message do
574 %{"actor" => "https://remote.instance/users/bob"}
575 end
576
577 defp build_remote_user do
578 %{
579 "id" => "https://remote.instance/users/bob",
580 "icon" => %{
581 "url" => "http://example.com/image.jpg",
582 "type" => "Image"
583 },
584 "image" => %{
585 "url" => "http://example.com/image.jpg",
586 "type" => "Image"
587 },
588 "type" => "Person"
589 }
590 end
591
592 defp build_remote_deletion_message do
593 %{
594 "type" => "Delete",
595 "actor" => "https://remote.instance/users/bob"
596 }
597 end
598 end