{# Macro for rendering dicts recursively #}
{% macro render_dict(data, indent=0) -%}
{%- for k, v in data.items() -%}
{{ k|replace('_', ' ')|title }}:
{%- if v is mapping -%}
{{- render_dict(v, 0) -}}
{%- elif v is iterable and v is not string -%}
{%- if v -%}
{%- for item in v -%}
{{ item }}{% if not loop.last %}, {% endif %}
{%- endfor -%}
{%- else -%}
empty
{%- endif -%}
{%- else -%}
{{ v }}
{%- endif %}
{% if port.service.product %}
{{ port.service.product }}
{% if port.service.version %} v{{ port.service.version }}{% endif %}
{% endif %}
{% if port.extrainfo %} | {{ port.extrainfo }}{% endif %}
{{ port.state }}
🔍 Port Summary
AI-generated analysis will appear here.
{% if port.scripts %}
📜 Nmap Scripts
{% for script_name, script_output in port.scripts.items() %}
{{ script_name }}
{{ script_output if script_output is string }}
{% endfor %}
{% endif %}
{%- if port.plugins %}
🔧 Plugin Results
{%- for plugin_name, plugin_data in port.plugins.items() %}
{{ plugin_name|replace('_', ' ')|title }}[+]
{%- if plugin_data.cmd %}
Command:
{%- if plugin_data.cmd is iterable and plugin_data.cmd is not string -%}
{%- for cmd in plugin_data.cmd -%}
{{ cmd }}
{%- endfor -%}
{%- else -%}
{{ plugin_data.cmd }}
{%- endif %}
{%- endif -%}
{%- if plugin_data.results %}
Results:
{%- if plugin_data.report_fields -%}
{%- for key in plugin_data.report_fields -%}
{%- if key in plugin_data.results -%}
{{ key|replace('_', ' ')|title }}:
{%- set value = plugin_data.results[key] -%}
{%- if value is mapping -%}
{{ render_dict(value) }}
{%- elif value is iterable and value is not string -%}
{%- for item in value -%}
{{ item }}
{%- endfor -%}
{%- else -%}
{{ value }}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- elif plugin_data.results is mapping -%}
{{ render_dict(plugin_data.results) }}
{%- elif plugin_data.results is iterable and plugin_data.results is not string -%}