Initial commit on Forgejo
All checks were successful
CI and deploy / Test (push) Successful in 4m52s
CI and deploy / Deploy production (push) Successful in 23s

Fresh repository history for elektrine/tarakan hosted at
https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
Maxfield Luke 2026-07-29 04:43:40 -04:00
commit af6077b9c3
455 changed files with 78366 additions and 0 deletions

View file

@ -0,0 +1,343 @@
defmodule TarakanWeb.InfestationLive.Show do
@moduledoc "One cross-repo infestation pattern: affected repos and instances."
use TarakanWeb, :live_view
alias Tarakan.Fixes
alias Tarakan.Infestations
alias Tarakan.Policy
alias TarakanWeb.RepositoryPaths
@graph_limit 64
@ledger_page 50
@impl true
def mount(%{"pattern_key" => pattern_key}, _session, socket) do
infestation = Infestations.get_infestation(pattern_key)
if is_nil(infestation) or infestation.repo_count < 1 do
raise Ecto.NoResultsError, queryable: Tarakan.Scans.CanonicalFinding
end
graph = Infestations.list_pattern_repos_page(pattern_key, limit: @graph_limit)
ledger = Infestations.list_instances_page(pattern_key, limit: @ledger_page)
{:ok,
socket
|> assign(:page_title, infestation.title)
|> assign(
:meta_description,
"#{infestation.title}. Seen in #{infestation.repo_count} listed repositories on Tarakan."
)
|> assign(:canonical_path, ~p"/infestations/#{pattern_key}")
|> assign(:pattern_key, pattern_key)
|> assign(:infestation, infestation)
|> assign(:open_bounties, Tarakan.Market.open_bounties_for_target(:infestation, pattern_key))
|> assign(:graph_repos, graph.entries)
|> assign(:graph_hidden, max(infestation.repo_count - length(graph.entries), 0))
|> stream(:ledger_instances, ledger.entries, reset: true)
|> assign(:ledger_cursor, ledger.next_cursor)
|> assign(:ledger_empty?, ledger.entries == [])
|> assign(
:can_moderate,
Policy.allowed?(socket.assigns.current_scope, :moderate)
)
|> load_fix_template(pattern_key)}
end
# A pattern has a fix template only once one of its instances has actually
# been fixed, so the whole section is absent until then.
defp load_fix_template(socket, pattern_key) do
case Fixes.latest_template(pattern_key) do
nil ->
socket
|> assign(:fix_template, nil)
|> assign(:propagations, [])
|> assign(:propagation_targets, 0)
template ->
socket
|> assign(:fix_template, template)
|> assign(:propagations, Fixes.list_propagations(template))
|> assign(:propagation_targets, length(Fixes.propagation_targets(template)))
end
end
@impl true
def handle_event("propagate_fix", _params, socket) do
template = socket.assigns.fix_template
cond do
not socket.assigns.can_moderate ->
{:noreply, put_flash(socket, :error, "Not authorized.")}
is_nil(template) ->
{:noreply, put_flash(socket, :error, "No captured fix to propagate.")}
true ->
case Fixes.propagate(socket.assigns.current_scope, template) do
{:ok, %{opened: opened, failed: failed}} ->
{:noreply,
socket
|> put_flash(:info, "Opened #{opened} fix job(s). Failed #{failed}.")
|> load_fix_template(socket.assigns.pattern_key)}
{:error, :unauthorized} ->
{:noreply, put_flash(socket, :error, "Not authorized.")}
{:error, _reason} ->
{:noreply, put_flash(socket, :error, "Could not open fix jobs.")}
end
end
end
def handle_event("load_more_instances", _params, socket) do
case socket.assigns.ledger_cursor do
nil ->
{:noreply, socket}
cursor ->
page =
Infestations.list_instances_page(socket.assigns.pattern_key,
limit: @ledger_page,
cursor: cursor
)
{:noreply,
socket
|> stream(:ledger_instances, page.entries)
|> assign(:ledger_cursor, page.next_cursor)}
end
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope} current_path={assigns[:current_path]}>
<Layouts.page width={:wide}>
<.breadcrumbs id="infestation-breadcrumb">
<:crumb navigate={~p"/"}>registry</:crumb>
<:crumb navigate={~p"/infestations"}>infestations</:crumb>
<:crumb>pattern</:crumb>
</.breadcrumbs>
<TarakanWeb.BountyComponents.wanted_banner bounties={@open_bounties} />
<div class="mt-4 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div class="min-w-0 max-w-3xl">
<h1 class="font-display text-2xl font-medium leading-tight tracking-[0.02em] text-ink sm:text-4xl">
{@infestation.title}
</h1>
<p
:if={@infestation.severity}
class="mt-2 font-mono text-xs text-signal"
>
{@infestation.severity}
</p>
</div>
</div>
<div class="mt-6 grid gap-px border-2 border-strong bg-rule sm:grid-cols-4">
<div class="bg-ground px-4 py-4">
<p class="text-xs font-medium text-ink-faint">Repos</p>
<p id="infestation-repo-count" class="mt-1 font-display text-3xl tabular-nums text-ink">
{@infestation.repo_count}
</p>
</div>
<div class="bg-ground px-4 py-4">
<p class="text-xs font-medium text-ink-faint">Open</p>
<p class="mt-1 font-display text-3xl tabular-nums text-phosphor">
{@infestation.open_count}
</p>
</div>
<div class="bg-ground px-4 py-4">
<p class="text-xs font-medium text-ink-faint">Verified</p>
<p class="mt-1 font-display text-3xl tabular-nums text-ink">
{@infestation.verified_count}
</p>
</div>
<div class="bg-ground px-4 py-4">
<p class="text-xs font-medium text-ink-faint">Fixed</p>
<p class="mt-1 font-display text-3xl tabular-nums text-ink-muted">
{@infestation.fixed_count}
</p>
</div>
</div>
<%!--
The registry's payoff: one repository fixed this, so every other
repository carrying the pattern can start from that fix.
--%>
<section
:if={@fix_template}
id="infestation-fix-template"
class="mt-8 border-2 border-phosphor"
>
<div class="flex flex-wrap items-center gap-3 border-b border-rule px-4 py-3">
<h2 class="min-w-0 flex-1 font-display text-sm uppercase tracking-[0.12em] text-phosphor">
Fixed on
<.link
navigate={RepositoryPaths.repository_path(@fix_template.source_repository)}
class="hover:underline"
>
{@fix_template.source_repository.owner}/{@fix_template.source_repository.name}
</.link>
</h2>
<span
:if={@propagations != []}
class="font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint"
>
carried to {length(@propagations)} repo(s)
</span>
<button
:if={@can_moderate and @propagation_targets > 0}
id="infestation-propagate-fix"
type="button"
phx-click="propagate_fix"
data-confirm={"Open fix jobs on #{@propagation_targets} repository(s) still carrying this pattern?"}
class="shrink-0 bg-btn px-3 py-1.5 font-mono text-[11px] uppercase tracking-[0.12em] text-btn-fg transition hover:opacity-90"
>
Propagate to {@propagation_targets}
</button>
</div>
<p class="whitespace-pre-line px-4 py-3 text-sm leading-6 text-ink-muted" phx-no-format>{String.slice(@fix_template.summary, 0, 600)}</p>
<ul :if={@propagations != []} class="divide-y divide-rule border-t border-rule">
<li
:for={propagation <- @propagations}
id={"propagation-#{propagation.id}"}
class="flex flex-wrap items-baseline gap-x-3 gap-y-1 px-4 py-2.5"
>
<.link
navigate={RepositoryPaths.repository_path(propagation.repository)}
class="min-w-0 flex-1 truncate font-mono text-xs text-ink hover:text-signal hover:underline"
>
{propagation.repository.owner}/{propagation.repository.name}
</.link>
<.link
:if={propagation.review_task}
navigate={~p"/jobs/#{propagation.review_task.id}"}
class="shrink-0 font-mono text-[11px] text-signal hover:underline"
>
Job
</.link>
<span class={[
"shrink-0 font-mono text-[10px] uppercase tracking-[0.12em]",
propagation.status == "fixed" && "text-phosphor",
propagation.status == "stale" && "text-ink-faint",
propagation.status == "open" && "text-ink-muted"
]}>
{propagation.status}
</span>
</li>
</ul>
</section>
<section class="mt-8">
<h2 class="mb-3 font-display text-lg uppercase tracking-[0.02em] text-ink">
Affected repositories
</h2>
<.infestation_graph
id="infestation-graph"
infestation={@infestation}
repos={repo_rows(@graph_repos)}
pattern_key={@pattern_key}
/>
<p
:if={@graph_hidden > 0}
id="infestation-graph-more"
class="mt-3 font-mono text-[11px] text-ink-faint"
>
+{@graph_hidden} more repositories
</p>
</section>
<section id="infestation-instances" class="mt-10">
<h2 class="mb-3 font-display text-lg uppercase tracking-[0.02em] text-ink">
Instances
</h2>
<p :if={@ledger_empty?} class="mt-4 font-mono text-xs text-ink-faint">No instances.</p>
<ul
id="infestation-ledger"
phx-update="stream"
class="mt-4 divide-y divide-rule border-2 border-strong"
>
<li
:for={{dom_id, instance} <- @streams.ledger_instances}
id={dom_id}
class="grid gap-2 px-4 py-4 sm:grid-cols-[auto_1fr_auto] sm:items-center sm:px-6"
>
<span class={[
"size-2.5 shrink-0 rounded-full",
instance.status == "open" && "bg-phosphor",
instance.status == "verified" && "bg-ink",
instance.status == "fixed" && "bg-ink-muted",
instance.status not in ["open", "verified", "fixed"] && "bg-ink-faint"
]}></span>
<div class="min-w-0">
<p class="font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint">
<.link
:if={instance[:host]}
navigate={RepositoryPaths.repository_path(instance)}
class="text-ink-muted hover:text-signal hover:underline"
>
{instance.owner}/{instance.name}
</.link>
<span :if={is_nil(instance[:host])}>{instance.owner}/{instance.name}</span>
<span class="mx-1">·</span>
{instance.status}
<span :if={instance[:commit_sha]} class="mx-1">·</span>
<span :if={instance[:commit_sha]} title={instance.commit_sha}>
{String.slice(instance.commit_sha || "", 0, 7)}
</span>
</p>
<p class="mt-1 truncate font-mono text-xs text-ink-muted">
{instance.file_path}
</p>
</div>
<.link
:if={instance.occurrence_public_id}
navigate={~p"/findings/#{instance.occurrence_public_id}"}
class="font-mono text-xs text-signal hover:underline"
>
Open
</.link>
</li>
</ul>
<button
:if={@ledger_cursor}
id="infestation-load-more"
type="button"
phx-click="load_more_instances"
class="mt-4 bg-panel px-4 py-2 font-mono text-[11px] uppercase tracking-[0.12em] text-ink transition hover:border-signal"
>
Load more
</button>
</section>
</Layouts.page>
</Layouts.app>
"""
end
# Repo matrix expects instance-shaped maps (status, owner, name, occurrence_public_id, id).
# Rows are pattern_repos: one per repository, rolled up from the instances
# listed below. instance_count/open_count are what make this table worth
# showing separately - without them a repo hit twice looks like one hit once.
defp repo_rows(repos) do
Enum.map(repos, fn r ->
%{
id: r.repository_id,
host: r.host,
owner: r.owner,
name: r.name,
status: r.status,
occurrence_public_id: r.occurrence_public_id,
instance_count: r.instance_count,
open_count: r.open_count
}
end)
end
end