{% from "partials/_macros.html" import card_header, pagination_bar %}
{% set verdict_cls = {
'malicious': 'bg-red-900/40 text-red-300 border border-red-900/60',
'suspicious': 'bg-amber-900/40 text-amber-300 border border-amber-900/60',
'unknown': 'bg-slate-700/40 text-slate-300 border border-slate-600',
'benign': 'bg-emerald-900/30 text-emerald-300 border border-emerald-900/50',
} %}
{% macro verdict_pill(row) %}
{% if row.verdict %}
{{ row.verdict }}{% if row.confidence is not none %}
{{ "%.0f"|format(row.confidence * 100) }}%
{% endif %}
{% else %}
—
{% endif %}
{% endmacro %}
{% macro subhead(title, c) %}
{{ card_header("persistence & tampering — SSH keys · hosts file · privileges") }}
{% if data %}
{# ----- filter bar ----- #}
{% endif %}
{% if data and data.any %}
{# ---------- SSH authorized keys ---------- #}
{{ subhead("SSH authorized keys", data.counts.ssh_keys) }}
{% if data.ssh_keys %}
verdict
account
type
fingerprint
comment
restrictions
{% for k in data.ssh_keys %}
{{ verdict_pill(k) }}
{{ k.owner }}
{{ k.key_type }}
{{ k.fingerprint or "—" }}
{{ k.comment or "—" }}
{{ k.options or "none" }}
{% endfor %}
{% if data.pagination.ssh.total_pages > 1 %}
← prev
{{ data.pagination.ssh.page }} / {{ data.pagination.ssh.total_pages }}
= data.pagination.ssh.total_pages %}disabled{% endif %}
hx-get="/fragments/persistence"
hx-target="#persistence"
hx-include="#persistence-filters"
hx-vals='{"ssh_page": {{ data.pagination.ssh.page + 1 }}}'>next →
{{ data.pagination.ssh.total }} total
{% endif %}
{% else %}
no authorized keys found
{% endif %}
{# ---------- /etc/hosts ---------- #}
{{ subhead("/etc/hosts mappings", data.counts.hosts) }}
{% if data.hosts %}
verdict
ip
hostnames
why
{% for hrow in data.hosts %}
{{ verdict_pill(hrow) }}
{{ hrow.ip }}
{{ hrow.hostnames }}
{{ hrow.reasoning }}
{% endfor %}
{% if data.pagination.hosts.total_pages > 1 %}
← prev
{{ data.pagination.hosts.page }} / {{ data.pagination.hosts.total_pages }}
= data.pagination.hosts.total_pages %}disabled{% endif %}
hx-get="/fragments/persistence"
hx-target="#persistence"
hx-include="#persistence-filters"
hx-vals='{"hosts_page": {{ data.pagination.hosts.page + 1 }}}'>next →
{{ data.pagination.hosts.total }} total
{% endif %}
{% else %}
no host mappings found
{% endif %}
{# ---------- privilege config ---------- #}
{{ subhead("privilege config", data.counts.privilege) }}
{% if data.privilege %}
verdict
kind
subject
detail
source
{% for p in data.privilege %}
{{ verdict_pill(p) }}
{{ p.kind }}
{{ p.subject }}
{{ p.detail }}
{{ p.source_path }}
{% endfor %}
{% if data.pagination.priv.total_pages > 1 %}
← prev
{{ data.pagination.priv.page }} / {{ data.pagination.priv.total_pages }}
= data.pagination.priv.total_pages %}disabled{% endif %}
hx-get="/fragments/persistence"
hx-target="#persistence"
hx-include="#persistence-filters"
hx-vals='{"priv_page": {{ data.pagination.priv.page + 1 }}}'>next →
{{ data.pagination.priv.total }} total
{% endif %}
{% else %}
no privilege entries found
{% endif %}
{% else %}
no persistence data yet — these collectors read SSH
authorized_keys ,
/etc/hosts and privilege config; run the
monitor (with sudo for full visibility)
and let a cycle complete.
{% endif %}