From 5a1fa6bca2cf67bcb70f1e2aee7a3b0ab3da159e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Jan 2022 11:21:49 -0600 Subject: [PATCH 1/3] CommonAPI: disallow quoting private posts through the API --- lib/pleroma/web/common_api/activity_draft.ex | 15 ++++++++++- .../web/common_api/activity_draft_test.exs | 26 +++++++++++++++++++ test/pleroma/web/common_api_test.exs | 14 ++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/pleroma/web/common_api/activity_draft_test.exs diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 5e16d2f44f..80d2bb860a 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do alias Pleroma.Conversation.Participation alias Pleroma.Object alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils @@ -57,6 +58,7 @@ def create(user, params) do |> with_valid(&in_reply_to_conversation/1) |> with_valid("e_post/1) |> with_valid(&visibility/1) + |> with_valid("ing_visibility/1) |> content() |> with_valid(&to_and_cc/1) |> with_valid(&context/1) @@ -131,7 +133,7 @@ defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} defp in_reply_to(draft), do: draft defp quote_post(%{params: %{quote_id: id}} = draft) when not_empty_string(id) do - case Activity.get_by_id(id) do + case Activity.get_by_id_with_object(id) do %Activity{actor: actor_ap_id} = activity when not_empty_string(actor_ap_id) -> %__MODULE__{draft | quote_post: activity, mentions: [actor_ap_id]} @@ -160,6 +162,17 @@ defp visibility(%{params: params} = draft) do end end + defp quoting_visibility(%{quote_post: %Activity{}} = draft) do + with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false), + visibility when visibility in ~w(public unlisted) <- Visibility.get_visibility(object) do + draft + else + _ -> add_error(draft, dgettext("errors", "Cannot quote private message")) + end + end + + defp quoting_visibility(draft), do: draft + defp expires_at(draft) do case CommonAPI.check_expiry_date(draft.params[:expires_in]) do {:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at} diff --git a/test/pleroma/web/common_api/activity_draft_test.exs b/test/pleroma/web/common_api/activity_draft_test.exs new file mode 100644 index 0000000000..8a09fc7102 --- /dev/null +++ b/test/pleroma/web/common_api/activity_draft_test.exs @@ -0,0 +1,26 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.CommonAPI.ActivityDraftTest do + use Pleroma.DataCase + + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.CommonAPI.ActivityDraft + + import Pleroma.Factory + + test "create/2 with a quote post" do + user = insert(:user) + + {:ok, direct} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) + {:ok, private} = CommonAPI.post(user, %{status: ".", visibility: "private"}) + {:ok, unlisted} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) + {:ok, public} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + + {:error, _} = ActivityDraft.create(user, %{status: "nice", quote_id: direct.id}) + {:error, _} = ActivityDraft.create(user, %{status: "nice", quote_id: private.id}) + {:ok, _} = ActivityDraft.create(user, %{status: "nice", quote_id: unlisted.id}) + {:ok, _} = ActivityDraft.create(user, %{status: "nice", quote_id: public.id}) + end +end diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index ec5bf9dedc..458a0325d6 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -722,6 +722,20 @@ test "quote posting with explicit addressing doesn't mention the OP" do assert Object.normalize(quote_post).data["to"] == [Pleroma.Constants.as_public()] end + + test "quote posting visibility" do + user = insert(:user) + + {:ok, direct} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) + {:ok, private} = CommonAPI.post(user, %{status: ".", visibility: "private"}) + {:ok, unlisted} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) + {:ok, public} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + + {:error, _} = CommonAPI.post(user, %{status: "nice", quote_id: direct.id}) + {:error, _} = CommonAPI.post(user, %{status: "nice", quote_id: private.id}) + {:ok, _} = CommonAPI.post(user, %{status: "nice", quote_id: unlisted.id}) + {:ok, _} = CommonAPI.post(user, %{status: "nice", quote_id: public.id}) + end end describe "reactions" do From 660490c2eca217beac41f0c6e19371c17f5c10d9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Jan 2022 11:49:31 -0600 Subject: [PATCH 2/3] StatusView: fix quote visibility --- .../web/mastodon_api/views/status_view.ex | 2 +- .../mastodon_api/views/status_view_test.exs | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 908567f1fe..e685cb9636 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -287,7 +287,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} quote_activity = get_quote(activity, opts) quote_post = - if quote_activity do + if visible_for_user?(quote_activity, opts[:for]) do quote_rendering_opts = Map.merge(opts, %{activity: quote_activity, show_quote: false}) render("show.json", quote_rendering_opts) else diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index d15b2d8208..d40e4fe8e0 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -400,6 +400,47 @@ test "a quote post" do assert status.pleroma.quote.id == to_string(quote_post.id) end + test "quoted private post" do + user = insert(:user) + + # Insert a private post + private = insert(:followers_only_note_activity, user: user) + private_object = Object.normalize(private) + + # Create a public post quoting the private post + quote_private = + insert(:note_activity, note: insert(:note, data: %{"quoteUrl" => private_object.data["id"]})) + + status = StatusView.render("show.json", %{activity: quote_private}) + + # The quote isn't rendered + refute status.pleroma.quote + assert status.pleroma.quote_url == private_object.data["id"] + + # After following the user, the quote is rendered + follower = insert(:user) + CommonAPI.follow(follower, user) + + status = StatusView.render("show.json", %{activity: quote_private, for: follower}) + assert status.pleroma.quote.id == to_string(private.id) + end + + test "quoted direct message" do + # Insert a direct message + direct = insert(:direct_note_activity) + direct_object = Object.normalize(direct) + + # Create a public post quoting the direct message + quote_direct = + insert(:note_activity, note: insert(:note, data: %{"quoteUrl" => direct_object.data["id"]})) + + status = StatusView.render("show.json", %{activity: quote_direct}) + + # The quote isn't rendered + refute status.pleroma.quote + assert status.pleroma.quote_url == direct_object.data["id"] + end + test "contains mentions" do user = insert(:user) mentioned = insert(:user) From 91822c383c20faf870874eb1812bc1f810b571b0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 26 Jan 2022 11:52:50 -0600 Subject: [PATCH 3/3] StatusView: add `quote_visible` param --- lib/pleroma/web/api_spec/schemas/status.ex | 4 ++++ lib/pleroma/web/mastodon_api/views/status_view.ex | 1 + test/pleroma/web/mastodon_api/views/status_view_test.exs | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index a3cc38b2d7..61e43f7a1c 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -188,6 +188,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do nullable: true, description: "URL of the quoted status" }, + quote_visible: %Schema{ + type: :boolean, + description: "`true` if the quoted post is visible to the user" + }, local: %Schema{ type: :boolean, description: "`true` if the post was made on the local instance" diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index e685cb9636..89ad2e1d9c 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -402,6 +402,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} in_reply_to_account_acct: reply_to_user && reply_to_user.nickname, quote: quote_post, quote_url: object.data["quoteUrl"], + quote_visible: visible_for_user?(quote_activity, opts[:for]), content: %{"text/plain" => content_plaintext}, spoiler_text: %{"text/plain" => summary}, expires_at: expires_at, diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index d40e4fe8e0..446ade626a 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -282,6 +282,7 @@ test "a note activity" do in_reply_to_account_acct: nil, quote: nil, quote_url: nil, + quote_visible: false, content: %{"text/plain" => HTML.strip_tags(object_data["content"])}, spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])}, expires_at: nil, @@ -416,6 +417,7 @@ test "quoted private post" do # The quote isn't rendered refute status.pleroma.quote assert status.pleroma.quote_url == private_object.data["id"] + refute status.pleroma.quote_visible # After following the user, the quote is rendered follower = insert(:user) @@ -423,6 +425,7 @@ test "quoted private post" do status = StatusView.render("show.json", %{activity: quote_private, for: follower}) assert status.pleroma.quote.id == to_string(private.id) + assert status.pleroma.quote_visible end test "quoted direct message" do @@ -439,6 +442,7 @@ test "quoted direct message" do # The quote isn't rendered refute status.pleroma.quote assert status.pleroma.quote_url == direct_object.data["id"] + refute status.pleroma.quote_visible end test "contains mentions" do