Insert text representation of hashtags into object["hashtags"]
[akkoma] / test / pleroma / 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 followers_only: [],
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", "hashtags"], ["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", "hashtags"], ["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 "hashtags" => ["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 :followers_only" do
267 test "is empty" do
268 Config.put([:mrf_simple, :followers_only], [])
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 dm_activity = %{
294 "actor" => actor.ap_id,
295 "to" => [
296 following_user.ap_id,
297 non_following_user.ap_id
298 ],
299 "cc" => []
300 }
301
302 actor_domain =
303 activity
304 |> Map.fetch!("actor")
305 |> URI.parse()
306 |> Map.fetch!(:host)
307
308 Config.put([:mrf_simple, :followers_only], [actor_domain])
309
310 assert {:ok, new_activity} = SimplePolicy.filter(activity)
311 assert actor.follower_address in new_activity["cc"]
312 assert following_user.ap_id in new_activity["to"]
313 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"]
314 refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"]
315 refute non_following_user.ap_id in new_activity["to"]
316 refute non_following_user.ap_id in new_activity["cc"]
317
318 assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity)
319 assert new_dm_activity["to"] == [following_user.ap_id]
320 assert new_dm_activity["cc"] == []
321 end
322 end
323
324 describe "when :accept" do
325 test "is empty" do
326 Config.put([:mrf_simple, :accept], [])
327
328 local_message = build_local_message()
329 remote_message = build_remote_message()
330
331 assert SimplePolicy.filter(local_message) == {:ok, local_message}
332 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
333 end
334
335 test "is not empty but activity doesn't have a matching host" do
336 Config.put([:mrf_simple, :accept], ["non.matching.remote"])
337
338 local_message = build_local_message()
339 remote_message = build_remote_message()
340
341 assert SimplePolicy.filter(local_message) == {:ok, local_message}
342 assert {:reject, _} = SimplePolicy.filter(remote_message)
343 end
344
345 test "activity has a matching host" do
346 Config.put([:mrf_simple, :accept], ["remote.instance"])
347
348 local_message = build_local_message()
349 remote_message = build_remote_message()
350
351 assert SimplePolicy.filter(local_message) == {:ok, local_message}
352 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
353 end
354
355 test "activity matches with wildcard domain" do
356 Config.put([:mrf_simple, :accept], ["*.remote.instance"])
357
358 local_message = build_local_message()
359 remote_message = build_remote_message()
360
361 assert SimplePolicy.filter(local_message) == {:ok, local_message}
362 assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
363 end
364
365 test "actor has a matching host" do
366 Config.put([:mrf_simple, :accept], ["remote.instance"])
367
368 remote_user = build_remote_user()
369
370 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
371 end
372 end
373
374 describe "when :avatar_removal" do
375 test "is empty" do
376 Config.put([:mrf_simple, :avatar_removal], [])
377
378 remote_user = build_remote_user()
379
380 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
381 end
382
383 test "is not empty but it doesn't have a matching host" do
384 Config.put([:mrf_simple, :avatar_removal], ["non.matching.remote"])
385
386 remote_user = build_remote_user()
387
388 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
389 end
390
391 test "has a matching host" do
392 Config.put([:mrf_simple, :avatar_removal], ["remote.instance"])
393
394 remote_user = build_remote_user()
395 {:ok, filtered} = SimplePolicy.filter(remote_user)
396
397 refute filtered["icon"]
398 end
399
400 test "match with wildcard domain" do
401 Config.put([:mrf_simple, :avatar_removal], ["*.remote.instance"])
402
403 remote_user = build_remote_user()
404 {:ok, filtered} = SimplePolicy.filter(remote_user)
405
406 refute filtered["icon"]
407 end
408 end
409
410 describe "when :banner_removal" do
411 test "is empty" do
412 Config.put([:mrf_simple, :banner_removal], [])
413
414 remote_user = build_remote_user()
415
416 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
417 end
418
419 test "is not empty but it doesn't have a matching host" do
420 Config.put([:mrf_simple, :banner_removal], ["non.matching.remote"])
421
422 remote_user = build_remote_user()
423
424 assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
425 end
426
427 test "has a matching host" do
428 Config.put([:mrf_simple, :banner_removal], ["remote.instance"])
429
430 remote_user = build_remote_user()
431 {:ok, filtered} = SimplePolicy.filter(remote_user)
432
433 refute filtered["image"]
434 end
435
436 test "match with wildcard domain" do
437 Config.put([:mrf_simple, :banner_removal], ["*.remote.instance"])
438
439 remote_user = build_remote_user()
440 {:ok, filtered} = SimplePolicy.filter(remote_user)
441
442 refute filtered["image"]
443 end
444 end
445
446 describe "when :reject_deletes is empty" do
447 setup do: Config.put([:mrf_simple, :reject_deletes], [])
448
449 test "it accepts deletions even from rejected servers" do
450 Config.put([:mrf_simple, :reject], ["remote.instance"])
451
452 deletion_message = build_remote_deletion_message()
453
454 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
455 end
456
457 test "it accepts deletions even from non-whitelisted servers" do
458 Config.put([:mrf_simple, :accept], ["non.matching.remote"])
459
460 deletion_message = build_remote_deletion_message()
461
462 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
463 end
464 end
465
466 describe "when :reject_deletes is not empty but it doesn't have a matching host" do
467 setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"])
468
469 test "it accepts deletions even from rejected servers" do
470 Config.put([:mrf_simple, :reject], ["remote.instance"])
471
472 deletion_message = build_remote_deletion_message()
473
474 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
475 end
476
477 test "it accepts deletions even from non-whitelisted servers" do
478 Config.put([:mrf_simple, :accept], ["non.matching.remote"])
479
480 deletion_message = build_remote_deletion_message()
481
482 assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
483 end
484 end
485
486 describe "when :reject_deletes has a matching host" do
487 setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"])
488
489 test "it rejects the deletion" do
490 deletion_message = build_remote_deletion_message()
491
492 assert {:reject, _} = SimplePolicy.filter(deletion_message)
493 end
494 end
495
496 describe "when :reject_deletes match with wildcard domain" do
497 setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"])
498
499 test "it rejects the deletion" do
500 deletion_message = build_remote_deletion_message()
501
502 assert {:reject, _} = SimplePolicy.filter(deletion_message)
503 end
504 end
505
506 defp build_local_message do
507 %{
508 "actor" => "#{Pleroma.Web.base_url()}/users/alice",
509 "to" => [],
510 "cc" => []
511 }
512 end
513
514 defp build_remote_message do
515 %{"actor" => "https://remote.instance/users/bob"}
516 end
517
518 defp build_remote_user do
519 %{
520 "id" => "https://remote.instance/users/bob",
521 "icon" => %{
522 "url" => "http://example.com/image.jpg",
523 "type" => "Image"
524 },
525 "image" => %{
526 "url" => "http://example.com/image.jpg",
527 "type" => "Image"
528 },
529 "type" => "Person"
530 }
531 end
532
533 defp build_remote_deletion_message do
534 %{
535 "type" => "Delete",
536 "actor" => "https://remote.instance/users/bob"
537 }
538 end
539 end