Merge remote-tracking branch 'upstream/develop' into simplepolicy-announce-leak
[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"])
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"])
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"])
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"])
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"])
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"])
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])
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], ["*." <> ftl_message_actor_host])
184 local_message = build_local_message()
185
186 assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
187 assert actor.follower_address in ftl_message["to"]
188 refute actor.follower_address in ftl_message["cc"]
189 refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
190 assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
191
192 assert SimplePolicy.filter(local_message) == {:ok, local_message}
193 end
194
195 test "has a matching host but only as:Public in to" do
196 {_actor, ftl_message} = build_ftl_actor_and_message()
197
198 ftl_message_actor_host =
199 ftl_message
200 |> Map.fetch!("actor")
201 |> URI.parse()
202 |> Map.fetch!(:host)
203
204 ftl_message = Map.put(ftl_message, "cc", [])
205
206 clear_config([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
207
208 assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
209 refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
210 assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
211 end
212 end
213
214 defp build_ftl_actor_and_message do
215 actor = insert(:user)
216
217 {actor,
218 %{
219 "actor" => actor.ap_id,
220 "to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"],
221 "cc" => [actor.follower_address, "http://foo.bar/qux"]
222 }}
223 end
224
225 describe "when :reject" do
226 test "is empty" do
227 clear_config([:mrf_simple, :reject], [])
228
229 remote_message = build_remote_message()
230
231 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
232 end
233
234 test "activity has a matching host" do
235 clear_config([:mrf_simple, :reject], ["remote.instance"])
236
237 remote_message = build_remote_message()
238
239 assert {:reject, _} = SimplePolicy.filter(remote_message)
240 end
241
242 test "activity matches with wildcard domain" do
243 clear_config([:mrf_simple, :reject], ["*.remote.instance"])
244
245 remote_message = build_remote_message()
246
247 assert {:reject, _} = SimplePolicy.filter(remote_message)
248 end
249
250 test "actor has a matching host" do
251 clear_config([:mrf_simple, :reject], ["remote.instance"])
252
253 remote_user = build_remote_user()
254
255 assert {:reject, _} = SimplePolicy.filter(remote_user)
256 end
257
258 test "reject Announce when object would be rejected" do
259 clear_config([:mrf_simple, :reject], ["blocked.tld"])
260
261 announce = %{
262 "type" => "Announce",
263 "actor" => "https://okay.tld/users/alice",
264 "object" => %{"type" => "Note", "actor" => "https://blocked.tld/users/bob"}
265 }
266
267 assert {:reject, _} = SimplePolicy.filter(announce)
268 end
269
270 test "reject by URI object" do
271 clear_config([:mrf_simple, :reject], ["blocked.tld"])
272
273 announce = %{
274 "type" => "Announce",
275 "actor" => "https://okay.tld/users/alice",
276 "object" => "https://blocked.tld/activities/1"
277 }
278
279 assert {:reject, _} = SimplePolicy.filter(announce)
280 end
281 end
282
283 describe "when :followers_only" do
284 test "is empty" do
285 clear_config([:mrf_simple, :followers_only], [])
286 {_, ftl_message} = build_ftl_actor_and_message()
287 local_message = build_local_message()
288
289 assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
290 assert SimplePolicy.filter(local_message) == {:ok, local_message}
291 end
292
293 test "has a matching host" do
294 actor = insert(:user)
295 following_user = insert(:user)
296 non_following_user = insert(:user)
297
298 {:ok, _, _, _} = CommonAPI.follow(following_user, actor)
299
300 activity = %{
301 "actor" => actor.ap_id,
302 "to" => [
303 "https://www.w3.org/ns/activitystreams#Public",
304 following_user.ap_id,
305 non_following_user.ap_id
306 ],
307 "cc" => [actor.follower_address, "http://foo.bar/qux"]
308 }
309
310 dm_activity = %{
311 "actor" => actor.ap_id,
312 "to" => [
313 following_user.ap_id,
314 non_following_user.ap_id
315 ],
316 "cc" => []
317 }
318
319 actor_domain =
320 activity
321 |> Map.fetch!("actor")
322 |> URI.parse()
323 |> Map.fetch!(:host)
324
325 clear_config([:mrf_simple, :followers_only], [actor_domain])
326
327 assert {:ok, new_activity} = SimplePolicy.filter(activity)
328 assert actor.follower_address in new_activity["cc"]
329 assert following_user.ap_id in new_activity["to"]
330 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"]
331 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"]
332 refute non_following_user.ap_id in new_activity["to"]
333 refute non_following_user.ap_id in new_activity["cc"]
334
335 assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity)
336 assert new_dm_activity["to"] == [following_user.ap_id]
337 assert new_dm_activity["cc"] == []
338 end
339 end
340
341 describe "when :accept" do
342 test "is empty" do
343 clear_config([:mrf_simple, :accept], [])
344
345 local_message = build_local_message()
346 remote_message = build_remote_message()
347
348 assert SimplePolicy.filter(local_message) == {:ok, local_message}
349 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
350 end
351
352 test "is not empty but activity doesn't have a matching host" do
353 clear_config([:mrf_simple, :accept], ["non.matching.remote"])
354
355 local_message = build_local_message()
356 remote_message = build_remote_message()
357
358 assert SimplePolicy.filter(local_message) == {:ok, local_message}
359 assert {:reject, _} = SimplePolicy.filter(remote_message)
360 end
361
362 test "activity has a matching host" do
363 clear_config([:mrf_simple, :accept], ["remote.instance"])
364
365 local_message = build_local_message()
366 remote_message = build_remote_message()
367
368 assert SimplePolicy.filter(local_message) == {:ok, local_message}
369 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
370 end
371
372 test "activity matches with wildcard domain" do
373 clear_config([:mrf_simple, :accept], ["*.remote.instance"])
374
375 local_message = build_local_message()
376 remote_message = build_remote_message()
377
378 assert SimplePolicy.filter(local_message) == {:ok, local_message}
379 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
380 end
381
382 test "actor has a matching host" do
383 clear_config([:mrf_simple, :accept], ["remote.instance"])
384
385 remote_user = build_remote_user()
386
387 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
388 end
389 end
390
391 describe "when :avatar_removal" do
392 test "is empty" do
393 clear_config([:mrf_simple, :avatar_removal], [])
394
395 remote_user = build_remote_user()
396
397 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
398 end
399
400 test "is not empty but it doesn't have a matching host" do
401 clear_config([:mrf_simple, :avatar_removal], ["non.matching.remote"])
402
403 remote_user = build_remote_user()
404
405 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
406 end
407
408 test "has a matching host" do
409 clear_config([:mrf_simple, :avatar_removal], ["remote.instance"])
410
411 remote_user = build_remote_user()
412 {:ok, filtered} = SimplePolicy.filter(remote_user)
413
414 refute filtered["icon"]
415 end
416
417 test "match with wildcard domain" do
418 clear_config([:mrf_simple, :avatar_removal], ["*.remote.instance"])
419
420 remote_user = build_remote_user()
421 {:ok, filtered} = SimplePolicy.filter(remote_user)
422
423 refute filtered["icon"]
424 end
425 end
426
427 describe "when :banner_removal" do
428 test "is empty" do
429 clear_config([:mrf_simple, :banner_removal], [])
430
431 remote_user = build_remote_user()
432
433 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
434 end
435
436 test "is not empty but it doesn't have a matching host" do
437 clear_config([:mrf_simple, :banner_removal], ["non.matching.remote"])
438
439 remote_user = build_remote_user()
440
441 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
442 end
443
444 test "has a matching host" do
445 clear_config([:mrf_simple, :banner_removal], ["remote.instance"])
446
447 remote_user = build_remote_user()
448 {:ok, filtered} = SimplePolicy.filter(remote_user)
449
450 refute filtered["image"]
451 end
452
453 test "match with wildcard domain" do
454 clear_config([:mrf_simple, :banner_removal], ["*.remote.instance"])
455
456 remote_user = build_remote_user()
457 {:ok, filtered} = SimplePolicy.filter(remote_user)
458
459 refute filtered["image"]
460 end
461 end
462
463 describe "when :reject_deletes is empty" do
464 setup do: clear_config([:mrf_simple, :reject_deletes], [])
465
466 test "it accepts deletions even from rejected servers" do
467 clear_config([:mrf_simple, :reject], ["remote.instance"])
468
469 deletion_message = build_remote_deletion_message()
470
471 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
472 end
473
474 test "it accepts deletions even from non-whitelisted servers" do
475 clear_config([:mrf_simple, :accept], ["non.matching.remote"])
476
477 deletion_message = build_remote_deletion_message()
478
479 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
480 end
481 end
482
483 describe "when :reject_deletes is not empty but it doesn't have a matching host" do
484 setup do: clear_config([:mrf_simple, :reject_deletes], ["non.matching.remote"])
485
486 test "it accepts deletions even from rejected servers" do
487 clear_config([:mrf_simple, :reject], ["remote.instance"])
488
489 deletion_message = build_remote_deletion_message()
490
491 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
492 end
493
494 test "it accepts deletions even from non-whitelisted servers" do
495 clear_config([:mrf_simple, :accept], ["non.matching.remote"])
496
497 deletion_message = build_remote_deletion_message()
498
499 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
500 end
501 end
502
503 describe "when :reject_deletes has a matching host" do
504 setup do: clear_config([:mrf_simple, :reject_deletes], ["remote.instance"])
505
506 test "it rejects the deletion" do
507 deletion_message = build_remote_deletion_message()
508
509 assert {:reject, _} = SimplePolicy.filter(deletion_message)
510 end
511 end
512
513 describe "when :reject_deletes match with wildcard domain" do
514 setup do: clear_config([:mrf_simple, :reject_deletes], ["*.remote.instance"])
515
516 test "it rejects the deletion" do
517 deletion_message = build_remote_deletion_message()
518
519 assert {:reject, _} = SimplePolicy.filter(deletion_message)
520 end
521 end
522
523 defp build_local_message do
524 %{
525 "actor" => "#{Pleroma.Web.base_url()}/users/alice",
526 "to" => [],
527 "cc" => []
528 }
529 end
530
531 defp build_remote_message do
532 %{"actor" => "https://remote.instance/users/bob"}
533 end
534
535 defp build_remote_user do
536 %{
537 "id" => "https://remote.instance/users/bob",
538 "icon" => %{
539 "url" => "http://example.com/image.jpg",
540 "type" => "Image"
541 },
542 "image" => %{
543 "url" => "http://example.com/image.jpg",
544 "type" => "Image"
545 },
546 "type" => "Person"
547 }
548 end
549
550 defp build_remote_deletion_message do
551 %{
552 "type" => "Delete",
553 "actor" => "https://remote.instance/users/bob"
554 }
555 end
556 end