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