diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a7ec1032876ec7c4aa4043bf095599749f68160..f6fc6aaee23c312a43f67b5210688b28e1060554 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Removed
+## 2.5.2
+
+### Security
+- `/proxy` endpoint now sets a Content-Security-Policy (sandbox)
+- WebSocket endpoint now respects unauthenticated restrictions for streams of public posts
+- OEmbed HTML tags are now filtered
+
+### Changed
+- docs: Be more explicit about the level of compatibility of OTP releases
+- Set default background worker timeout to 15 minutes
+
+### Fixed
+- Atom/RSS formatting (HTML truncation, published, missing summary)
+- Remove `static_fe` pipeline for `/users/:nickname/feed`
+- Stop oban from retrying if validating errors occur when processing incoming data
+- Make sure object refetching as used by already received polls follows MRF rules
+
+### Removed
+- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
+
## 2.5.1
### Added
diff --git a/changelog.d/3126.fix b/changelog.d/3126.fix
new file mode 100644
index 0000000000000000000000000000000000000000..91d396c89d4fbc9ee4c572006a56f376d309d241
--- /dev/null
+++ b/changelog.d/3126.fix
@@ -0,0 +1 @@
+MediaProxy responses now return a sandbox CSP header
diff --git a/changelog.d/3883.fix b/changelog.d/3883.fix
new file mode 100644
index 0000000000000000000000000000000000000000..6824f201345db5fd24305782dc6b842692216f4e
--- /dev/null
+++ b/changelog.d/3883.fix
@@ -0,0 +1 @@
+Fix abnormal behaviour when refetching a poll
diff --git a/changelog.d/3891.fix b/changelog.d/3891.fix
new file mode 100644
index 0000000000000000000000000000000000000000..f1fb62d826a3a3f5e442a9f2a585550975175570
--- /dev/null
+++ b/changelog.d/3891.fix
@@ -0,0 +1 @@
+OEmbed HTML tags are now filtered
diff --git a/changelog.d/fix-object-test.fix b/changelog.d/fix-object-test.fix
new file mode 100644
index 0000000000000000000000000000000000000000..5eea719f0bd89557ab2052d925036bf4802442bb
--- /dev/null
+++ b/changelog.d/fix-object-test.fix
@@ -0,0 +1 @@
+Correctly handle the situation when a poll has both "anyOf" and "oneOf" but one of them being empty
diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md
index 8c02201e67699c54b694ddef13d4e54520751c98..f2812346b1348cdf5c383e1812d841a4ffaaf10c 100644
--- a/docs/installation/otp_en.md
+++ b/docs/installation/otp_en.md
@@ -2,15 +2,16 @@
{! backend/installation/otp_vs_from_source.include !}
-This guide covers a installation using an OTP release. To install Pleroma from source, please check out the corresponding guide for your distro.
+This guide covers a installation using OTP releases as built by the Pleroma project, it is meant as a fallback to distribution packages/recipes which are the preferred installation method.
+To install Pleroma from source, please check out the corresponding guide for your distro.
## Pre-requisites
-* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
+* A machine you have root access to running Debian GNU/Linux or compatible (eg. Ubuntu), or Alpine on `x86_64`, `aarch64` or `armv7l` CPU. If you are not sure what you are running see [Detecting flavour section](#detecting-flavour) below
* A (sub)domain pointed to the machine
-You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo su`/`su`.
+You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo -i`/`su`.
-While in theory OTP releases are possbile to install on any compatible machine, for the sake of simplicity this guide focuses only on Debian/Ubuntu and Alpine.
+Similarly to other binaries, OTP releases tend to be only compatible with the distro they are built on, as such this guide focuses only on Debian/Ubuntu and Alpine.
### Detecting flavour
@@ -19,7 +20,7 @@ Paste the following into the shell:
arch="$(uname -m)";if [ "$arch" = "x86_64" ];then arch="amd64";elif [ "$arch" = "armv7l" ];then arch="arm";elif [ "$arch" = "aarch64" ];then arch="arm64";else echo "Unsupported arch: $arch">&2;fi;if getconf GNU_LIBC_VERSION>/dev/null;then libc_postfix="";elif [ "$(ldd 2>&1|head -c 9)" = "musl libc" ];then libc_postfix="-musl";elif [ "$(find /lib/libc.musl*|wc -l)" ];then libc_postfix="-musl";else echo "Unsupported libc">&2;fi;echo "$arch$libc_postfix"
```
-If your platform is supported the output will contain the flavour string, you will need it later. If not, this just means that we don't build releases for your platform, you can still try installing from source.
+This should give your flavour string. If not this just means that we don't build releases for your platform, you can still try installing from source.
### Installing the required packages
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index a9a9eeeed17adaacc51a5cd6927e5f0daa65e79d..cc3772563b8380af0da1a64ceba238ea144a05ac 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -8,77 +8,30 @@ defmodule Pleroma.Object.Fetcher do
alias Pleroma.Maps
alias Pleroma.Object
alias Pleroma.Object.Containment
- alias Pleroma.Repo
alias Pleroma.Signature
alias Pleroma.Web.ActivityPub.InternalFetchActor
+ alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.ObjectValidator
+ alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.Federator
require Logger
require Pleroma.Constants
- defp touch_changeset(changeset) do
- updated_at =
- NaiveDateTime.utc_now()
- |> NaiveDateTime.truncate(:second)
-
- Ecto.Changeset.put_change(changeset, :updated_at, updated_at)
- end
-
- defp maybe_reinject_internal_fields(%{data: %{} = old_data}, new_data) do
- has_history? = fn
- %{"formerRepresentations" => %{"orderedItems" => list}} when is_list(list) -> true
- _ -> false
- end
-
- internal_fields = Map.take(old_data, Pleroma.Constants.object_internal_fields())
-
- remote_history_exists? = has_history?.(new_data)
-
- # If the remote history exists, we treat that as the only source of truth.
- new_data =
- if has_history?.(old_data) and not remote_history_exists? do
- Map.put(new_data, "formerRepresentations", old_data["formerRepresentations"])
- else
- new_data
- end
-
- # If the remote does not have history information, we need to manage it ourselves
- new_data =
- if not remote_history_exists? do
- changed? =
- Pleroma.Constants.status_updatable_fields()
- |> Enum.any?(fn field -> Map.get(old_data, field) != Map.get(new_data, field) end)
-
- %{updated_object: updated_object} =
- new_data
- |> Object.Updater.maybe_update_history(old_data,
- updated: changed?,
- use_history_in_new_object?: false
- )
-
- updated_object
- else
- new_data
- end
-
- Map.merge(new_data, internal_fields)
- end
-
- defp maybe_reinject_internal_fields(_, new_data), do: new_data
-
@spec reinject_object(struct(), map()) :: {:ok, Object.t()} | {:error, any()}
- defp reinject_object(%Object{data: %{"type" => "Question"}} = object, new_data) do
+ defp reinject_object(%Object{data: %{}} = object, new_data) do
Logger.debug("Reinjecting object #{new_data["id"]}")
- with data <- maybe_reinject_internal_fields(object, new_data),
- {:ok, data, _} <- ObjectValidator.validate(data, %{}),
- changeset <- Object.change(object, %{data: data}),
- changeset <- touch_changeset(changeset),
- {:ok, object} <- Repo.insert_or_update(changeset),
- {:ok, object} <- Object.set_cache(object) do
- {:ok, object}
+ with {:ok, new_data, _} <- ObjectValidator.validate(new_data, %{}),
+ {:ok, new_data} <- MRF.filter(new_data),
+ {:ok, new_object, _} <-
+ Object.Updater.do_update_and_invalidate_cache(
+ object,
+ new_data,
+ _touch_changeset? = true
+ ) do
+ {:ok, new_object}
else
e ->
Logger.error("Error while processing object: #{inspect(e)}")
@@ -86,20 +39,11 @@ defp reinject_object(%Object{data: %{"type" => "Question"}} = object, new_data)
end
end
- defp reinject_object(%Object{} = object, new_data) do
- Logger.debug("Reinjecting object #{new_data["id"]}")
-
- with new_data <- Transmogrifier.fix_object(new_data),
- data <- maybe_reinject_internal_fields(object, new_data),
- changeset <- Object.change(object, %{data: data}),
- changeset <- touch_changeset(changeset),
- {:ok, object} <- Repo.insert_or_update(changeset),
- {:ok, object} <- Object.set_cache(object) do
+ defp reinject_object(_, new_data) do
+ with {:ok, object, _} <- Pipeline.common_pipeline(new_data, local: false) do
{:ok, object}
else
- e ->
- Logger.error("Error while processing object: #{inspect(e)}")
- {:error, e}
+ e -> e
end
end
diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex
index ab38d3ed2b65c843b9268e6b01cb214dbc26b3bb..b1e4870bac4c7900966a57d7dd6774606c0f6109 100644
--- a/lib/pleroma/object/updater.ex
+++ b/lib/pleroma/object/updater.ex
@@ -5,6 +5,9 @@
defmodule Pleroma.Object.Updater do
require Pleroma.Constants
+ alias Pleroma.Object
+ alias Pleroma.Repo
+
def update_content_fields(orig_object_data, updated_object) do
Pleroma.Constants.status_updatable_fields()
|> Enum.reduce(
@@ -97,12 +100,14 @@ def maybe_update_history(
end
defp maybe_update_poll(to_be_updated, updated_object) do
- choice_key = fn data ->
- if Map.has_key?(data, "anyOf"), do: "anyOf", else: "oneOf"
+ choice_key = fn
+ %{"anyOf" => [_ | _]} -> "anyOf"
+ %{"oneOf" => [_ | _]} -> "oneOf"
+ _ -> nil
end
with true <- to_be_updated["type"] == "Question",
- key <- choice_key.(updated_object),
+ key when not is_nil(key) <- choice_key.(updated_object),
true <- key == choice_key.(to_be_updated),
orig_choices <- to_be_updated[key] |> Enum.map(&Map.drop(&1, ["replies"])),
new_choices <- updated_object[key] |> Enum.map(&Map.drop(&1, ["replies"])),
@@ -237,4 +242,49 @@ def do_with_history(object, fun) do
{:history_items, e} -> e
end
end
+
+ defp maybe_touch_changeset(changeset, true) do
+ updated_at =
+ NaiveDateTime.utc_now()
+ |> NaiveDateTime.truncate(:second)
+
+ Ecto.Changeset.put_change(changeset, :updated_at, updated_at)
+ end
+
+ defp maybe_touch_changeset(changeset, _), do: changeset
+
+ def do_update_and_invalidate_cache(orig_object, updated_object, touch_changeset? \\ false) do
+ orig_object_ap_id = updated_object["id"]
+ orig_object_data = orig_object.data
+
+ %{
+ updated_data: updated_object_data,
+ updated: updated,
+ used_history_in_new_object?: used_history_in_new_object?
+ } = make_new_object_data_from_update_object(orig_object_data, updated_object)
+
+ changeset =
+ orig_object
+ |> Repo.preload(:hashtags)
+ |> Object.change(%{data: updated_object_data})
+ |> maybe_touch_changeset(touch_changeset?)
+
+ with {:ok, new_object} <- Repo.update(changeset),
+ {:ok, _} <- Object.invalid_object_cache(new_object),
+ {:ok, _} <- Object.set_cache(new_object),
+ # The metadata/utils.ex uses the object id for the cache.
+ {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(new_object.id) do
+ if used_history_in_new_object? do
+ with create_activity when not is_nil(create_activity) <-
+ Pleroma.Activity.get_create_by_object_ap_id(orig_object_ap_id),
+ {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(create_activity.id) do
+ nil
+ else
+ _ -> nil
+ end
+ end
+
+ {:ok, new_object, updated}
+ end
+ end
end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index a2152b945235892d7f87c3fb4b185a740bd4112e..fc5dec3628c7f5e0e19c2c91cdb4baa22f103a39 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -428,37 +428,13 @@ defp handle_update_object(
end
if orig_object_data["type"] in Pleroma.Constants.updatable_object_types() do
- %{
- updated_data: updated_object_data,
- updated: updated,
- used_history_in_new_object?: used_history_in_new_object?
- } = Object.Updater.make_new_object_data_from_update_object(orig_object_data, updated_object)
-
- changeset =
- orig_object
- |> Repo.preload(:hashtags)
- |> Object.change(%{data: updated_object_data})
-
- with {:ok, new_object} <- Repo.update(changeset),
- {:ok, _} <- Object.invalid_object_cache(new_object),
- {:ok, _} <- Object.set_cache(new_object),
- # The metadata/utils.ex uses the object id for the cache.
- {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(new_object.id) do
- if used_history_in_new_object? do
- with create_activity when not is_nil(create_activity) <-
- Pleroma.Activity.get_create_by_object_ap_id(orig_object_ap_id),
- {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(create_activity.id) do
- nil
- else
- _ -> nil
- end
- end
+ {:ok, _, updated} =
+ Object.Updater.do_update_and_invalidate_cache(orig_object, updated_object)
- if updated do
- object
- |> Activity.normalize()
- |> ActivityPub.notify_and_stream()
- end
+ if updated do
+ object
+ |> Activity.normalize()
+ |> ActivityPub.notify_and_stream()
end
end
diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex
index 449659f4bbe7b37a3c5f9d11b55fe7036d0d425f..034722eb2e7a7c99fcb1384132d81b6ed7dcd3f1 100644
--- a/lib/pleroma/web/feed/feed_view.ex
+++ b/lib/pleroma/web/feed/feed_view.ex
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.Feed.FeedView do
use Phoenix.HTML
use Pleroma.Web, :view
- alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.Gettext
@@ -72,7 +71,9 @@ def logo(user) do
def last_activity(activities), do: List.last(activities)
- def activity_title(%{"content" => content, "summary" => summary} = data, opts \\ %{}) do
+ def activity_title(%{"content" => content} = data, opts \\ %{}) do
+ summary = Map.get(data, "summary", "")
+
title =
cond do
summary != "" -> summary
@@ -81,9 +82,8 @@ def activity_title(%{"content" => content, "summary" => summary} = data, opts \\
end
title
- |> Pleroma.Web.Metadata.Utils.scrub_html()
- |> Pleroma.Emoji.Formatter.demojify()
- |> Formatter.truncate(opts[:max_length], opts[:omission])
+ |> Pleroma.Web.Metadata.Utils.scrub_html_and_truncate(opts[:max_length], opts[:omission])
+ |> HtmlEntities.encode()
end
def activity_description(data) do
diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
index d2ad62c13910fbb4a53630ff395089ce204ee8dc..bda5b36edcea2341d29052567b0b4638d3d41660 100644
--- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
@@ -12,6 +12,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
alias Pleroma.Web.MediaProxy
alias Plug.Conn
+ plug(:sandbox)
+
def remote(conn, %{"sig" => sig64, "url" => url64}) do
with {_, true} <- {:enabled, MediaProxy.enabled?()},
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
@@ -202,4 +204,9 @@ defp media_preview_proxy_config do
defp media_proxy_opts do
Config.get([:media_proxy, :proxy_opts], [])
end
+
+ defp sandbox(conn, _params) do
+ conn
+ |> merge_resp_headers([{"content-security-policy", "sandbox;"}])
+ end
end
diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex
index 15414a988d72ca36c2ea8e92f6b7528ce4d8df94..80a8be9a2d5e2db48f37d1ff809c1e565517a087 100644
--- a/lib/pleroma/web/metadata/utils.ex
+++ b/lib/pleroma/web/metadata/utils.ex
@@ -30,12 +30,13 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|> scrub_html_and_truncate_object_field(object)
end
- def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
+ def scrub_html_and_truncate(content, max_length \\ 200, omission \\ "...")
+ when is_binary(content) do
content
|> scrub_html
|> Emoji.Formatter.demojify()
|> HtmlEntities.decode()
- |> Formatter.truncate(max_length)
+ |> Formatter.truncate(max_length, omission)
end
def scrub_html(content) when is_binary(content) do
diff --git a/lib/pleroma/web/plugs/authentication_plug.ex b/lib/pleroma/web/plugs/authentication_plug.ex
index a7fd697b5d106a5101ea484d79ca05b3cbd3fc97..f912a1542f8a761cb4cee485528beddd2f0cc60c 100644
--- a/lib/pleroma/web/plugs/authentication_plug.ex
+++ b/lib/pleroma/web/plugs/authentication_plug.ex
@@ -38,10 +38,6 @@ def call(
def call(conn, _), do: conn
- def checkpw(password, "$6" <> _ = password_hash) do
- :crypt.crypt(password, password_hash) == password_hash
- end
-
def checkpw(password, "$2" <> _ = password_hash) do
# Handle bcrypt passwords for Mastodon migration
Bcrypt.verify_pass(password, password_hash)
@@ -60,10 +56,6 @@ def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
do_update_password(user, password)
end
- def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
- do_update_password(user, password)
- end
-
def maybe_update_password(user, _), do: {:ok, user}
defp do_update_password(user, password) do
diff --git a/lib/pleroma/web/rich_media/parsers/o_embed.ex b/lib/pleroma/web/rich_media/parsers/o_embed.ex
index 75318d9c7207bc0cf2890a5193bb0eeed6600418..0f303176ce849ad36587859edf7d1af73773edbe 100644
--- a/lib/pleroma/web/rich_media/parsers/o_embed.ex
+++ b/lib/pleroma/web/rich_media/parsers/o_embed.ex
@@ -6,8 +6,8 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html),
oembed_url when is_binary(oembed_url) <- get_oembed_url(elements),
- {:ok, oembed_data} <- get_oembed_data(oembed_url) do
- oembed_data
+ {:ok, oembed_data = %{"html" => html}} <- get_oembed_data(oembed_url) do
+ %{oembed_data | "html" => Pleroma.HTML.filter_tags(html)}
else
_e -> %{}
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index ba1d64ab23dbf9f21342c239d800708724e9498d..c1a690e28a59009292587d76176a80cd73715fb4 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -835,8 +835,7 @@ defmodule Pleroma.Web.Router do
end
scope "/", Pleroma.Web do
- # Note: html format is supported only if static FE is enabled
- pipe_through([:accepts_html_xml, :static_fe])
+ pipe_through([:accepts_html_xml])
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
end
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index 3c0da5c276d7aa0936511a9726d001405eee0243..b9a04cc7674408bd063cd0cb5c4d3ef8d055afb8 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -25,6 +25,7 @@ defmodule Pleroma.Web.Streamer do
def registry, do: @registry
@public_streams ["public", "public:local", "public:media", "public:local:media"]
+ @local_streams ["public:local", "public:local:media"]
@user_streams ["user", "user:notification", "direct", "user:pleroma_chat"]
@doc "Expands and authorizes a stream, and registers the process for streaming."
@@ -41,14 +42,37 @@ def get_topic_and_add_socket(stream, user, oauth_token, params \\ %{}) do
end
end
+ defp can_access_stream(user, oauth_token, kind) do
+ with {_, true} <- {:restrict?, Config.restrict_unauthenticated_access?(:timelines, kind)},
+ {_, %User{id: user_id}, %Token{user_id: user_id}} <- {:user, user, oauth_token},
+ {_, true} <-
+ {:scopes,
+ OAuthScopesPlug.filter_descendants(["read:statuses"], oauth_token.scopes) != []} do
+ true
+ else
+ {:restrict?, _} ->
+ true
+
+ _ ->
+ false
+ end
+ end
+
@doc "Expand and authorizes a stream"
@spec get_topic(stream :: String.t(), User.t() | nil, Token.t() | nil, Map.t()) ::
{:ok, topic :: String.t()} | {:error, :bad_topic}
def get_topic(stream, user, oauth_token, params \\ %{})
- # Allow all public steams.
- def get_topic(stream, _user, _oauth_token, _params) when stream in @public_streams do
- {:ok, stream}
+ # Allow all public steams if the instance allows unauthenticated access.
+ # Otherwise, only allow users with valid oauth tokens.
+ def get_topic(stream, user, oauth_token, _params) when stream in @public_streams do
+ kind = if stream in @local_streams, do: :local, else: :federated
+
+ if can_access_stream(user, oauth_token, kind) do
+ {:ok, stream}
+ else
+ {:error, :unauthorized}
+ end
end
# Allow all hashtags streams.
@@ -57,12 +81,20 @@ def get_topic("hashtag", _user, _oauth_token, %{"tag" => tag} = _params) do
end
# Allow remote instance streams.
- def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
- {:ok, "public:remote:" <> instance}
+ def get_topic("public:remote", user, oauth_token, %{"instance" => instance} = _params) do
+ if can_access_stream(user, oauth_token, :federated) do
+ {:ok, "public:remote:" <> instance}
+ else
+ {:error, :unauthorized}
+ end
end
- def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
- {:ok, "public:remote:media:" <> instance}
+ def get_topic("public:remote:media", user, oauth_token, %{"instance" => instance} = _params) do
+ if can_access_stream(user, oauth_token, :federated) do
+ {:ok, "public:remote:media:" <> instance}
+ else
+ {:error, :unauthorized}
+ end
end
# Expand user streams.
diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
index 260338772ac9823de69c4c85fe2eb7765bcd9b9d..b774f7984428c554757867b3d1934b7d24bc1547 100644
--- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
@@ -4,8 +4,8 @@