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