logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/
commit: a5dbf8deadaf14cec4b28e4a6a018619758fcaf9
parent 8428a1bed38d461e45dddde19758020ab98044e8
Author: Lain Soykaf <lain@lain.com>
Date:   Wed, 27 Aug 2025 15:27:37 +0400

Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into lazarus

Diffstat:

M.gitlab-ci.yml10+++++-----
MDockerfile7++++---
Achangelog.d/admin-self-revocation.security2++
Achangelog.d/dockerfile-versions.change2++
Achangelog.d/moderation-log-unknown-actions.fix2++
Achangelog.d/repost-repeat-filtering-3391.add2++
Mdocs/development/API/differences_in_mastoapi_responses.md1+
Mlib/pleroma/moderation_log.ex6++++++
Mlib/pleroma/web/activity_pub/activity_pub.ex4++++
Mlib/pleroma/web/admin_api/controllers/admin_api_controller.ex8++++----
Mlib/pleroma/web/api_spec/operations/account_operation.ex6++++++
Mtest/pleroma/moderation_log_test.exs33+++++++++++++++++++++++++++++++++
Mtest/pleroma/web/activity_pub/activity_pub_test.exs10++++++++++
Mtest/pleroma/web/admin_api/controllers/admin_api_controller_test.exs30++++++++++++++++++++++++++++++
Mtest/pleroma/web/mastodon_api/controllers/account_controller_test.exs11+++++++++++
15 files changed, 122 insertions(+), 12 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml @@ -290,7 +290,7 @@ stop_review_app: amd64: stage: release image: - name: hexpm/elixir-amd64:1.17.3-erlang-26.2.5.6-ubuntu-focal-20241011 + name: hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 only: &release-only - stable@pleroma/pleroma - develop@pleroma/pleroma @@ -317,7 +317,7 @@ amd64: VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS DEBIAN_FRONTEND: noninteractive before_script: &before-release - - apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git + - apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git build-essential - echo "import Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force @@ -333,7 +333,7 @@ amd64-musl: artifacts: *release-artifacts only: *release-only image: - name: hexpm/elixir-amd64:1.17.3-erlang-26.2.5.6-alpine-3.17.9 + name: hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 tags: - amd64 cache: *release-cache @@ -377,7 +377,7 @@ arm64: tags: - arm image: - name: hexpm/elixir-arm64:1.17.3-erlang-26.2.5.6-ubuntu-focal-20241011 + name: hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 cache: *release-cache variables: *release-variables before_script: *before-release @@ -390,7 +390,7 @@ arm64-musl: tags: - arm image: - name: hexpm/elixir-arm64:1.17.3-erlang-26.2.5.6-alpine-3.17.9 + name: hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 cache: *release-cache variables: *release-variables before_script: *before-release-musl diff --git a/Dockerfile b/Dockerfile @@ -1,10 +1,10 @@ # https://hub.docker.com/r/hexpm/elixir/tags ARG ELIXIR_IMG=hexpm/elixir -ARG ELIXIR_VER=1.14.5 -ARG ERLANG_VER=25.3.2.14 +ARG ELIXIR_VER=1.17.3 +ARG ERLANG_VER=26.2.5.6 ARG ALPINE_VER=3.17.9 -FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build +FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} AS build COPY . . @@ -15,6 +15,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev vips-dev &&\ echo "import Config" > config/prod.secret.exs &&\ mix local.hex --force &&\ mix local.rebar --force &&\ + mix deps.clean --all &&\ mix deps.get --only prod &&\ mkdir release &&\ mix release --path release diff --git a/changelog.d/admin-self-revocation.security b/changelog.d/admin-self-revocation.security @@ -0,0 +1 @@ +Admin API: Fixed self-revocation vulnerability where admins could accidentally revoke their own admin status via the single-user permission endpoint +\ No newline at end of file diff --git a/changelog.d/dockerfile-versions.change b/changelog.d/dockerfile-versions.change @@ -0,0 +1 @@ +Update Dockerfile to use Elixir 1.17.3, Erlang 26.2.5.6, and Alpine 3.17.9 to match CI release builds +\ No newline at end of file diff --git a/changelog.d/moderation-log-unknown-actions.fix b/changelog.d/moderation-log-unknown-actions.fix @@ -0,0 +1 @@ +Fix ModerationLog FunctionClauseError for unknown actions +\ No newline at end of file diff --git a/changelog.d/repost-repeat-filtering-3391.add b/changelog.d/repost-repeat-filtering-3391.add @@ -0,0 +1 @@ +Add only_reblogs parameter to account statuses API for filtering to show only reblogs/reposts +\ No newline at end of file diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md @@ -88,6 +88,7 @@ The `id` parameter can also be the `nickname` of the user. This only works in th - `only_media`: include only statuses with media attached - `with_muted`: include statuses/reactions from muted accounts - `exclude_reblogs`: exclude reblogs +- `only_reblogs`: include only reblogs - `exclude_replies`: exclude replies - `exclude_visibilities`: exclude visibilities diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex @@ -575,6 +575,12 @@ defmodule Pleroma.ModerationLog do "@#{actor_nickname} requested account backup for @#{user_nickname}" end + def get_log_entry_message(%ModerationLog{data: data}) do + actor_name = get_in(data, ["actor", "nickname"]) || "unknown" + action = data["action"] || "unknown" + "@#{actor_name} performed action #{action}" + end + defp nicknames_to_string(nicknames) do nicknames |> Enum.map(&"@#{&1}") diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1065,6 +1065,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data)) end + defp restrict_reblogs(query, %{only_reblogs: true}) do + from(activity in query, where: fragment("?->>'type' = 'Announce'", activity.data)) + end + defp restrict_reblogs(query, _), do: query defp restrict_muted(query, %{with_muted: true}), do: query diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -240,6 +240,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do render_error(conn, :not_found, "No such permission_group") end + def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do + render_error(conn, :forbidden, "You can't revoke your own admin status.") + end + def right_delete( %{assigns: %{user: admin}} = conn, %{ @@ -265,10 +269,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do json(conn, fields) end - def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do - render_error(conn, :forbidden, "You can't revoke your own admin status.") - end - @doc "Get a password reset token (base64 string) for given nickname" def get_password_reset(conn, %{"nickname" => nickname}) do (%User{local: true} = user) = User.get_cached_by_nickname(nickname) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -143,6 +143,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Include statuses from muted accounts." ), Operation.parameter(:exclude_reblogs, :query, BooleanLike.schema(), "Exclude reblogs"), + Operation.parameter( + :only_reblogs, + :query, + BooleanLike.schema(), + "Include only reblogs" + ), Operation.parameter(:exclude_replies, :query, BooleanLike.schema(), "Exclude replies"), Operation.parameter( :exclude_visibilities, diff --git a/test/pleroma/moderation_log_test.exs b/test/pleroma/moderation_log_test.exs @@ -308,4 +308,37 @@ defmodule Pleroma.ModerationLogTest do assert log.data["message"] == "@#{moderator.nickname} deleted status ##{note.id}" end end + + describe "get_log_entry_message/1" do + setup do + moderator = insert(:user, is_moderator: true) + [moderator: moderator] + end + + test "handles unknown action types gracefully", %{moderator: moderator} do + log_entry = %ModerationLog{ + data: %{ + "actor" => %{"nickname" => moderator.nickname}, + "action" => "unknown_action", + "some_data" => "test_value" + } + } + + assert ModerationLog.get_log_entry_message(log_entry) =~ moderator.nickname + assert ModerationLog.get_log_entry_message(log_entry) =~ "unknown_action" + end + + test "handles malformed log entries gracefully" do + log_entry = %ModerationLog{ + data: %{ + "action" => "force_password_reset" + # Missing "actor" and "subject" fields + } + } + + message = ModerationLog.get_log_entry_message(log_entry) + assert is_binary(message) + assert message =~ "force_password_reset" + end + end end diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1270,6 +1270,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity == expected_activity end + test "includes only reblogs on request" do + user = insert(:user) + {:ok, _} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user}) + {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user}) + + [activity] = ActivityPub.fetch_user_activities(user, nil, %{only_reblogs: true}) + + assert activity == expected_activity + end + describe "irreversible filters" do setup do user = insert(:user) diff --git a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs @@ -321,6 +321,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert ModerationLog.get_log_entry_message(log_entry) == "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{user_two.nickname}" end + + test "/:right DELETE, admin cannot revoke their own admin status (single)", %{ + admin: admin, + conn: conn + } do + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users/#{admin.nickname}/permission_group/admin") + + assert json_response(conn, 403) == %{"error" => "You can't revoke your own admin status."} + end + + test "/:right DELETE, admin cannot revoke their own admin status (multiple)", %{ + admin: admin, + conn: conn + } do + user = insert(:user, is_admin: true) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users/permission_group/admin", %{ + nicknames: [admin.nickname, user.nickname] + }) + + assert json_response(conn, 403) == %{ + "error" => "You can't revoke your own admin/moderator status." + } + end end describe "/api/pleroma/admin/users/:nickname/password_reset" do diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -469,6 +469,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200) end + test "gets only a user's reblogs", %{user: user, conn: conn} do + {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"}) + {:ok, %{id: reblog_id}} = CommonAPI.repeat(post_id, user) + + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_reblogs=true") + assert [%{"id" => ^reblog_id}] = json_response_and_validate_schema(conn, 200) + + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_reblogs=1") + assert [%{"id" => ^reblog_id}] = json_response_and_validate_schema(conn, 200) + end + test "filters user's statuses by a hashtag", %{user: user, conn: conn} do {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"}) {:ok, _post} = CommonAPI.post(user, %{status: "hashtag"})