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