{% from "partials/_macros.html" import card_header, verdict_pill %} {# File scan — the compiled YARA ruleset summary (what's loaded, from which sources, how many skipped) plus this run's matches. The ruleset lives only in the monitor process, so the monitor persists this summary for the read-only dashboard to render. #}
{{ card_header("File scan — YARA ruleset & matches") }} {# ===== Ruleset summary ===== #} {% set st = data.status if data else none %} {% if st and st.rules_loaded %} {# Width-aware summary: a stats column (metric tiles + sources) on the left, the category breakdown spanning the remaining two columns. #}
{# ----- left: ruleset stats ----- #}
{{ "{:,}".format(st.rules_loaded) }}
rules loaded
{{ "{:,}".format(st.files_loaded) }}
files
{{ "{:,}".format(st.files_skipped or 0) }}
skipped
{% if st.files_skipped and st.skip_reasons %}
{% for reason, n in st.skip_reasons.items() %}{{ n }} {{ reason }}{% if not loop.last %} · {% endif %}{% endfor %}
{% endif %}
{{ st.category_count or (st.top_categories | length) }}
categories
{% if st.sources %}
sources
{% for name, n in st.sources.items() %} {{ name }} {{ "{:,}".format(n) }} {% endfor %}
{% endif %} {% if st.compiled_at %}
compiled {{ st.compiled_at }}
{% endif %}
{# ----- right (2 cols): category breakdown, laid out 2-up ----- #} {% if st.top_categories %} {% set cat_max = (st.top_categories | map('last') | max) or 1 %}
top categories{% if st.category_count > st.top_categories|length %} (of {{ st.category_count }}){% endif %}
{% for name, n in st.top_categories %}
{{ name }}
{{ "{:,}".format(n) }}
{% endfor %}
{% endif %}
{% elif st %}
no YARA rules loaded — drop rules under the rules dir, or fetch a pack with scripts/update_rules.py.
{% else %}
the file scanner hasn't compiled a ruleset yet — wait for a collection cycle to complete.
{% endif %} {# ===== Coverage assessment (LLM: does the ruleset fit this host?) ===== #} {% set cov = data.coverage if data else none %} {% if cov %} {% set posture_class = {"well_covered": "text-emerald-300 border-emerald-700/60 bg-emerald-900/20", "partial": "text-amber-300 border-amber-700/60 bg-amber-900/20", "thin": "text-rose-300 border-rose-700/60 bg-rose-900/20"} %}
{{ cov.posture.replace("_", " ") }} ruleset coverage
{{ cov.headline }}
{% if cov.summary %}
{{ cov.summary }}
{% endif %} {% if cov.gaps %}
gaps
    {% for g in cov.gaps %}
  • {{ g.area }} {% if g.detail %} — {{ g.detail }}{% endif %}
  • {% endfor %}
{% endif %} {% if cov.recommendations %}
recommendations
    {% for r in cov.recommendations %}
  • {{ r.action }} {% if r.detail %} — {{ r.detail }}{% endif %}
  • {% endfor %}
{% endif %} {% if cov.created_at %}
assessed {{ cov.created_at }}
{% endif %}
{% endif %} {# ===== Matches ===== #} {% if data %}
{% endif %} {% if data and data.matches %}
{% for m in data.matches %} {% endfor %}
verdict rule path author source why
{% if m.verdict %}{{ verdict_pill(m.verdict, m.confidence) }} {% else %}{% endif %} {{ m.rule or "—" }} {{ m.path or "—" }} {{ m.author or "—" }} {{ m.scan_source or "—" }} {{ m.reasoning }}
{% elif data %}
no YARA matches{% if data.q or data.verdict %} for the current filters{% endif %} — a clean host with no rule hits looks exactly like this.
{% endif %}