Files
compliance-scan/src/sslysze_scan/reporter/template_utils.py
2025-12-19 20:10:39 +01:00

165 lines
4.3 KiB
Python

"""Shared utilities for report template rendering."""
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
from jinja2 import Environment, FileSystemLoader, select_autoescape
from .query import has_tls_support
def format_tls_version(version: str) -> str:
"""Format TLS version string for display.
Args:
version: TLS version identifier (e.g., "1.2", "ssl_3.0")
Returns:
Formatted version string (e.g., "TLS 1.2", "SSL 3.0")
"""
version_map = {
"ssl_3.0": "SSL 3.0",
"1.0": "TLS 1.0",
"1.1": "TLS 1.1",
"1.2": "TLS 1.2",
"1.3": "TLS 1.3",
}
return version_map.get(version, version)
def create_jinja_env() -> Environment:
"""Create Jinja2 environment with standard configuration.
Returns:
Configured Jinja2 Environment with custom filters
"""
template_dir = Path(__file__).parent.parent / "templates"
env = Environment(
loader=FileSystemLoader(str(template_dir)),
autoescape=select_autoescape(["html", "xml"]),
trim_blocks=True,
lstrip_blocks=True,
)
env.filters["format_tls_version"] = format_tls_version
return env
def generate_report_id(metadata: dict[str, Any]) -> str:
"""Generate report ID from scan metadata.
Args:
metadata: Scan metadata dictionary containing timestamp
Returns:
Report ID in format YYYYMMDD_<scanid>
"""
try:
dt = datetime.fromisoformat(metadata["timestamp"])
date_str = dt.strftime("%Y%m%d")
except (ValueError, KeyError):
date_str = datetime.now(UTC).strftime("%Y%m%d")
return f"{date_str}_{metadata['scan_id']}"
def build_template_context(data: dict[str, Any]) -> dict[str, Any]:
"""Build template context from scan data.
Args:
data: Scan data dictionary from get_scan_data()
Returns:
Dictionary with template context variables
"""
metadata = data["metadata"]
duration = metadata.get("duration")
if duration is not None:
duration_str = (
f"{duration:.2f}" if isinstance(duration, (int, float)) else str(duration)
)
else:
duration_str = "N/A"
# Format timestamp to minute precision (DD.MM.YYYY HH:MM)
timestamp_str = metadata["timestamp"]
try:
dt = datetime.fromisoformat(timestamp_str)
timestamp_str = dt.strftime("%d.%m.%Y %H:%M")
except (ValueError, KeyError):
pass
# Filter ports with TLS support for port sections
ports_with_tls = []
for port_data in data["ports_data"].values():
if has_tls_support(port_data):
ports_with_tls.append(port_data)
return {
"scan_id": metadata["scan_id"],
"hostname": metadata["hostname"],
"fqdn": metadata["fqdn"],
"ipv4": metadata["ipv4"],
"ipv6": metadata["ipv6"],
"timestamp": timestamp_str,
"duration": duration_str,
"ports": ", ".join(metadata["ports"]),
"ports_without_tls": data.get("summary", {}).get("ports_without_tls", 0),
"summary": data.get("summary", {}),
"ports_data": sorted(ports_with_tls, key=lambda x: x["port"]),
}
def prepare_output_path(
output_file: str | None,
output_dir: str,
default_filename: str,
) -> Path:
"""Prepare output file path and ensure parent directory exists.
Args:
output_file: Explicit output file path (optional)
output_dir: Output directory for auto-generated files
default_filename: Default filename if output_file is None
Returns:
Path object for output file
"""
if output_file:
output_path = Path(output_file)
else:
output_path = Path(output_dir) / default_filename
output_path.parent.mkdir(parents=True, exist_ok=True)
return output_path
def render_template_to_file(
template_name: str,
context: dict[str, Any],
output_path: Path,
) -> str:
"""Render Jinja2 template and write to file.
Args:
template_name: Name of template file
context: Template context variables
output_path: Output file path
Returns:
String path of generated file
"""
env = create_jinja_env()
template = env.get_template(template_name)
content = template.render(**context)
output_path.write_text(content, encoding="utf-8")
return str(output_path)