- SSH scanning via ssh-audit (KEX, encryption, MAC, host keys) - BSI TR-02102-4 and IANA compliance validation for SSH - CSV/Markdown/reST reports for SSH results - Unified compliance schema and database views - Code optimization: modular query/writer architecture
298 lines
9.5 KiB
Python
298 lines
9.5 KiB
Python
"""Tests for CSV export functionality."""
|
|
|
|
import csv
|
|
from pathlib import Path
|
|
|
|
from sslysze_scan.reporter.csv_export import generate_csv_reports
|
|
|
|
|
|
class TestCsvExport:
|
|
"""Tests for CSV file generation."""
|
|
|
|
def test_export_summary(self, test_db_path: str, tmp_path: Path) -> None:
|
|
"""Test summary CSV export with aggregated statistics."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
summary_file = output_dir / "summary.csv"
|
|
assert summary_file.exists()
|
|
assert str(summary_file) in files
|
|
|
|
with open(summary_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Metric", "Value"]
|
|
assert len(rows) >= 7
|
|
|
|
metrics = {row[0]: row[1] for row in rows[1:]}
|
|
assert "Scanned Ports" in metrics
|
|
assert "Ports with TLS Support" in metrics
|
|
assert "Cipher Suites Checked" in metrics
|
|
|
|
def test_export_cipher_suites_port_443(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test cipher suites export for port 443."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
accepted_files = [
|
|
f for f in files if "443_cipher_suites" in f and "accepted" in f
|
|
]
|
|
assert len(accepted_files) > 0
|
|
|
|
accepted_file = Path(accepted_files[0])
|
|
assert accepted_file.exists()
|
|
|
|
with open(accepted_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Cipher Suite", "IANA", "BSI", "Valid Until", "Compliant"]
|
|
assert len(rows) > 1
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 5
|
|
assert row[4] in ["Yes", "No", "-"]
|
|
|
|
def test_export_supported_groups_port_636(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test supported groups export for port 636."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
groups_files = [f for f in files if "636_supported_groups.csv" in f]
|
|
|
|
if groups_files:
|
|
groups_file = Path(groups_files[0])
|
|
assert groups_file.exists()
|
|
|
|
with open(groups_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Group", "IANA", "BSI", "Valid Until", "Compliant"]
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 5
|
|
assert row[4] in ["Yes", "No", "-"]
|
|
|
|
def test_export_missing_groups_port_443(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test missing groups export for port 443."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
bsi_files = [f for f in files if "443_missing_groups_bsi.csv" in f]
|
|
|
|
if bsi_files:
|
|
bsi_file = Path(bsi_files[0])
|
|
assert bsi_file.exists()
|
|
|
|
with open(bsi_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Group", "TLS Versions", "Valid Until"]
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 3
|
|
|
|
def test_export_certificates_port_636(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test certificates export for port 636."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
cert_files = [f for f in files if "636_certificates.csv" in f]
|
|
|
|
if cert_files:
|
|
cert_file = Path(cert_files[0])
|
|
assert cert_file.exists()
|
|
|
|
with open(cert_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
expected_headers = [
|
|
"Position",
|
|
"Subject",
|
|
"Issuer",
|
|
"Valid From",
|
|
"Valid Until",
|
|
"Key Type",
|
|
"Key Size",
|
|
"Compliant",
|
|
]
|
|
assert rows[0] == expected_headers
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 8
|
|
assert row[7] in ["Yes", "No", "-"]
|
|
|
|
def test_export_vulnerabilities_port_443(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test vulnerabilities export for port 443."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
vuln_files = [f for f in files if "443_vulnerabilities.csv" in f]
|
|
|
|
if vuln_files:
|
|
vuln_file = Path(vuln_files[0])
|
|
assert vuln_file.exists()
|
|
|
|
with open(vuln_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Type", "Vulnerable", "Details"]
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 3
|
|
assert row[1] in ["Yes", "No", "-"]
|
|
|
|
def test_export_protocol_features_port_636(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test protocol features export for port 636."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
protocol_files = [f for f in files if "636_protocol_features.csv" in f]
|
|
|
|
if protocol_files:
|
|
protocol_file = Path(protocol_files[0])
|
|
assert protocol_file.exists()
|
|
|
|
with open(protocol_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Feature", "Supported", "Details"]
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 3
|
|
assert row[1] in ["Yes", "No", "-"]
|
|
|
|
def test_export_session_features_port_443(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test session features export for port 443."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
session_files = [f for f in files if "443_session_features.csv" in f]
|
|
|
|
if session_files:
|
|
session_file = Path(session_files[0])
|
|
assert session_file.exists()
|
|
|
|
with open(session_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
expected_headers = [
|
|
"Feature",
|
|
"Client Initiated",
|
|
"Secure",
|
|
"Session ID",
|
|
"TLS Ticket",
|
|
"Details",
|
|
]
|
|
assert rows[0] == expected_headers
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 6
|
|
for i in range(1, 5):
|
|
assert row[i] in ["Yes", "No", "-"]
|
|
|
|
def test_export_http_headers_port_636(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test HTTP headers export for port 636."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
header_files = [f for f in files if "636_http_headers.csv" in f]
|
|
|
|
if header_files:
|
|
header_file = Path(header_files[0])
|
|
assert header_file.exists()
|
|
|
|
with open(header_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Header", "Present", "Value"]
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 3
|
|
assert row[1] in ["Yes", "No", "-"]
|
|
|
|
def test_export_compliance_status_port_443(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test compliance status export for port 443."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
compliance_files = [f for f in files if "443_compliance_status.csv" in f]
|
|
|
|
if compliance_files:
|
|
compliance_file = Path(compliance_files[0])
|
|
assert compliance_file.exists()
|
|
|
|
with open(compliance_file, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
rows = list(reader)
|
|
|
|
assert rows[0] == ["Category", "Checked", "Compliant", "Percentage"]
|
|
|
|
for row in rows[1:]:
|
|
assert len(row) == 4
|
|
assert "%" in row[3]
|
|
|
|
def test_generate_csv_reports_all_files(
|
|
self, test_db_path: str, tmp_path: Path
|
|
) -> None:
|
|
"""Test that generate_csv_reports creates expected files."""
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
files = generate_csv_reports(test_db_path, 1, str(output_dir))
|
|
|
|
assert len(files) > 0
|
|
assert any("summary.csv" in f for f in files)
|
|
assert any("443_" in f for f in files)
|
|
assert any("636_" in f for f in files)
|
|
|
|
for file_path in files:
|
|
assert Path(file_path).exists()
|
|
assert Path(file_path).suffix == ".csv"
|