template restructure
This commit is contained in:
40
app/main.py
40
app/main.py
@@ -3,8 +3,6 @@ import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# TODO:
|
||||
# LOGGING
|
||||
# REPORT - use the scan config names for the names of the report and file
|
||||
# REPORT - make darkmode
|
||||
# TLS SCANNING
|
||||
# TLS Version PROBE
|
||||
@@ -12,13 +10,14 @@ logging.basicConfig(level=logging.INFO)
|
||||
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
from dataclasses import asdict
|
||||
from ipaddress import ip_address
|
||||
from typing import Any, Dict, List, Set
|
||||
|
||||
from utils.scan_config_loader import ScanConfigRepository, ScanConfigFile
|
||||
from utils.schedule_manager import ScanScheduler
|
||||
from utils.scanner import nmap_scanner
|
||||
from utils.models import HostResult
|
||||
from utils.models import HostResult, HostReport, GroupedReports
|
||||
|
||||
from reporting_jinja import write_html_report_jinja
|
||||
from utils.settings import get_settings
|
||||
@@ -29,8 +28,6 @@ logger = logging.getLogger(__file__)
|
||||
utils = get_common_utils()
|
||||
settings = get_settings()
|
||||
|
||||
HTML_REPORT_FILE = Path() / "data" / "report.html"
|
||||
|
||||
def results_to_open_sets(
|
||||
results: List[HostResult],
|
||||
count_as_open: Set[str] = frozenset({"open", "open|filtered"})) -> Dict[str, Dict[str, Set[int]]]:
|
||||
@@ -125,16 +122,35 @@ def build_reports(
|
||||
|
||||
def run_repo_scan(scan_config:ScanConfigFile):
|
||||
logger.info(f"Starting scan for {scan_config.name}")
|
||||
logger.info("Options: udp=%s tls_sec=%s tls_exp=%s",
|
||||
scan_config.scan_options.udp_scan,
|
||||
scan_config.scan_options.tls_security_scan,
|
||||
scan_config.scan_options.tls_exp_check)
|
||||
logger.info(f"Options: udp={scan_config.scan_options.udp_scan} tls_sec={scan_config.scan_options.tls_security_scan} tls_exp={scan_config.scan_options.tls_exp_check}",)
|
||||
logger.info(f"Reporting Options: Dark Mode: {scan_config.reporting.dark_mode}")
|
||||
logger.info("Targets: %d hosts", len(scan_config.scan_targets))
|
||||
|
||||
# tack the filename on the end of our data path
|
||||
file_out_path = Path() / "data" / "output" / scan_config.reporting.report_filename
|
||||
|
||||
LIGHT_TEMPLATE = "report_light.html.j2"
|
||||
DARK_TEMPLATE = "report_dark.html.j2"
|
||||
|
||||
template = LIGHT_TEMPLATE
|
||||
|
||||
if scan_config.reporting.dark_mode:
|
||||
template = DARK_TEMPLATE
|
||||
|
||||
logger.info(f"Reporting Template Set to: {template}")
|
||||
|
||||
scanner = nmap_scanner(scan_config)
|
||||
scan_results = scanner.scan_targets()
|
||||
discovered_sets = results_to_open_sets(scan_results, count_as_open={"open", "open|filtered"})
|
||||
reports = build_reports(scan_config, discovered_sets)
|
||||
write_html_report_jinja(reports=reports,host_results=scan_results,out_path=HTML_REPORT_FILE,title="Compliance Report",only_issues=True)
|
||||
|
||||
# build the HTML report
|
||||
write_html_report_jinja(reports=reports,
|
||||
host_results=scan_results,
|
||||
out_path=file_out_path,
|
||||
title=scan_config.reporting.report_name,
|
||||
template_name=template,
|
||||
only_issues=True)
|
||||
scanner.cleanup()
|
||||
|
||||
def main():
|
||||
|
||||
@@ -78,7 +78,6 @@ def render_html_report_jinja(
|
||||
)
|
||||
return html
|
||||
|
||||
|
||||
def write_html_report_jinja(
|
||||
reports: Dict[str, Dict[str, List[int]]],
|
||||
host_results: List[HostResult],
|
||||
|
||||
177
app/templates/report_dark.html.j2
Normal file
177
app/templates/report_dark.html.j2
Normal file
@@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ title }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{# Dark mode variant of compliance report. Filename: report_dark.html.j2 #}
|
||||
</head>
|
||||
<body style="margin:0;padding:16px;background:#0b1020;color:#e5e7eb">
|
||||
<div style="max-width:860px;margin:0 auto;font-family:Segoe UI,Arial,sans-serif">
|
||||
|
||||
{# ===== Title Card ===== #}
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 12px 0">
|
||||
<tr>
|
||||
<td style="padding:14px 16px;background:#1e293b;border:1px solid #334155;border-radius:10px;color:#f1f5f9">
|
||||
<div style="font-size:20px;font-weight:700;margin-bottom:4px">{{ title }}</div>
|
||||
<div style="font-size:12px;color:#94a3b8">Generated: {{ generated }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
{# ===== Summary Bar ===== #}
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 16px 0">
|
||||
<tr>
|
||||
<td style="
|
||||
padding:12px 16px;
|
||||
border:1px solid #334155;
|
||||
border-radius:8px;
|
||||
background:#1e293b;
|
||||
color:#f1f5f9;
|
||||
font-size:14px;
|
||||
">
|
||||
Total hosts:
|
||||
<strong style="color:#f8fafc">{{ total_hosts }}</strong>
|
||||
Matching expected:
|
||||
<strong style="color:#4ade80">{{ ok_hosts }}</strong>
|
||||
With issues:
|
||||
<strong style="color:#f87171">{{ hosts_with_issues|length }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
{% if not only_issues and ok_hosts == total_hosts and total_hosts > 0 %}
|
||||
<div style="margin:6px 0 12px 0;font-size:12px;color:#9ca3af">
|
||||
All hosts matched expected ports.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if only_issues and hosts_with_issues|length == 0 %}
|
||||
<div style="margin:8px 0;color:#9ca3af">
|
||||
No hosts with issues found. ✅
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# ===== Host Sections ===== #}
|
||||
{% for ip_key, r in reports|dictsort %}
|
||||
{% set has_issues = r.unexpected_tcp or r.missing_tcp or r.unexpected_udp or r.missing_udp %}
|
||||
{% set hr = host_results_by_ip.get(ip_key) %}
|
||||
{% set header_title = ip_key ~ ((' (' ~ hr.host ~ ')') if hr and hr.host else '') %}
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 18px 0;border-collapse:separate;border-spacing:0">
|
||||
<tr>
|
||||
<td colspan="5" style="padding:12px 10px;background:#1e293b;color:#f1f5f9;font-weight:600;font-size:14px;border-radius:8px;border:1px solid #334155">
|
||||
|
||||
{{ header_title }}
|
||||
{% if has_issues %}
|
||||
{{ badge('ISSUES', '#ef4444') }}
|
||||
{% else %}
|
||||
{{ badge('OK', '#16a34a') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% if has_issues %}
|
||||
<tr>
|
||||
<td colspan="5" style="padding:10px 10px 6px 10px;font-size:13px">
|
||||
{{ badge('ISSUES', '#ef4444') }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% macro delta_row(label, ports) -%}
|
||||
<tr>
|
||||
<td colspan="5" style="padding:4px 10px 6px 10px;font-size:13px">
|
||||
<strong style="color:#e5e7eb">{{ label }}:</strong>
|
||||
<span style="color:#cbd5e1">{{ fmt_ports(ports) }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endmacro %}
|
||||
|
||||
{{ 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) }}
|
||||
|
||||
<tr>
|
||||
<td colspan="5" style="padding:8px 10px 6px 10px;font-size:13px">
|
||||
<div style="font-weight:600;margin:8px 0;color:#e5e7eb">Discovered Ports</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Protocol</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Port</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">State</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Service</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Expectation</td>
|
||||
</tr>
|
||||
|
||||
{% if hr and hr.ports %}
|
||||
{# Track visible rows after filtering (e.g., skip closed UDP) #}
|
||||
{% set ns = namespace(visible_rows=0) %}
|
||||
|
||||
{% for p in hr.ports %}
|
||||
{# --- Normalize helpers --- #}
|
||||
{% set proto = (p.protocol|string|lower) %}
|
||||
{% set state = (p.state|string|lower) %}
|
||||
|
||||
{# --- Skip rule: hide UDP rows that are "closed" (substring match) --- #}
|
||||
{% set skip_row = (proto == 'udp' and ('closed' in state)) %}
|
||||
|
||||
{# --- Expectation labeling --- #}
|
||||
{% 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 '<span style="color:#9ca3af">—</span>')
|
||||
) %}
|
||||
|
||||
{% if not skip_row %}
|
||||
{% set ns.visible_rows = ns.visible_rows + 1 %}
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020">{{ pill_proto(p.protocol) }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#e5e7eb">{{ p.port }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020">{{ pill_state(p.state) }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#cbd5e1">{{ p.service or '-' }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020">{{ expectation_badge | safe }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if ns.visible_rows == 0 %}
|
||||
<tr>
|
||||
<td colspan="5" style="padding:8px 10px;border:1px solid #1f2937;background:#0b1020;color:#9ca3af">
|
||||
No per-port details to display after filtering closed UDP results.
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" style="padding:8px 10px;border:1px solid #1f2937;background:#0b1020;color:#9ca3af">
|
||||
No per-port details available for this host.
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{# Host has no issues #}
|
||||
<tr>
|
||||
<td colspan="5" style="padding:10px 10px 8px 10px;font-size:13px">
|
||||
{{ badge('OK', '#16a34a') }} <span style="color:#cbd5e1">Matches expected ports.</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
{% endfor %}
|
||||
|
||||
<div style="margin-top:18px;font-size:11px;color:#9ca3af">
|
||||
Report generated by mass-scan-v2 • {{ generated }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -81,24 +81,61 @@
|
||||
<td style="padding:8px 10px;border:1px solid #e5e7eb;background:#f8fafc;font-weight:600">Port</td>
|
||||
<td style="padding:8px 10px;border:1px solid #e5e7eb;background:#f8fafc;font-weight:600">State</td>
|
||||
<td style="padding:8px 10px;border:1px solid #e5e7eb;background:#f8fafc;font-weight:600">Service</td>
|
||||
<td style="padding:8px 10px;border:1px solid #e5e7eb;background:#f8fafc;font-weight:600">Expectation</td>
|
||||
</tr>
|
||||
|
||||
{% if hr and hr.ports %}
|
||||
{% for p in hr.ports %}
|
||||
{# Track whether we rendered any visible rows after filtering #}
|
||||
{% set ns = namespace(visible_rows=0) %}
|
||||
|
||||
{% for p in hr.ports %}
|
||||
{# Normalize helpers #}
|
||||
{% set proto = (p.protocol|string|lower) %}
|
||||
{% set state = (p.state|string|lower) %}
|
||||
|
||||
{# Skip rule: hide UDP rows that are 'closed' #}
|
||||
{% set skip_row = (proto == 'udp' and ('closed' in state)) %}
|
||||
|
||||
{# Compute expectation labeling (only matters for displayed rows) #}
|
||||
{% 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 '<span style="color:#64748b">—</span>')
|
||||
) %}
|
||||
|
||||
{% if not skip_row %}
|
||||
{% set ns.visible_rows = ns.visible_rows + 1 %}
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #e5e7eb">{{ pill_proto(p.protocol) }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #e5e7eb">{{ p.port }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #e5e7eb">{{ pill_state(p.state) }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #e5e7eb">{{ p.service or '-' }}</td>
|
||||
<td style="padding:6px 10px;border:1px solid #e5e7eb">{{ expectation_badge | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# If everything was filtered out, show a friendly note #}
|
||||
{% if ns.visible_rows == 0 %}
|
||||
<tr>
|
||||
<td colspan="4" style="padding:8px 10px;border:1px solid #e5e7eb;color:#64748b">
|
||||
No per-port details available for this host.
|
||||
<td colspan="5" style="padding:8px 10px;border:1px solid #e5e7eb;color:#64748b">
|
||||
No per-port details to display after filtering closed UDP results.
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" style="padding:8px 10px;border:1px solid #e5e7eb;color:#64748b">
|
||||
No per-port details available for this host.
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{# Host has no issues #}
|
||||
<tr>
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional
|
||||
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Dict, List, Set, Optional, Any
|
||||
from ipaddress import ip_address
|
||||
|
||||
@dataclass
|
||||
class PortFinding:
|
||||
@@ -16,7 +16,6 @@ class PortFinding:
|
||||
state: str
|
||||
service: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class HostResult:
|
||||
"""
|
||||
@@ -28,3 +27,68 @@ class HostResult:
|
||||
address: str
|
||||
host: Optional[str] = None
|
||||
ports: List[PortFinding] = field(default_factory=list)
|
||||
|
||||
@dataclass
|
||||
class HostReport:
|
||||
"""
|
||||
Delta result for a single host.
|
||||
"""
|
||||
ip: str
|
||||
unexpected_tcp: List[int]
|
||||
missing_tcp: List[int]
|
||||
unexpected_udp: List[int]
|
||||
missing_udp: List[int]
|
||||
|
||||
def has_issues(self) -> bool:
|
||||
"""
|
||||
Returns True if any delta list is non-empty.
|
||||
"""
|
||||
if self.unexpected_tcp:
|
||||
return True
|
||||
if self.missing_tcp:
|
||||
return True
|
||||
if self.unexpected_udp:
|
||||
return True
|
||||
if self.missing_udp:
|
||||
return True
|
||||
return False
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Convert to a plain dict for JSON/Jinja contexts.
|
||||
"""
|
||||
return asdict(self)
|
||||
|
||||
@dataclass
|
||||
class GroupedReports:
|
||||
"""
|
||||
Final, template-friendly structure:
|
||||
- issues: list of HostReport with any deltas (sorted by IP)
|
||||
- expected: list of HostReport with no deltas (sorted by IP)
|
||||
- by_ip: mapping for random access if needed
|
||||
"""
|
||||
issues: List[HostReport]
|
||||
expected: List[HostReport]
|
||||
by_ip: Dict[str, HostReport]
|
||||
|
||||
def to_context(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Produce plain-dict context for Jinja render() if you prefer dicts.
|
||||
"""
|
||||
issues_dicts: List[Dict[str, Any]] = []
|
||||
for hr in self.issues:
|
||||
issues_dicts.append(hr.to_dict())
|
||||
|
||||
expected_dicts: List[Dict[str, Any]] = []
|
||||
for hr in self.expected:
|
||||
expected_dicts.append(hr.to_dict())
|
||||
|
||||
by_ip_dict: Dict[str, Dict[str, Any]] = {}
|
||||
for ip, hr in self.by_ip.items():
|
||||
by_ip_dict[ip] = hr.to_dict()
|
||||
|
||||
return {
|
||||
"issues": issues_dicts,
|
||||
"expected": expected_dicts,
|
||||
"by_ip": by_ip_dict,
|
||||
}
|
||||
@@ -41,6 +41,7 @@ class Reporting:
|
||||
report_name: str = "Scan Report"
|
||||
report_filename: str = "report.html"
|
||||
full_details: bool = False
|
||||
dark_mode: bool = True
|
||||
email_to: List[str] = field(default_factory=list)
|
||||
email_cc: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[
|
||||
{"ip": "10.10.99.6", "expected_tcp": [22,222,3000], "expected_udp": []},
|
||||
{"ip": "10.10.99.2", "expected_tcp": [22,222,3000], "expected_udp": []},
|
||||
{"ip": "10.10.99.10", "expected_tcp": [22,80,443], "expected_udp": []}
|
||||
]
|
||||
190
data/output/corp-wan.html
Normal file
190
data/output/corp-wan.html
Normal file
@@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Corporate WAN Perimeter</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body style="margin:0;padding:16px;background:#0b1020;color:#e5e7eb">
|
||||
<div style="max-width:860px;margin:0 auto;font-family:Segoe UI,Arial,sans-serif">
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 12px 0">
|
||||
<tr>
|
||||
<td style="padding:14px 16px;background:#1e293b;border:1px solid #334155;border-radius:10px;color:#f1f5f9">
|
||||
<div style="font-size:20px;font-weight:700;margin-bottom:4px">Corporate WAN Perimeter</div>
|
||||
<div style="font-size:12px;color:#94a3b8">Generated: 2025-10-22 01:45:32</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 16px 0">
|
||||
<tr>
|
||||
<td style="
|
||||
padding:12px 16px;
|
||||
border:1px solid #334155;
|
||||
border-radius:8px;
|
||||
background:#1e293b;
|
||||
color:#f1f5f9;
|
||||
font-size:14px;
|
||||
">
|
||||
Total hosts:
|
||||
<strong style="color:#f8fafc">3</strong>
|
||||
Matching expected:
|
||||
<strong style="color:#4ade80">2</strong>
|
||||
With issues:
|
||||
<strong style="color:#f87171">1</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 18px 0;border-collapse:separate;border-spacing:0">
|
||||
<tr>
|
||||
<td colspan="5" style="padding:12px 10px;background:#1e293b;color:#f1f5f9;font-weight:600;font-size:14px;border-radius:8px;border:1px solid #334155">
|
||||
|
||||
10.10.20.12
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#ef4444;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">ISSUES</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="5" style="padding:10px 10px 6px 10px;font-size:13px">
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#ef4444;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">ISSUES</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td colspan="5" style="padding:4px 10px 6px 10px;font-size:13px">
|
||||
<strong style="color:#e5e7eb">Unexpected TCP open ports:</strong>
|
||||
<span style="color:#cbd5e1">3128</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" style="padding:4px 10px 6px 10px;font-size:13px">
|
||||
<strong style="color:#e5e7eb">Expected TCP ports not seen:</strong>
|
||||
<span style="color:#cbd5e1">none</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" style="padding:4px 10px 6px 10px;font-size:13px">
|
||||
<strong style="color:#e5e7eb">Unexpected UDP open ports:</strong>
|
||||
<span style="color:#cbd5e1">none</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" style="padding:4px 10px 6px 10px;font-size:13px">
|
||||
<strong style="color:#e5e7eb">Expected UDP ports not seen:</strong>
|
||||
<span style="color:#cbd5e1">none</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="5" style="padding:8px 10px 6px 10px;font-size:13px">
|
||||
<div style="font-weight:600;margin:8px 0;color:#e5e7eb">Discovered Ports</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Protocol</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Port</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">State</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Service</td>
|
||||
<td style="padding:8px 10px;border:1px solid #1f2937;background:#111827;font-weight:600;color:#e5e7eb">Expectation</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#334155;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">tcp</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#e5e7eb">22</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">open</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#cbd5e1">ssh</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">Expected</span></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#334155;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">tcp</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#e5e7eb">111</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">open</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#cbd5e1">rpcbind</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">Expected</span></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#334155;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">tcp</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#e5e7eb">3128</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">open</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#cbd5e1">squid-http</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#ef4444;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">Issue</span></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#334155;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">tcp</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#e5e7eb">8006</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">open</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#cbd5e1">wpl-analytics</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">Expected</span></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#334155;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">udp</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#e5e7eb">111</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">open</span></td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020;color:#cbd5e1">rpcbind</td>
|
||||
<td style="padding:6px 10px;border:1px solid #1f2937;background:#0b1020"><span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">Expected</span></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 18px 0;border-collapse:separate;border-spacing:0">
|
||||
<tr>
|
||||
<td colspan="5" style="padding:12px 10px;background:#1e293b;color:#f1f5f9;font-weight:600;font-size:14px;border-radius:8px;border:1px solid #334155">
|
||||
|
||||
10.10.20.4
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="5" style="padding:10px 10px 8px 10px;font-size:13px">
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span> <span style="color:#cbd5e1">Matches expected ports.</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 18px 0;border-collapse:separate;border-spacing:0">
|
||||
<tr>
|
||||
<td colspan="5" style="padding:12px 10px;background:#1e293b;color:#f1f5f9;font-weight:600;font-size:14px;border-radius:8px;border:1px solid #334155">
|
||||
|
||||
10.10.20.5
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="5" style="padding:10px 10px 8px 10px;font-size:13px">
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span> <span style="color:#cbd5e1">Matches expected ports.</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div style="margin-top:18px;font-size:11px;color:#9ca3af">
|
||||
Report generated by mass-scan-v2 • 2025-10-22 01:45:32
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Compliance Report</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body style="margin:0;padding:16px;background:#ffffff">
|
||||
<div style="max-width:860px;margin:0 auto;font-family:Segoe UI,Arial,sans-serif">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 12px 0">
|
||||
<tr>
|
||||
<td style="padding:10px;background:#0f172a;border-radius:10px;color:#e2e8f0">
|
||||
<div style="font-size:18px;font-weight:700;margin-bottom:4px">Compliance Report</div>
|
||||
<div style="font-size:12px;color:#94a3b8">Generated: 2025-10-17 21:42:08</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 16px 0">
|
||||
<tr>
|
||||
<td style="padding:10px;border:1px solid #e5e7eb;border-radius:8px">
|
||||
Total hosts: <strong>2</strong>
|
||||
Matching expected: <strong>2</strong>
|
||||
With issues: <strong>0</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<div style="margin:8px 0;color:#64748b">
|
||||
No hosts with issues found. ✅
|
||||
</div>
|
||||
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 18px 0">
|
||||
<tr>
|
||||
<td colspan="4" style="padding:12px 10px;background:#0f172a;color:#e2e8f0;font-weight:600;font-size:14px;border-radius:8px">
|
||||
10.10.20.4 <span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span> </td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="4" style="padding:10px 10px 8px 10px;font-size:13px">
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span> Matches expected ports.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin:0 0 18px 0">
|
||||
<tr>
|
||||
<td colspan="4" style="padding:12px 10px;background:#0f172a;color:#e2e8f0;font-weight:600;font-size:14px;border-radius:8px">
|
||||
10.10.20.5 <span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span> </td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="4" style="padding:10px 10px 8px 10px;font-size:13px">
|
||||
<span style="display:inline-block;padding:2px 6px;border-radius:12px;font-size:12px;line-height:16px;background:#16a34a;color:#ffffff;font-family:Segoe UI,Arial,sans-serif">OK</span> Matches expected ports.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<div style="margin-top:18px;font-size:11px;color:#94a3b8">
|
||||
Report generated by mass-scan-v2 • 2025-10-17 21:42:08
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
[
|
||||
{"ip": "81.246.102.192", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.193", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.194", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.195", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.196", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.197", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.198", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.199", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.200", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.201", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.202", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.203", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.204", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.205", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.206", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.207", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.208", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.209", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.210", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.211", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.212", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.213", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.214", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.215", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.216", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.217", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.218", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.219", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.220", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.221", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.222", "expected_tcp": [], "expected_udp": []},
|
||||
{"ip": "81.246.102.223", "expected_tcp": [], "expected_udp": []}
|
||||
]
|
||||
@@ -8,6 +8,7 @@ scan_options:
|
||||
reporting:
|
||||
report_name: Corporate WAN Perimeter
|
||||
report_filename: corp-wan.html
|
||||
dark_mode: true
|
||||
full_details: true
|
||||
email_to: soc@example.com # single string is fine; or a list
|
||||
email_cc: [] # explicitly none
|
||||
@@ -20,3 +21,7 @@ scan_targets:
|
||||
- ip: 10.10.20.5
|
||||
expected_tcp: [22, 53, 80]
|
||||
expected_udp: [53]
|
||||
|
||||
- ip: 10.10.20.12
|
||||
expected_tcp: [22, 111, 8006]
|
||||
expected_udp: [111]
|
||||
Reference in New Issue
Block a user