fix(feed): complete Atom XML metadata

This commit is contained in:
Lain Soykaf 2026-07-24 21:04:35 +04:00
commit c5230d98c5
No known key found for this signature in database
6 changed files with 75 additions and 4 deletions

View file

@ -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

View file

@ -1,7 +1,7 @@
<entry>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<id><%= @data["id"] %></id>
<id><%= escape(@data["id"] || "") %></id>
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<content type="html"><%= escape(atom_content_encoded(@data)) %></content>
<published><%= to_rfc3339(@data["published"]) %></published>

View file

@ -4,7 +4,7 @@
<%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %>
<id><%= @data["id"] %></id>
<id><%= escape(@data["id"] || "") %></id>
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<content type="html"><%= escape(atom_content_encoded(@data)) %></content>
<published><%= to_rfc3339(@data["published"]) %></published>

View file

@ -13,7 +13,7 @@
<subtitle><%= subtitle %></subtitle>
<% end %>
<logo><%= feed_logo() %></logo>
<updated><%= most_recent_update(@activities) %></updated>
<updated><%= most_recent_update(@activities) || to_rfc3339(NaiveDateTime.utc_now()) %></updated>
<link rel="self" href="<%= feed_self_url(@conn) %>" type="application/atom+xml"/>
<%= for activity <- @activities do %>

View file

@ -232,6 +232,49 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
refute response =~ "<lastBuildDate>"
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&amp;second=two"
assert response =~ "third=three&amp;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"

View file

@ -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", "<script>alert('feed')</script>")
object
|> Ecto.Changeset.change(data: data)
|> Pleroma.Repo.update!()
response =
conn
|> get("/users/#{user.nickname}/feed.atom")
|> response(200)
assert response =~ "&amp;lt;script&amp;gt;alert(&amp;#39;feed&amp;#39;)&amp;lt;/script&amp;gt;"
content_values = response |> parse() |> xpath(~x"//entry/content/text()"sl)
assert Enum.any?(content_values, fn content ->
String.starts_with?(content, "&lt;script&gt;alert(&#39;feed&#39;)&lt;/script&gt;")
end)
end
test "does not expose sensitive attachments as enclosures", %{
conn: conn,
object: object,