- 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
249 lines
8.6 KiB
Python
249 lines
8.6 KiB
Python
"""Test to verify that IANA SSH tables remain empty due to import issues."""
|
|
|
|
import argparse
|
|
import os
|
|
import sqlite3
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from src.sslysze_scan.commands.update_iana import handle_update_iana_command
|
|
|
|
|
|
def test_iana_ssh_tables_populated_after_successful_import():
|
|
"""Test that IANA SSH tables are populated after successful import.
|
|
|
|
This test verifies that the IANA SSH parameter import now succeeds
|
|
and populates the SSH tables with data using local XML fixtures.
|
|
"""
|
|
# Use the template database for this test
|
|
import shutil
|
|
|
|
template_db = (
|
|
Path(__file__).parent.parent.parent
|
|
/ "src"
|
|
/ "sslysze_scan"
|
|
/ "data"
|
|
/ "crypto_standards.db"
|
|
)
|
|
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as temp_db:
|
|
db_path = temp_db.name
|
|
# Copy the template database to use as our test database
|
|
shutil.copy2(template_db, db_path)
|
|
|
|
# Path to local XML fixtures
|
|
fixtures_dir = Path(__file__).parent.parent / "fixtures" / "iana_xml"
|
|
|
|
def mock_fetch_xml(url: str, timeout: int = 30) -> str:
|
|
"""Mock function that returns local XML files instead of downloading."""
|
|
if "tls-parameters" in url:
|
|
xml_file = fixtures_dir / "tls-parameters-minimal.xml"
|
|
elif "ikev2-parameters" in url:
|
|
xml_file = fixtures_dir / "ikev2-parameters-minimal.xml"
|
|
elif "ssh-parameters" in url:
|
|
xml_file = fixtures_dir / "ssh-parameters-minimal.xml"
|
|
else:
|
|
raise ValueError(f"Unknown URL: {url}")
|
|
|
|
return xml_file.read_text(encoding="utf-8")
|
|
|
|
try:
|
|
# Check initial state of SSH tables
|
|
conn = sqlite3.connect(db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# Count initial entries in IANA SSH tables
|
|
ssh_tables = [
|
|
"iana_ssh_kex_methods",
|
|
"iana_ssh_encryption_algorithms",
|
|
"iana_ssh_mac_algorithms",
|
|
"iana_ssh_compression_algorithms",
|
|
]
|
|
|
|
initial_counts = {}
|
|
for table in ssh_tables:
|
|
cursor.execute(f"SELECT COUNT(*) FROM {table}")
|
|
initial_counts[table] = cursor.fetchone()[0]
|
|
|
|
conn.close()
|
|
|
|
# Run the IANA update command directly with mocked fetch and validation
|
|
with (
|
|
patch(
|
|
"src.sslysze_scan.commands.update_iana.fetch_xml_from_url",
|
|
side_effect=mock_fetch_xml,
|
|
),
|
|
patch(
|
|
"src.sslysze_scan.iana_validator.MIN_ROWS",
|
|
{
|
|
"iana_tls_cipher_suites": 1,
|
|
"iana_tls_signature_schemes": 1,
|
|
"iana_tls_supported_groups": 1,
|
|
"iana_tls_alerts": 1,
|
|
"iana_tls_content_types": 1,
|
|
"iana_ikev2_encryption_algorithms": 1,
|
|
"iana_ikev2_prf_algorithms": 1,
|
|
"iana_ikev2_integrity_algorithms": 1,
|
|
"iana_ikev2_dh_groups": 1,
|
|
"iana_ikev2_authentication_methods": 1,
|
|
"iana_ssh_kex_methods": 1,
|
|
"iana_ssh_encryption_algorithms": 1,
|
|
"iana_ssh_mac_algorithms": 1,
|
|
"iana_ssh_compression_algorithms": 1,
|
|
},
|
|
),
|
|
):
|
|
args = argparse.Namespace(database=db_path)
|
|
result = handle_update_iana_command(args)
|
|
|
|
# Verify that the command succeeded
|
|
assert result == 0, (
|
|
f"IANA update command should succeed, got return code: {result}"
|
|
)
|
|
|
|
# Connect to database again to check if tables are now populated
|
|
conn = sqlite3.connect(db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# Check that SSH tables are now populated and get final counts
|
|
final_counts = {}
|
|
for table in ssh_tables:
|
|
cursor.execute(f"SELECT COUNT(*) FROM {table}")
|
|
final_count = cursor.fetchone()[0]
|
|
final_counts[table] = final_count
|
|
|
|
# The tables should now have data after successful import
|
|
# Note: Using minimal fixtures, so counts may be lower than full data
|
|
assert final_count > 0, (
|
|
f"Table {table} should be populated after successful import"
|
|
)
|
|
|
|
conn.close()
|
|
|
|
print(
|
|
"Test confirmed: IANA SSH tables are properly populated after "
|
|
"successful import using minimal fixtures"
|
|
)
|
|
print(f"Initial counts (from template DB): {initial_counts}")
|
|
print(f"Final counts (from minimal fixtures): {final_counts}")
|
|
|
|
finally:
|
|
# Clean up temporary database
|
|
if os.path.exists(db_path):
|
|
os.unlink(db_path)
|
|
|
|
|
|
def test_compliance_works_with_populated_iana_ssh_tables():
|
|
"""Test that compliance checking works appropriately when IANA SSH tables are populated."""
|
|
# Use the template database for this test
|
|
import shutil
|
|
|
|
template_db = (
|
|
Path(__file__).parent.parent.parent
|
|
/ "src"
|
|
/ "sslysze_scan"
|
|
/ "data"
|
|
/ "crypto_standards.db"
|
|
)
|
|
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as temp_db:
|
|
db_path = temp_db.name
|
|
# Copy the template database to use as our test database
|
|
shutil.copy2(template_db, db_path)
|
|
|
|
try:
|
|
# Connect to database to check SSH table status
|
|
conn = sqlite3.connect(db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# Verify that IANA SSH tables are now populated
|
|
cursor.execute("SELECT COUNT(*) FROM iana_ssh_kex_methods")
|
|
kex_count = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM iana_ssh_encryption_algorithms")
|
|
enc_count = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM iana_ssh_mac_algorithms")
|
|
mac_count = cursor.fetchone()[0]
|
|
|
|
conn.close()
|
|
|
|
# Verify that the tables are populated (this is the corrected behavior)
|
|
assert kex_count > 0, (
|
|
f"IANA SSH KEX table should be populated but has {kex_count} entries"
|
|
)
|
|
assert enc_count > 0, (
|
|
f"IANA SSH encryption table should be populated but has {enc_count} entries"
|
|
)
|
|
assert mac_count > 0, (
|
|
f"IANA SSH MAC table should be populated but has {mac_count} entries"
|
|
)
|
|
|
|
print(
|
|
f"Confirmed populated SSH tables: KEX={kex_count}, ENC={enc_count}, MAC={mac_count}"
|
|
)
|
|
|
|
finally:
|
|
# Clean up temporary database
|
|
if os.path.exists(db_path):
|
|
os.unlink(db_path)
|
|
|
|
|
|
def test_iana_ssh_tables_should_not_be_empty_but_are():
|
|
"""Test that fails if IANA SSH tables are empty (demonstrating the issue).
|
|
|
|
This test expects SSH tables to have data but will fail because they are empty
|
|
due to the import column mismatch issue.
|
|
"""
|
|
# Use the template database for this test
|
|
import shutil
|
|
|
|
template_db = (
|
|
Path(__file__).parent.parent.parent
|
|
/ "src"
|
|
/ "sslysze_scan"
|
|
/ "data"
|
|
/ "crypto_standards.db"
|
|
)
|
|
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as temp_db:
|
|
db_path = temp_db.name
|
|
# Copy the template database to use as our test database
|
|
shutil.copy2(template_db, db_path)
|
|
|
|
try:
|
|
# Connect to database to check SSH table status
|
|
conn = sqlite3.connect(db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# Check that IANA SSH tables are empty (this demonstrates the problem)
|
|
cursor.execute("SELECT COUNT(*) FROM iana_ssh_kex_methods")
|
|
kex_count = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM iana_ssh_encryption_algorithms")
|
|
enc_count = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM iana_ssh_mac_algorithms")
|
|
mac_count = cursor.fetchone()[0]
|
|
|
|
conn.close()
|
|
|
|
# This assertion will fail, demonstrating the issue
|
|
# The tables SHOULD have entries after a successful IANA import, but they don't
|
|
assert kex_count > 0, (
|
|
f"IANA SSH KEX table should have entries but has {kex_count} - this demonstrates the import issue"
|
|
)
|
|
assert enc_count > 0, (
|
|
f"IANA SSH encryption table should have entries but has {enc_count} - this demonstrates the import issue"
|
|
)
|
|
assert mac_count > 0, (
|
|
f"IANA SSH MAC table should have entries but has {mac_count} - this demonstrates the import issue"
|
|
)
|
|
|
|
print(
|
|
f"SSH tables have data as expected: KEX={kex_count}, ENC={enc_count}, MAC={mac_count}"
|
|
)
|
|
|
|
finally:
|
|
# Clean up temporary database
|
|
if os.path.exists(db_path):
|
|
os.unlink(db_path)
|