From c5230d98c5bbb4ba70516f825fec4d1fb60d836d Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 24 Jul 2026 21:04:35 +0400 Subject: [PATCH] fix(feed): complete Atom XML metadata --- lib/pleroma/web/feed/feed_view.ex | 2 +- .../templates/feed/feed/_activity.atom.eex | 2 +- .../feed/feed/_tag_activity.atom.eex | 2 +- .../web/templates/feed/feed/tag.atom.eex | 2 +- test/pleroma/web/feed/tag_controller_test.exs | 43 +++++++++++++++++++ .../pleroma/web/feed/user_controller_test.exs | 28 ++++++++++++ 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index fc29db2b7..b409b1923 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -43,7 +43,7 @@ defmodule Pleroma.Web.Feed.FeedView do def most_recent_update(activities, :rss) do activities |> Enum.map(fn activity -> - case Object.normalize(activity, fetch: false) do + case activity.object do %Object{updated_at: updated_at} -> updated_at _ -> activity.updated_at end diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index 91ef947f7..f94ba0793 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -1,7 +1,7 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post - <%= @data["id"] %> + <%= escape(@data["id"] || "") %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= escape(atom_content_encoded(@data)) %> <%= to_rfc3339(@data["published"]) %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index 44c4806cf..6c74c2ab7 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -4,7 +4,7 @@ <%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %> - <%= @data["id"] %> + <%= escape(@data["id"] || "") %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> <%= escape(atom_content_encoded(@data)) %> <%= to_rfc3339(@data["published"]) %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index 1646aeec7..fb04544d3 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -13,7 +13,7 @@ <%= subtitle %> <% end %> <%= feed_logo() %> - <%= most_recent_update(@activities) %> + <%= most_recent_update(@activities) || to_rfc3339(NaiveDateTime.utc_now()) %> <%= for activity <- @activities do %> diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index a23c25274..35e29ed8c 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -232,6 +232,49 @@ defmodule Pleroma.Web.Feed.TagControllerTest do refute response =~ "" end + test "provides a valid updated timestamp for an empty Atom feed", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/atom+xml") + |> get(tag_feed_path(conn, :feed, "empty.atom")) + |> response(200) + + updated = response |> parse() |> xpath(~x"//feed/updated/text()"s) + assert {:ok, _datetime, 0} = DateTime.from_iso8601(updated) + end + + test "escapes Atom IDs and tag feed self links", %{conn: conn} do + user = insert(:user, local: false) + object_id = "https://remote.example/object?first=one&second=two" + + note = + insert(:note, + user: user, + object_local: false, + data: %{ + "content" => "remote #atomlink", + "external_url" => object_id, + "id" => object_id, + "summary" => "", + "tag" => ["atomlink"] + } + ) + + insert(:note_activity, user: user, note: note, local: false, object_local: false) + + response = + conn + |> put_req_header("accept", "application/atom+xml") + |> get("/tags/atomlink.atom?third=three&fourth=four") + |> response(200) + + assert response =~ "https://remote.example/object?first=one&second=two" + assert response =~ "third=three&fourth=four" + + xml = parse(response) + assert xpath(xml, ~x"//feed/entry/id/text()"s) == object_id + end + test "escapes and round-trips RSS item permalinks", %{conn: conn} do user = insert(:user, local: false) permalink = "https://remote.example/post?first=one&second=two" diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index b78ddb63a..458f5ec8d 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -150,6 +150,34 @@ defmodule Pleroma.Web.Feed.UserControllerTest do end end + test "keeps fallback summary markup inert in rendered Atom content", %{ + conn: conn, + object: object, + user: user + } do + data = + object.data + |> Map.put("content", "") + |> Map.put("summary", "") + + object + |> Ecto.Changeset.change(data: data) + |> Pleroma.Repo.update!() + + response = + conn + |> get("/users/#{user.nickname}/feed.atom") + |> response(200) + + assert response =~ "&lt;script&gt;alert(&#39;feed&#39;)&lt;/script&gt;" + + content_values = response |> parse() |> xpath(~x"//entry/content/text()"sl) + + assert Enum.any?(content_values, fn content -> + String.starts_with?(content, "<script>alert('feed')</script>") + end) + end + test "does not expose sensitive attachments as enclosures", %{ conn: conn, object: object,