mrf/object_age: fix handling of non-public objects

Current logic unconditionally adds public adressing to "cc"
and follower adressing to "to" after attempting to strip it
from the other one. This creates serious problems:

First the bug prompting this investigation and fix,
unconditional addition creates duplicates when adressing
URIs already were in their intended final field; e.g.
this is prominently the case for all "unlisted" posts.
Since List.delete only removes the first occurence,
this then broke follower-adress stripping later on
making the policy ineffective.

It’s also just not safe in general wrt to non-public adressing:
e.g. pre-existing duplicates didn’t get fully stripped,
bespoke adressing modes with only one of public addressing
or follower addressing are mangled — and most importantly:
any belatedly received DM or follower-only post
also got public adressing added!
Shockingly this last point was actually asserted as "correct" in tests;
it appears to be a mistake from mindless match adjustments
while fixing crashes on nil adressing in
10c792110e.

Clean up this sloppy logic up, making sure no more duplicates are
added by us, all instances of relevant adresses are purged and only
readded when they actually existed to begin with.

(cherry picked from commit 932810c35ecba9d11fa4f11112c3444b62c45b65)
This commit is contained in:
Oneric 2024-11-16 00:43:38 +01:00 • committed by Lain Soykaf
commit 2a55239ca8
No known key found for this signature in database
2 changed files with 27 additions and 9 deletions

View file

@ -36,16 +36,34 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
end end
end end
@spec delete_and_count(list(), term()) :: {integer(), list()}
defp delete_and_count(list, element), do: delete_and_count(list, element, {0, [], list})
defp delete_and_count([], _element, {0, _nlist, olist}), do: {0, olist}
defp delete_and_count([], _element, {count, nlist, _olist}), do: {count, Enum.reverse(nlist)}
defp delete_and_count([h | r], h, {count, nlist, olist}),
do: delete_and_count(r, h, {count + 1, nlist, olist})
defp delete_and_count([h | r], element, {count, nlist, olist}),
do: delete_and_count(r, element, {count, [h | nlist], olist})
defp insert_if_needed(list, oldcount, element) do
if oldcount <= 0 || Enum.member?(list, element) do
list
else
[element | list]
end
end
defp check_delist(activity, actions) do defp check_delist(activity, actions) do
if :delist in actions do if :delist in actions do
with %User{} = user <- User.get_cached_by_ap_id(activity["actor"]) do with %User{} = user <- User.get_cached_by_ap_id(activity["actor"]) do
to = {pubcnt, to} = delete_and_count(activity["to"] || [], Pleroma.Constants.as_public())
List.delete(activity["to"] || [], Pleroma.Constants.as_public()) ++ {flwcnt, cc} = delete_and_count(activity["cc"] || [], user.follower_address)
[user.follower_address]
cc = cc = insert_if_needed(cc, pubcnt, Pleroma.Constants.as_public())
List.delete(activity["cc"] || [], user.follower_address) ++ to = insert_if_needed(to, flwcnt, user.follower_address)
[Pleroma.Constants.as_public()]
activity = activity =
activity activity
@ -67,8 +85,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
defp check_strip_followers(activity, actions) do defp check_strip_followers(activity, actions) do
if :strip_followers in actions do if :strip_followers in actions do
with %User{} = user <- User.get_cached_by_ap_id(activity["actor"]) do with %User{} = user <- User.get_cached_by_ap_id(activity["actor"]) do
to = List.delete(activity["to"] || [], user.follower_address) {_, to} = delete_and_count(activity["to"] || [], user.follower_address)
cc = List.delete(activity["cc"] || [], user.follower_address) {_, cc} = delete_and_count(activity["cc"] || [], user.follower_address)
activity = activity =
activity activity

View file

@ -79,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
{:ok, data} = ObjectAgePolicy.filter(data) {:ok, data} = ObjectAgePolicy.filter(data)
assert Visibility.get_visibility(%{data: data}) == "unlisted" assert Visibility.get_visibility(%{data: data}) == "direct"
end end
test "it delists an old post" do test "it delists an old post" do