logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/
commit: 2b76243ec82f2c246264f2a1f655b975f12e7290
parent a4e480a6368e20e26293fa88205b63d8e3bb837e
Author: Phantasm <phantasm@centrum.cz>
Date:   Wed,  3 Dec 2025 23:34:39 +0100

CommonAPI: Fail when user sends report with posts not visible to them

Diffstat:

Mlib/pleroma/web/api_spec/operations/report_operation.ex12+++++++++++-
Mlib/pleroma/web/common_api.ex19+++++++++++++++++++
Mlib/pleroma/web/common_api/activity_draft.ex2+-
Mlib/pleroma/web/mastodon_api/controllers/report_controller.ex6++++++
Mtest/pleroma/web/common_api_test.exs41+++++++++++++++++++++++++++++++++++++++++
Mtest/pleroma/web/mastodon_api/controllers/report_controller_test.exs47++++++++++++++++++++++++++++++++++++++++++++++-
6 files changed, 124 insertions(+), 3 deletions(-)

diff --git a/lib/pleroma/web/api_spec/operations/report_operation.ex b/lib/pleroma/web/api_spec/operations/report_operation.ex @@ -24,7 +24,17 @@ defmodule Pleroma.Web.ApiSpec.ReportOperation do requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ 200 => Operation.response("Report", "application/json", create_response()), - 400 => Operation.response("Report", "application/json", ApiError) + 400 => Operation.response("Report", "application/json", ApiError), + 404 => + Operation.response( + "Report", + "application/json", + %Schema{ + allOf: [ApiError], + title: "Report", + example: %{"error" => "Record not found"} + } + ) } } end diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex @@ -620,6 +620,7 @@ defmodule Pleroma.Web.CommonAPI do with {:ok, account} <- get_reported_account(data.account_id), {:ok, {content_html, _, _}} <- make_report_content_html(data[:comment]), {:ok, statuses} <- get_report_statuses(account, data), + true <- check_statuses_visibility(user, statuses), rules <- get_report_rules(Map.get(data, :rule_ids, nil)) do ActivityPub.flag(%{ context: Utils.generate_context_id(), @@ -630,9 +631,27 @@ defmodule Pleroma.Web.CommonAPI do forward: Map.get(data, :forward, false), rules: rules }) + else + false -> + {:error, :visibility} + + error -> + error end end + defp check_statuses_visibility(user, statuses) when is_list(statuses) do + visibility = for status <- statuses, do: Visibility.visible_for_user?(status, user) + + case Enum.all?(visibility) do + true -> true + _ -> false + end + end + + # There are no statuses associated with the report, pass! + defp check_statuses_visibility(_, status) when status == nil, do: true + defp get_reported_account(account_id) do case User.get_cached_by_id(account_id) do %User{} = account -> {:ok, account} diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex @@ -147,7 +147,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do # TODO: Fix this quirk in FE and remove here and other affected places with %Activity{} = activity <- Activity.get_by_id(id), true <- Visibility.visible_for_user?(activity, draft.user), - {:type, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do + {_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do %__MODULE__{draft | in_reply_to: activity} else nil -> diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -16,6 +16,12 @@ defmodule Pleroma.Web.MastodonAPI.ReportController do def create(%{assigns: %{user: user}, body_params: params} = conn, _) do with {:ok, activity} <- Pleroma.Web.CommonAPI.report(user, params) do render(conn, "show.json", activity: activity) + else + {:error, :visibility} -> + {:error, :not_found, "Record not found"} + + error -> + error end end end diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs @@ -1286,6 +1286,47 @@ defmodule Pleroma.Web.CommonAPITest do } = flag_activity end + test "doesn't create a report when post is not visible to user" do + reporter = insert(:user) + target_user = insert(:user) + {:ok, post} = CommonAPI.post(target_user, %{status: "Eric", visibility: "private"}) + + assert Pleroma.Web.ActivityPub.Visibility.private?(post) + refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(post, reporter) + + # Fails when all status are invisible + report_data = %{ + account_id: target_user.id, + comment: "foobar", + status_ids: [post.id] + } + + assert {:error, :visibility} = CommonAPI.report(reporter, report_data) + end + + test "doesn't create a report when some posts are not visible to user" do + reporter = insert(:user) + target_user = insert(:user) + + {:ok, visible_activity} = CommonAPI.post(target_user, %{status: "cofe"}) + + {:ok, invisibile_activity} = + CommonAPI.post(target_user, %{status: "cawfee", visibility: "private"}) + + assert Pleroma.Web.ActivityPub.Visibility.private?(invisibile_activity) + assert Pleroma.Web.ActivityPub.Visibility.public?(visible_activity) + refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(invisibile_activity, reporter) + + # Fails when some statuses are invisible + report_data_partial = %{ + account_id: target_user.id, + comment: "foobar", + status_ids: [visible_activity.id, invisibile_activity.id] + } + + assert {:error, :visibility} = CommonAPI.report(reporter, report_data_partial) + end + test "updates report state" do [reporter, target_user] = insert_pair(:user) activity = insert(:note_activity, user: target_user) diff --git a/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs @@ -147,7 +147,7 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do |> json_response_and_validate_schema(400) end - test "returns error when account is not exist", %{ + test "returns error when account does not exist", %{ conn: conn, activity: activity } do @@ -159,6 +159,51 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do assert json_response_and_validate_schema(conn, 400) == %{"error" => "Account not found"} end + test "returns not found when post isn't visible to reporter", %{user: target_user} do + %{conn: conn, user: reporter} = oauth_access(["write:reports"]) + + {:ok, invisible_activity} = + CommonAPI.post(target_user, %{status: "Invisible!", visibility: "private"}) + + assert Pleroma.Web.ActivityPub.Visibility.private?(invisible_activity) + refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(invisible_activity, reporter) + + assert %{"error" => "Record not found"} = + conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/v1/reports", + %{"account_id" => target_user.id, "status_ids" => [invisible_activity.id]} + ) + |> json_response_and_validate_schema(404) + end + + test "returns not found when some post aren't visible to reporter", %{ + activity: activity, + user: target_user + } do + %{conn: conn, user: reporter} = oauth_access(["write:reports"]) + + {:ok, invisible_activity} = + CommonAPI.post(target_user, %{status: "Invisible!", visibility: "private"}) + + assert Pleroma.Web.ActivityPub.Visibility.private?(invisible_activity) + assert Pleroma.Web.ActivityPub.Visibility.visible_for_user?(activity, reporter) + refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(invisible_activity, reporter) + + assert %{"error" => "Record not found"} = + conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/v1/reports", + %{ + "account_id" => target_user.id, + "status_ids" => [activity.id, invisible_activity.id] + } + ) + |> json_response_and_validate_schema(404) + end + test "doesn't fail if an admin has no email", %{conn: conn, target_user: target_user} do insert(:user, %{is_admin: true, email: nil})