From d390c4b491cd9350caf7db5f560e7d4a4dfa301d Mon Sep 17 00:00:00 2001 From: Phillip Tarrant Date: Fri, 14 Nov 2025 01:43:59 +0000 Subject: [PATCH] Add HTML report generation with dark theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements comprehensive HTML report generation from JSON scan data with Jinja2 templates. Reports feature a dark slate theme with summary dashboard, drift alerts, security warnings, and expandable service details. Features: - Dark theme HTML reports with slate/grey color scheme - Summary dashboard: scan statistics, drift alerts, security warnings - Site-by-site breakdown with IP grouping and status badges - Expandable service details and SSL/TLS certificate information - Visual badges: green (expected), red (unexpected), yellow (missing) - UDP port handling: shows expected, unexpected, and missing UDP ports - Screenshot links with relative paths for portability - Optimized hover effects for table rows - Standalone HTML output (no external dependencies) Technical changes: - Added src/report_generator.py: HTMLReportGenerator class with summary calculations - Added templates/report_template.html: Jinja2 template for dynamic reports - Added templates/report_mockup.html: Static mockup for design testing - Updated requirements.txt: Added Jinja2==3.1.2 - Updated README.md: Added HTML report generation section with usage and features - Updated CLAUDE.md: Added implementation details, usage guide, and troubleshooting Usage: python3 src/report_generator.py output/scan_report.json šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CLAUDE.md | 199 +++-- README.md | 91 +- requirements.txt | 1 + src/report_generator.py | 327 ++++++++ templates/report_mockup.html | 1424 ++++++++++++++++++++++++++++++++ templates/report_template.html | 949 +++++++++++++++++++++ 6 files changed, 2933 insertions(+), 58 deletions(-) create mode 100755 src/report_generator.py create mode 100644 templates/report_mockup.html create mode 100644 templates/report_template.html diff --git a/CLAUDE.md b/CLAUDE.md index fcd00dc..986fa65 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,11 +61,21 @@ python3 -c "import yaml; yaml.safe_load(open('configs/example-site.yaml'))" - `_get_screenshot_dir()`: Creates screenshots subdirectory - `_generate_filename()`: Generates filename for screenshot (IP_PORT.png) -3. **configs/** - YAML configuration files +3. **src/report_generator.py** - HTML report generator + - `HTMLReportGenerator` class: Generates comprehensive HTML reports from JSON scan data + - `generate_report()`: Main method to generate HTML report with summary dashboard and service details + - `_load_json_report()`: Loads and parses JSON scan report + - `_calculate_summary_stats()`: Calculates scan statistics for dashboard (IPs, ports, services, screenshots) + - `_identify_drift_alerts()`: Identifies unexpected/missing ports and services + - `_identify_security_warnings()`: Identifies security issues (expiring certs, weak TLS, self-signed certs) + - `_format_date()`: Helper to format ISO date strings + - `_format_duration()`: Helper to format scan duration + +4. **configs/** - YAML configuration files - Define scan title, sites, IPs, and expected network behavior - Each IP includes expected ping response and TCP/UDP ports -4. **output/** - JSON scan reports and screenshots +5. **output/** - JSON scan reports and screenshots - Timestamped JSON files: `scan_report_YYYYMMDD_HHMMSS.json` - Screenshot directory: `scan_report_YYYYMMDD_HHMMSS_screenshots/` - Contains actual vs. expected comparison for each IP @@ -266,6 +276,41 @@ JSON structure defined in src/scanner.py:365+. To modify: 3. Update README.md output format documentation 4. Update example output in both README.md and CLAUDE.md +### Generating HTML Reports + +**Basic usage:** +```bash +# Generate HTML report from most recent JSON scan +python3 src/report_generator.py output/scan_report_20251113_175235.json +``` + +**Custom output location:** +```bash +# Specify output filename +python3 src/report_generator.py output/scan.json reports/my_report.html +``` + +**Programmatic usage:** +```python +from src.report_generator import HTMLReportGenerator + +generator = HTMLReportGenerator('output/scan_report.json') +html_path = generator.generate_report('custom_output.html') +print(f"Report generated: {html_path}") +``` + +**Customizing the HTML template:** +- Edit `templates/report_template.html` to modify layout, colors, or content +- Template uses Jinja2 syntax with variables like `{{ title }}`, `{% for site in sites %}` +- CSS is embedded in the template for portability (single file output) +- Test design changes with `templates/report_mockup.html` first + +**Output:** +- Standalone HTML file (no external dependencies) +- Can be opened directly in any browser +- Dark theme optimized for readability +- Screenshot links are relative paths (keep output directory structure intact) + ### Customizing Screenshot Capture **Change viewport size** (src/screenshot_capture.py:35): @@ -339,53 +384,69 @@ Optimization strategies: - Adjust screenshot timeout (currently 15 seconds in src/screenshot_capture.py:34) - Disable screenshot capture for faster scans (set screenshot_capture to None) +## HTML Report Generation (āœ… Implemented) + +SneakyScanner now includes comprehensive HTML report generation from JSON scan data. + +**Usage:** +```bash +# Generate HTML report from JSON scan output +python3 src/report_generator.py output/scan_report_20251113_175235.json + +# Specify custom output path +python3 src/report_generator.py output/scan.json custom_report.html +``` + +**Implemented Features:** +- āœ… Dark theme with slate/grey color scheme for easy reading +- āœ… Summary dashboard with scan statistics, drift alerts, and security warnings +- āœ… Site-by-site breakdown with IP grouping +- āœ… Service details with expandable cards (click to expand) +- āœ… Visual badges for expected vs. unexpected services (green/red/yellow) +- āœ… SSL/TLS certificate details with expandable sections +- āœ… TLS version support display with cipher suites +- āœ… Certificate expiration warnings (highlighted for <30 days) +- āœ… Weak TLS version detection (TLS 1.0/1.1) +- āœ… Self-signed certificate identification +- āœ… Screenshot links (referenced, not embedded) +- āœ… Missing expected services clearly marked +- āœ… UDP port handling: + - Expected UDP ports shown with green "Expected" badge + - Unexpected UDP ports shown with red "Unexpected" badge + - Missing expected UDP ports shown with yellow "Missing" badge + - Note displayed that service detection not available for UDP +- āœ… Responsive layout for different screen sizes +- āœ… No JavaScript dependencies - pure HTML/CSS with minimal vanilla JS +- āœ… Optimized hover effects for table rows (lighter background + blue left border) + +**Template Architecture:** +- `templates/report_template.html` - Jinja2 template for dynamic report generation +- `templates/report_mockup.html` - Static mockup for design testing +- Custom CSS with dark slate theme (#0f172a background, #60a5fa accents) +- Expandable service details and SSL/TLS sections +- Color-coded status badges (expected=green, unexpected=red, missing=yellow) + +**HTMLReportGenerator Class** (`src/report_generator.py`): +- Loads JSON scan reports +- Calculates summary statistics (IPs, ports, services, screenshots) +- Identifies drift alerts (unexpected/missing ports and services) +- Identifies security warnings (expiring certs, weak TLS, self-signed certs, high ports) +- Renders Jinja2 template with context data +- Outputs standalone HTML file (no server required) + +**Future Enhancements for HTML Reports:** +- Sortable/filterable tables with JavaScript +- Timeline view of scan history +- Scan comparison (diff between two reports) +- Export to PDF capability +- Interactive charts/graphs for trends +- Embedded screenshot thumbnails (currently links only) + ## Planned Features (Future Development) The following features are planned for future implementation: -### 1. HTML Report Generation -Build comprehensive HTML reports from JSON scan data with interactive visualizations. - -**Report Features:** -- Service details and SSL/TLS information tables -- Visual comparison of expected vs. actual results (red/green highlighting) -- Certificate expiration warnings with countdown timers -- TLS version compliance reports (highlight weak configurations) -- Embedded webpage screenshots -- Sortable/filterable tables -- Timeline view of scan history -- Export to PDF capability - -**Implementation Considerations:** -- Template engine: Jinja2 or similar -- CSS framework: Bootstrap or Tailwind for responsive design -- Charts/graphs: Chart.js or Plotly for visualizations -- Store templates in `templates/` directory -- Generate static HTML that can be opened without server - -**Architecture:** -```python -class HTMLReportGenerator: - def __init__(self, json_report_path, template_dir='templates'): - pass - - def generate_report(self, output_path): - # Parse JSON - # Render template with data - # Include screenshots - # Write HTML file - pass - - def _compare_expected_actual(self, expected, actual): - # Generate diff/comparison data - pass - - def _generate_cert_warnings(self, services): - # Identify expiring certs, weak TLS, etc. - pass -``` - -### 2. Comparison Reports (Scan Diffs) +### 1. Comparison Reports (Scan Diffs) Generate reports showing changes between scans over time. **Features:** @@ -409,18 +470,21 @@ Generate reports showing changes between scans over time. - python-libnmap==0.7.3 (nmap XML parsing) - sslyze==6.0.0 (SSL/TLS analysis) - playwright==1.40.0 (webpage screenshot capture) -- Built-in: socket, ssl, subprocess, xml.etree.ElementTree, logging +- Jinja2==3.1.2 (HTML report template engine) +- Built-in: socket, ssl, subprocess, xml.etree.ElementTree, logging, json, pathlib, datetime - System: chromium, chromium-driver (installed via Dockerfile) -### For HTML Reports, Will Need: -- Jinja2 (template engine) -- Optional: weasyprint or pdfkit for PDF export +### For Future Enhancements, May Need: +- weasyprint or pdfkit for PDF export +- Chart.js or Plotly for interactive visualizations ### Key Files to Modify for New Features: 1. **src/scanner.py** - Core scanning logic (add new phases/methods) 2. **src/screenshot_capture.py** - āœ… Implemented: Webpage screenshot capture module -3. **src/report_generator.py** - New file for HTML report generation (planned) -4. **templates/** - New directory for HTML templates (planned) +3. **src/report_generator.py** - āœ… Implemented: HTML report generation with Jinja2 templates +4. **templates/** - āœ… Implemented: Jinja2 templates for HTML reports + - `report_template.html` - Main report template with dark theme + - `report_mockup.html` - Static mockup for design testing 5. **requirements.txt** - Add new dependencies 6. **Dockerfile** - Install additional system dependencies (browsers, etc.) @@ -475,6 +539,39 @@ Generate reports showing changes between scans over time. - **Check**: System certificates up to date in container - **Solution**: This should not happen; file an issue if it does +### HTML Report Generation Issues + +**Problem**: "Template not found" error +- **Check**: Verify `templates/report_template.html` exists +- **Check**: Running script from correct directory (project root) +- **Solution**: Ensure template directory structure is intact: `templates/report_template.html` + +**Problem**: Report generated but looks broken/unstyled +- **Check**: Browser opens HTML file correctly (not viewing source) +- **Check**: CSS is embedded in template (should be in ` + + +
+ +
+

Production Infrastructure Scan

+
+ šŸ“… Scan Time: 2025-11-14 10:30:00 UTC + ā±ļø Duration: 127.5 seconds + šŸ“„ Config: /app/configs/production.yaml +
+
+ + +
+

Scan Summary

+
+ +
+

Scan Statistics

+
+
+ Total IPs Scanned + 5 +
+
+ TCP Ports Found + 47 +
+
+ UDP Ports Found + 3 +
+
+ Services Identified + 42 +
+
+ Web Services + 12 +
+
+ Screenshots Captured + 11 +
+
+
+ + +
+

Drift Alerts

+
+
+ Unexpected TCP Ports + 4 +
+
+ Missing Expected Services + 2 +
+
+ New Services Detected + 3 +
+
+ Unexpected UDP Ports + 1 +
+
+
+ + +
+

Security Warnings

+
+
+ Certificates Expiring Soon (<30 days) + 2 +
+
+ Weak TLS Versions (1.0/1.1) + 1 +
+
+ Self-Signed Certificates + 3 +
+
+ High Port Services (>10000) + 5 +
+
+
+
+
+ + +
+ + + +
+
+

192.168.1.10

+
+ Ping: Expected + 2 Unexpected Ports +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortProtocolServiceProductStatus
22TCPsshOpenSSHExpected
+
+
+
+ Product + OpenSSH +
+
+ Version + 8.2p1 Ubuntu 4ubuntu0.5 +
+
+ OS Type + Linux +
+
+ Extra Info + protocol 2.0 +
+
+
+
80TCPhttpnginxExpected
+
+
+
+ Product + nginx +
+
+ Version + 1.18.0 (Ubuntu) +
+
+ Protocol + HTTP +
+
+ + šŸ–¼ļø View Screenshot + +
+
443TCPhttpsnginxExpected
+
+
+
+ Product + nginx +
+
+ Version + 1.18.0 +
+
+ Protocol + HTTPS +
+
+ + šŸ–¼ļø View Screenshot + + + +
+
+

šŸ”’ SSL/TLS Details

+ Click to expand ā–¼ +
+
+ +
Certificate Information
+
+
+ Subject + CN=prod.example.com +
+
+ Issuer + CN=Let's Encrypt Authority X3 +
+
+ Valid From + 2025-01-01 00:00:00 UTC +
+
+ Valid Until + 2025-04-01 23:59:59 UTC +
+
+ Days Until Expiry + 138 days +
+
+ Serial Number + 03:AB:CD:EF:12:34:56:78 +
+
+ +
+ Subject Alternative Names (SANs) + prod.example.com, www.prod.example.com, api.example.com +
+ + +
+
TLS Version Support
+ +
+
+ TLS 1.0 + Not Supported +
+
+ +
+
+ TLS 1.1 + Not Supported +
+
+ +
+
+ TLS 1.2 + Supported +
+
    +
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • +
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • +
  • TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
  • +
+
+ +
+
+ TLS 1.3 + Supported +
+
    +
  • TLS_AES_256_GCM_SHA384
  • +
  • TLS_AES_128_GCM_SHA256
  • +
  • TLS_CHACHA20_POLY1305_SHA256
  • +
+
+
+
+
+
+
3000TCPhttpNode.js ExpressUnexpected
+
+
+
+ Product + Node.js Express +
+
+ Version + 4.18.2 +
+
+ Protocol + HTTP +
+
+ āš ļø Status + Not in expected ports list +
+
+ + šŸ–¼ļø View Screenshot + +
+
8080TCPhttp-proxyTomcatUnexpected
+
+
+
+ Product + Apache Tomcat +
+
+ Version + 9.0.65 +
+
+ Protocol + HTTP +
+
+ āš ļø Status + Not in expected ports list +
+
+ + šŸ–¼ļø View Screenshot + +
+
+
+ + +
+
+

192.168.1.11

+
+ Ping: Expected + All Ports Expected +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortProtocolServiceProductStatus
22TCPsshOpenSSHExpected
+
+
+
+ Product + OpenSSH +
+
+ Version + 8.9p1 Ubuntu 3ubuntu0.1 +
+
+ OS Type + Linux +
+
+
+
443TCPhttpsApacheExpected
+
+
+
+ Product + Apache httpd +
+
+ Version + 2.4.52 +
+
+ Protocol + HTTPS +
+
+ + šŸ–¼ļø View Screenshot + + + +
+
+

šŸ”’ SSL/TLS Details Certificate Expiring Soon

+ Click to expand ā–¼ +
+
+
Certificate Information
+
+
+ Subject + CN=backup.example.com +
+
+ Issuer + CN=DigiCert TLS RSA SHA256 2020 CA1 +
+
+ Valid From + 2024-10-15 00:00:00 UTC +
+
+ Valid Until + 2025-11-29 23:59:59 UTC +
+
+ Days Until Expiry + 15 days āš ļø +
+
+ Serial Number + 09:FE:DC:BA:98:76:54:32 +
+
+ +
+ Subject Alternative Names (SANs) + backup.example.com +
+ +
+
TLS Version Support
+ +
+
+ TLS 1.0 + Supported (Weak) āš ļø +
+
    +
  • TLS_RSA_WITH_AES_128_CBC_SHA
  • +
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • +
+
+ +
+
+ TLS 1.2 + Supported +
+
    +
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • +
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • +
+
+ +
+
+ TLS 1.3 + Not Supported +
+
+
+
+
+
+
53UDPdomainISC BINDExpected
+
+
+
+ Product + ISC BIND +
+
+ Version + 9.16.1-Ubuntu +
+
+ Service Type + DNS Server +
+
+
+
+
+ + +
+
+

192.168.1.12

+
+ Ping: Expected + 1 Missing Service +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortProtocolServiceProductStatus
22TCPsshOpenSSHExpected
+
+
+
+ Product + OpenSSH +
+
+ Version + 8.2p1 +
+
+
+
443TCPāŒ Expected but not foundMissing
+
+
+ + +
+ + + +
+
+

192.168.1.50

+
+ Ping: Expected + All Ports Expected +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortProtocolServiceProductStatus
22TCPsshOpenSSHExpected
+
+
+
+ Product + OpenSSH +
+
+ Version + 8.4p1 Debian 5+deb11u1 +
+
+
+
8006TCPhttpsProxmox VEExpected
+
+
+
+ Product + Proxmox Virtual Environment +
+
+ Version + 7.4-1 +
+
+ Protocol + HTTPS +
+
+ Service Type + Virtualization Management +
+
+ + šŸ–¼ļø View Screenshot + + + +
+
+

šŸ”’ SSL/TLS Details Self-Signed Certificate

+ Click to expand ā–¼ +
+
+
Certificate Information
+
+
+ Subject + CN=proxmox.local +
+
+ Issuer + CN=proxmox.local (Self-Signed) +
+
+ Valid From + 2024-01-10 12:00:00 UTC +
+
+ Valid Until + 2034-01-08 12:00:00 UTC +
+
+ Days Until Expiry + 3342 days +
+
+ Serial Number + 01:23:45:67:89:AB:CD:EF +
+
+ +
+
TLS Version Support
+ +
+
+ TLS 1.0 + Not Supported +
+
+ +
+
+ TLS 1.1 + Not Supported +
+
+ +
+
+ TLS 1.2 + Supported +
+
    +
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • +
  • TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
  • +
+
+ +
+
+ TLS 1.3 + Supported +
+
    +
  • TLS_AES_256_GCM_SHA384
  • +
  • TLS_CHACHA20_POLY1305_SHA256
  • +
+
+
+
+
+
+
+
+ + +
+
+

192.168.1.51

+
+ Ping: Expected + All Ports Expected +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortProtocolServiceProductStatus
22TCPsshOpenSSHExpected
+
+
+
+ Product + OpenSSH +
+
+ Version + 8.9p1 Ubuntu 3ubuntu0.6 +
+
+
+
5432TCPpostgresqlPostgreSQLExpected
+
+
+
+ Product + PostgreSQL DB +
+
+ Version + 14.7 +
+
+ Extra Info + Debian 14.7-1.pgdg110+1 +
+
+ OS Type + Linux +
+
+
+
6379TCPredisRedisExpected
+
+
+
+ Product + Redis key-value store +
+
+ Version + 7.0.8 +
+
+ Extra Info + 64-bit +
+
+
+
+
+
+ +
+ + + + diff --git a/templates/report_template.html b/templates/report_template.html new file mode 100644 index 0000000..0e668bb --- /dev/null +++ b/templates/report_template.html @@ -0,0 +1,949 @@ + + + + + + SneakyScanner Report - {{ title }} + + + +
+ +
+

{{ title }}

+
+ šŸ“… Scan Time: {{ scan_time | format_date }} + ā±ļø Duration: {{ scan_duration | format_duration }} + {% if config_file %} + šŸ“„ Config: {{ config_file }} + {% endif %} +
+
+ + +
+

Scan Summary

+
+ +
+

Scan Statistics

+
+
+ Total IPs Scanned + {{ summary_stats.total_ips }} +
+
+ TCP Ports Found + {{ summary_stats.tcp_ports }} +
+
+ UDP Ports Found + {{ summary_stats.udp_ports }} +
+
+ Services Identified + {{ summary_stats.services }} +
+
+ Web Services + {{ summary_stats.web_services }} +
+
+ Screenshots Captured + {{ summary_stats.screenshots }} +
+
+
+ + +
+

Drift Alerts

+
+ {% if drift_alerts.unexpected_tcp > 0 %} +
+ Unexpected TCP Ports + {{ drift_alerts.unexpected_tcp }} +
+ {% endif %} + {% if drift_alerts.unexpected_udp > 0 %} +
+ Unexpected UDP Ports + {{ drift_alerts.unexpected_udp }} +
+ {% endif %} + {% if drift_alerts.missing_tcp > 0 or drift_alerts.missing_udp > 0 %} +
+ Missing Expected Services + {{ drift_alerts.missing_tcp + drift_alerts.missing_udp }} +
+ {% endif %} + {% if drift_alerts.new_services > 0 %} +
+ New Services Detected + {{ drift_alerts.new_services }} +
+ {% endif %} + {% if drift_alerts.unexpected_tcp == 0 and drift_alerts.unexpected_udp == 0 and drift_alerts.missing_tcp == 0 and drift_alerts.missing_udp == 0 %} +
+ No drift detected - all services match expectations + āœ“ +
+ {% endif %} +
+
+ + +
+

Security Warnings

+
+ {% if security_warnings.expiring_certs > 0 %} +
+ Certificates Expiring Soon (<30 days) + {{ security_warnings.expiring_certs }} +
+ {% endif %} + {% if security_warnings.weak_tls > 0 %} +
+ Weak TLS Versions (1.0/1.1) + {{ security_warnings.weak_tls }} +
+ {% endif %} + {% if security_warnings.self_signed > 0 %} +
+ Self-Signed Certificates + {{ security_warnings.self_signed }} +
+ {% endif %} + {% if security_warnings.high_ports > 0 %} +
+ High Port Services (>10000) + {{ security_warnings.high_ports }} +
+ {% endif %} + {% if security_warnings.expiring_certs == 0 and security_warnings.weak_tls == 0 and security_warnings.self_signed == 0 %} +
+ No critical security warnings detected + āœ“ +
+ {% endif %} +
+
+
+
+ + + {% for site in sites %} +
+ + + + {% for ip_data in site.ips %} + {% set expected = ip_data.expected %} + {% set actual = ip_data.actual %} + {% set expected_tcp = expected.tcp_ports | default([]) | list %} + {% set actual_tcp = actual.tcp_ports | default([]) | list %} + {% set expected_udp = expected.udp_ports | default([]) | list %} + {% set actual_udp = actual.udp_ports | default([]) | list %} + {% set unexpected_tcp = actual_tcp | reject('in', expected_tcp) | list %} + {% set unexpected_udp = actual_udp | reject('in', expected_udp) | list %} + {% set missing_tcp = expected_tcp | reject('in', actual_tcp) | list %} + {% set missing_udp = expected_udp | reject('in', actual_udp) | list %} + +
+
+

{{ ip_data.address }}

+
+ {% if expected.ping %} + {% if actual.ping %} + Ping: Expected + {% else %} + Ping: Missing + {% endif %} + {% endif %} + + {% if (unexpected_tcp | length) > 0 or (unexpected_udp | length) > 0 %} + {{ (unexpected_tcp | length) + (unexpected_udp | length) }} Unexpected Port{{ 's' if ((unexpected_tcp | length) + (unexpected_udp | length)) > 1 else '' }} + {% elif (missing_tcp | length) > 0 or (missing_udp | length) > 0 %} + {{ (missing_tcp | length) + (missing_udp | length) }} Missing Service{{ 's' if ((missing_tcp | length) + (missing_udp | length)) > 1 else '' }} + {% else %} + All Ports Expected + {% endif %} +
+
+ + + + + + + + + + + + + {% for service in actual.services | default([]) %} + {% set service_id = 'service_' ~ loop.index ~ '_' ~ ip_data.address | replace('.', '_') %} + {% set is_expected = service.port in expected_tcp or service.port in expected_udp %} + + + + + + + + + + + {% endfor %} + + {# Show expected UDP ports (UDP ports found and expected, with no service details) #} + {% for port in actual_udp %} + {% if port in expected_udp %} + + + + + + + + + + {% endif %} + {% endfor %} + + {# Show unexpected UDP ports (UDP ports found but not expected, with no service details) #} + {% for port in unexpected_udp %} + + + + + + + + + + {% endfor %} + + {# Show missing expected services #} + {% for port in missing_tcp %} + + + + + + + {% endfor %} + {% for port in missing_udp %} + + + + + + + {% endfor %} + +
PortProtocolServiceProductStatus
{{ service.port }}{{ service.protocol | upper }}{{ service.service | default('unknown') }}{{ service.product | default('') }} {% if service.version %}{{ service.version }}{% endif %} + {% if is_expected %} + Expected + {% else %} + Unexpected + {% endif %} +
+
+
+ {% if service.product %} +
+ Product + {{ service.product }} +
+ {% endif %} + {% if service.version %} +
+ Version + {{ service.version }} +
+ {% endif %} + {% if service.extrainfo %} +
+ Extra Info + {{ service.extrainfo }} +
+ {% endif %} + {% if service.ostype %} +
+ OS Type + {{ service.ostype }} +
+ {% endif %} + {% if service.http_info %} +
+ Protocol + {{ service.http_info.protocol | upper }} +
+ {% endif %} + {% if not is_expected %} +
+ āš ļø Status + Not in expected ports list +
+ {% endif %} +
+ + {% if service.http_info and service.http_info.screenshot %} + + šŸ–¼ļø View Screenshot + + {% endif %} + + {% if service.http_info and service.http_info.ssl_tls %} + {% set ssl_id = 'ssl_' ~ loop.index ~ '_' ~ ip_data.address | replace('.', '_') %} + {% set ssl = service.http_info.ssl_tls %} + {% set cert = ssl.certificate %} +
+
+

šŸ”’ SSL/TLS Details + {% if cert.days_until_expiry is defined and cert.days_until_expiry < 30 %} + Certificate Expiring Soon + {% elif cert.issuer == cert.subject %} + Self-Signed Certificate + {% endif %} +

+ Click to expand ā–¼ +
+
+
Certificate Information
+
+ {% if cert.subject %} +
+ Subject + {{ cert.subject }} +
+ {% endif %} + {% if cert.issuer %} +
+ Issuer + + {{ cert.issuer }}{% if cert.issuer == cert.subject %} (Self-Signed){% endif %} + +
+ {% endif %} + {% if cert.not_valid_before %} +
+ Valid From + {{ cert.not_valid_before | format_date }} +
+ {% endif %} + {% if cert.not_valid_after %} +
+ Valid Until + + {{ cert.not_valid_after | format_date }} + +
+ {% endif %} + {% if cert.days_until_expiry is defined %} +
+ Days Until Expiry + + {{ cert.days_until_expiry }} days{% if cert.days_until_expiry < 30 %} āš ļø{% endif %} + +
+ {% endif %} + {% if cert.serial_number %} +
+ Serial Number + {{ cert.serial_number }} +
+ {% endif %} +
+ + {% if cert.sans %} +
+ Subject Alternative Names (SANs) + {{ cert.sans | join(', ') }} +
+ {% endif %} + + {% if ssl.tls_versions %} +
+
TLS Version Support
+ + {% for version_name in ['TLS 1.0', 'TLS 1.1', 'TLS 1.2', 'TLS 1.3'] %} + {% set tls_version = ssl.tls_versions.get(version_name, {}) %} + {% if tls_version.supported %} +
+
+ {{ version_name }} + {% if version_name in ['TLS 1.0', 'TLS 1.1'] %} + Supported (Weak) āš ļø + {% else %} + Supported + {% endif %} +
+ {% if tls_version.cipher_suites %} +
    + {% for cipher in tls_version.cipher_suites %} +
  • {{ cipher }}
  • + {% endfor %} +
+ {% endif %} +
+ {% elif tls_version.supported is defined %} +
+
+ {{ version_name }} + Not Supported +
+
+ {% endif %} + {% endfor %} +
+ {% endif %} +
+
+ {% endif %} +
+
{{ port }}UDPNo service detection availableExpected
+
+
+
+ Protocol + UDP +
+
+ Note + Service detection not available for UDP ports +
+
+
+
{{ port }}UDPNo service detection availableUnexpected
+
+
+
+ āš ļø Status + UDP port discovered but not in expected ports list. Service detection not available for UDP. +
+
+
+
{{ port }}TCPāŒ Expected but not foundMissing
{{ port }}UDPāŒ Expected but not foundMissing
+
+ {% endfor %} +
+ {% endfor %} + +
+ + + +