Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
commit
af6077b9c3
455 changed files with 78366 additions and 0 deletions
139
lib/tarakan_web/controllers/feed_controller.ex
Normal file
139
lib/tarakan_web/controllers/feed_controller.ex
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
defmodule TarakanWeb.FeedController do
|
||||
@moduledoc """
|
||||
Atom 1.0 feeds over the public record.
|
||||
|
||||
Both feeds reuse the anonymous disclosure rules: findings come from
|
||||
`Scans.list_recent_disclosed_findings/1` (listed repositories, fully public
|
||||
reviews, terminal trust states), infestations from `Infestations.list_infestations/1`
|
||||
(public canonical findings on listed repositories only).
|
||||
"""
|
||||
use TarakanWeb, :controller
|
||||
|
||||
alias Tarakan.Infestations
|
||||
alias Tarakan.Scans
|
||||
|
||||
@feed_limit 50
|
||||
@cache_control "public, max-age=300"
|
||||
|
||||
def findings(conn, _params) do
|
||||
base = TarakanWeb.Endpoint.url()
|
||||
|
||||
entries =
|
||||
[limit: @feed_limit]
|
||||
|> Scans.list_recent_disclosed_findings()
|
||||
|> Enum.map(fn %{canonical: canonical, repository: repository} = row ->
|
||||
url = base <> ~p"/findings/#{row.occurrence_public_id}"
|
||||
|
||||
%{
|
||||
id: url,
|
||||
link: url,
|
||||
title: canonical.title,
|
||||
updated: canonical.updated_at,
|
||||
summary:
|
||||
"#{String.capitalize(canonical.severity)} in " <>
|
||||
"#{repository.owner}/#{repository.name} (#{canonical.file_path}): " <>
|
||||
truncate(canonical.description, 240)
|
||||
}
|
||||
end)
|
||||
|
||||
send_feed(conn, "Tarakan findings", "#{base}/feeds/findings.xml", entries)
|
||||
end
|
||||
|
||||
def infestations(conn, _params) do
|
||||
base = TarakanWeb.Endpoint.url()
|
||||
|
||||
entries =
|
||||
[days: 30, limit: @feed_limit]
|
||||
|> Infestations.list_infestations()
|
||||
|> Enum.map(fn pattern ->
|
||||
url = base <> ~p"/infestations/#{pattern.pattern_key}"
|
||||
|
||||
%{
|
||||
id: url,
|
||||
link: url,
|
||||
title: pattern.title,
|
||||
updated: pattern.last_seen_at || pattern.first_seen_at || DateTime.utc_now(),
|
||||
summary:
|
||||
"Seen in #{pattern.repo_count} repositories over 30 days: " <>
|
||||
"#{pattern.open_count} open, #{pattern.verified_count} verified, " <>
|
||||
"#{pattern.fixed_count} fixed, #{pattern.disputed_count} disputed."
|
||||
}
|
||||
end)
|
||||
|
||||
send_feed(conn, "Tarakan infestations", "#{base}/feeds/infestations.xml", entries)
|
||||
end
|
||||
|
||||
defp send_feed(conn, title, self_url, entries) do
|
||||
base = TarakanWeb.Endpoint.url()
|
||||
|
||||
updated =
|
||||
entries |> Enum.map(& &1.updated) |> Enum.max(DateTime, fn -> DateTime.utc_now() end)
|
||||
|
||||
body = [
|
||||
~s(<?xml version="1.0" encoding="UTF-8"?>),
|
||||
~s(<feed xmlns="http://www.w3.org/2005/Atom">),
|
||||
"<title>",
|
||||
xml_escape(title),
|
||||
"</title>",
|
||||
"<id>",
|
||||
xml_escape(self_url),
|
||||
"</id>",
|
||||
~s(<link rel="self" href="),
|
||||
xml_escape(self_url),
|
||||
~s("/>),
|
||||
~s(<link rel="alternate" href="),
|
||||
xml_escape(base),
|
||||
~s("/>),
|
||||
"<updated>",
|
||||
rfc3339(updated),
|
||||
"</updated>",
|
||||
Enum.map(entries, &entry_xml/1),
|
||||
"</feed>"
|
||||
]
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/atom+xml")
|
||||
|> put_resp_header("cache-control", @cache_control)
|
||||
|> send_resp(200, body)
|
||||
end
|
||||
|
||||
defp entry_xml(entry) do
|
||||
[
|
||||
"<entry>",
|
||||
"<id>",
|
||||
xml_escape(entry.id),
|
||||
"</id>",
|
||||
"<title>",
|
||||
xml_escape(entry.title),
|
||||
"</title>",
|
||||
"<updated>",
|
||||
rfc3339(entry.updated),
|
||||
"</updated>",
|
||||
~s(<link href="),
|
||||
xml_escape(entry.link),
|
||||
~s("/>),
|
||||
"<summary>",
|
||||
xml_escape(entry.summary),
|
||||
"</summary>",
|
||||
"</entry>"
|
||||
]
|
||||
end
|
||||
|
||||
defp rfc3339(%DateTime{} = datetime) do
|
||||
datetime |> DateTime.truncate(:second) |> DateTime.to_iso8601()
|
||||
end
|
||||
|
||||
defp truncate(text, max) when byte_size(text) <= max, do: text
|
||||
defp truncate(text, max), do: String.slice(text, 0, max - 1) <> "…"
|
||||
|
||||
defp xml_escape(nil), do: ""
|
||||
|
||||
defp xml_escape(text) do
|
||||
text
|
||||
|> String.replace("&", "&")
|
||||
|> String.replace("<", "<")
|
||||
|> String.replace(">", ">")
|
||||
|> String.replace("\"", """)
|
||||
|> String.replace("'", "'")
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue