{{ title }}
Generated: {{ generated }}
Total hosts: {{ total_hosts }}  Matching expected: {{ ok_hosts }}  With issues: {{ hosts_with_issues|length }}
{% if not only_issues and ok_hosts == total_hosts and total_hosts > 0 %}
All hosts matched expected ports.
{% endif %} {% if only_issues and hosts_with_issues|length == 0 %}
No hosts with issues found. ✅
{% endif %} {# ===== Host Sections (LIGHT theme) ===== #} {# Reusable delta row #} {% macro delta_row(label, ports) -%} {{ label }}: {{ fmt_ports(ports) }} {%- endmacro %} {# ---------- 1) Issues first (already IP-sorted in Python) ---------- #} {% for hr in issues %} {% set ip_key = hr.ip %} {% set r = hr %} {% set host_row = host_results_by_ip.get(ip_key) %} {% set header_title = ip_key ~ ((' (' ~ host_row.host ~ ')') if host_row and host_row.host else '') %} {{ delta_row('Unexpected TCP open ports', r.unexpected_tcp) }} {{ delta_row('Expected TCP ports not seen', r.missing_tcp) }} {{ delta_row('Unexpected UDP open ports', r.unexpected_udp) }} {{ delta_row('Expected UDP ports not seen', r.missing_udp) }} {% if host_row and host_row.ports %} {% set ns = namespace(visible_rows=0) %} {% for p in host_row.ports %} {% set proto = (p.protocol|string|lower) %} {% set state = (p.state|string|lower) %} {% set skip_row = (proto == 'udp' and ('closed' in state)) %} {% set is_tcp = (proto == 'tcp') %} {% set is_udp = (proto == 'udp') %} {% set is_open = ('open' in state) %} {% set is_issue = (is_open and ( (is_tcp and (p.port in (r.unexpected_tcp or []))) or (is_udp and (p.port in (r.unexpected_udp or []))) )) %} {% set expectation_badge = ( badge('Issue', '#ef4444') if is_issue else (badge('Expected', '#16a34a') if is_open else '') ) %} {% if not skip_row %} {% set ns.visible_rows = ns.visible_rows + 1 %} {% endif %} {% endfor %} {% if ns.visible_rows == 0 %} {% endif %} {% else %} {% endif %}
{{ header_title }} {{ badge('ISSUES', '#ef4444') }}
{{ badge('ISSUES', '#ef4444') }}
Discovered Ports
Protocol Port State Service Expectation
{{ pill_proto(p.protocol) }} {{ p.port }} {{ pill_state(p.state) }} {{ p.service or '-' }} {{ expectation_badge | safe }}
No per-port details to display after filtering closed UDP results.
No per-port details available for this host.
{% endfor %} {# ---------- 2) Expected / OK (only if not only_issues) ---------- #} {% if not only_issues %} {% for hr in expected %} {% set ip_key = hr.ip %} {% set host_row = host_results_by_ip.get(ip_key) %} {% set header_title = ip_key ~ ((' (' ~ host_row.host ~ ')') if host_row and host_row.host else '') %}
{{ header_title }} {{ badge('OK', '#16a34a') }}
{{ badge('OK', '#16a34a') }}   Matches expected ports.
{% endfor %} {% endif %}
Report generated by mass-scan-v2 • {{ generated }}