api: ensure only visible posts are interactable

Port of Akkoma PR 1014 with a few changes:
- comments regarding akkomafe changed to Pleroma-FE when applicable
- different error message for replying to/interacting with invisible post
  in Pleroma.Web.CommonAPI.ActivityDraft.in_reply_to/1
- split "doesn't do funny things to other users favs" test into three:
  - can't unfavourite post that isn't favourited
  - can't unfavourite other user's favs
  - can't unfavourite other user's favs using their activity
- switched order of args for some CommonAPI function since Akkoma hasn't
  backported our old change for that

Pleroma.Web.CommonAPI.ActivityDraft.in_reply_to/1 now refactored to use
`with` statement as in Akkoma. Some defp in_reply_to/1 were therefore removed

Original PR author: Oneric
Original commit message:
It doesn't make sense to like, react, reply, etc to something you cannot
see and is unexpected for the author of the interacted with post and
might make them believe the reacting user actually _can_ see the post.

Wrt to fav, reblog, reaction indexes the missing visibility check was
also leaking some (presumably/hopefully) low-severity data.

Add full-API test for all modes of interactions with private posts.
This commit is contained in:
Oneric 2025-12-01 00:06:13 +01:00 committed by Phantasm
commit 59fcb5c96e
No known key found for this signature in database
GPG key ID: 2669E588BCC634C8
11 changed files with 398 additions and 20 deletions

View file

@ -258,7 +258,7 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, _} = res ->
res
{:error, :not_found} = res ->
{:error, reason} = res when reason in [:not_found, :forbidden] ->
res
{:error, e} ->
@ -269,6 +269,7 @@ defmodule Pleroma.Web.CommonAPI do
defp favorite_helper(user, id) do
with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
{_, true} <- {:visible, Visibility.visible_for_user?(object, user)},
{_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
{_, {:ok, %Activity{} = activity, _meta}} <-
{:common_pipeline,
@ -278,6 +279,9 @@ defmodule Pleroma.Web.CommonAPI do
{:find_object, _} ->
{:error, :not_found}
{:visible, _} ->
{:error, :forbidden}
{:common_pipeline, {:error, {:validate, {:error, changeset}}}} = e ->
if {:object, {"already liked by this actor", []}} in changeset.errors do
{:ok, :already_liked}
@ -311,11 +315,15 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, Activity.t()} | {:error, String.t()}
def react_with_emoji(id, user, emoji) do
with %Activity{} = activity <- Activity.get_by_id(id),
{_, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
object <- Object.normalize(activity, fetch: false),
{:ok, emoji_react, _} <- Builder.emoji_react(user, object, emoji),
{:ok, activity, _} <- Pipeline.common_pipeline(emoji_react, local: true) do
{:ok, activity}
else
{:visible, _} ->
{:error, dgettext("errors", "Must be able to access post to interact with it")}
_ ->
{:error, dgettext("errors", "Could not add reaction emoji")}
end