- 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
203 lines
7.1 KiB
Python
203 lines
7.1 KiB
Python
"""Tests for IANA XML parsing functionality."""
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
import pytest
|
|
|
|
from sslysze_scan.iana_parser import (
|
|
extract_field_value,
|
|
find_registry,
|
|
get_element_text,
|
|
is_unassigned,
|
|
parse_xml_with_namespace_support,
|
|
process_xref_elements,
|
|
)
|
|
|
|
|
|
class TestParseXmlWithNamespace:
|
|
"""Tests for XML parsing with namespace detection."""
|
|
|
|
def test_parse_tls_parameters_with_namespace(self) -> None:
|
|
"""Test parsing TLS parameters XML with IANA namespace."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
assert root is not None
|
|
assert ns is not None
|
|
assert "iana" in ns
|
|
assert ns["iana"] == "http://www.iana.org/assignments"
|
|
|
|
def test_parse_nonexistent_file(self) -> None:
|
|
"""Test that parsing nonexistent file raises FileNotFoundError."""
|
|
with pytest.raises(FileNotFoundError):
|
|
parse_xml_with_namespace_support("nonexistent.xml")
|
|
|
|
|
|
class TestFindRegistry:
|
|
"""Tests for finding registry by ID."""
|
|
|
|
def test_find_cipher_suites_registry(self) -> None:
|
|
"""Test finding TLS cipher suites registry."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
|
|
assert registry is not None
|
|
assert registry.get("id") == "tls-parameters-4"
|
|
|
|
def test_find_nonexistent_registry(self) -> None:
|
|
"""Test that finding nonexistent registry raises ValueError."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
with pytest.raises(ValueError, match="Registry with ID '.*' not found"):
|
|
find_registry(root, "nonexistent-registry", ns)
|
|
|
|
|
|
class TestGetElementText:
|
|
"""Tests for element text extraction."""
|
|
|
|
def test_get_element_text_with_namespace(self) -> None:
|
|
"""Test extracting text from element with namespace."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
records = registry.findall("iana:record", ns)
|
|
first_record = records[0]
|
|
|
|
value = get_element_text(first_record, "value", ns)
|
|
assert value == "0x13,0x01"
|
|
|
|
description = get_element_text(first_record, "description", ns)
|
|
assert description == "TLS_AES_128_GCM_SHA256"
|
|
|
|
def test_get_element_text_nonexistent(self) -> None:
|
|
"""Test that nonexistent element returns empty string."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
records = registry.findall("iana:record", ns)
|
|
first_record = records[0]
|
|
|
|
result = get_element_text(first_record, "nonexistent", ns)
|
|
assert result == ""
|
|
|
|
|
|
class TestProcessXrefElements:
|
|
"""Tests for xref element processing."""
|
|
|
|
def test_process_single_xref(self) -> None:
|
|
"""Test processing single xref element."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
records = registry.findall("iana:record", ns)
|
|
first_record = records[0]
|
|
|
|
xref_str = process_xref_elements(first_record, ns)
|
|
assert "rfc:rfc8446" in xref_str
|
|
|
|
def test_process_no_xref(self) -> None:
|
|
"""Test processing record without xref elements."""
|
|
xml_str = """
|
|
<record xmlns="http://www.iana.org/assignments">
|
|
<value>0x13,0x01</value>
|
|
<description>Test</description>
|
|
</record>
|
|
"""
|
|
record = ET.fromstring(xml_str)
|
|
ns = {"iana": "http://www.iana.org/assignments"}
|
|
|
|
xref_str = process_xref_elements(record, ns)
|
|
assert xref_str == ""
|
|
|
|
|
|
class TestExtractFieldValue:
|
|
"""Tests for field value extraction."""
|
|
|
|
def test_extract_recommended_field(self) -> None:
|
|
"""Test extracting Recommended field."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
records = registry.findall("iana:record", ns)
|
|
first_record = records[0]
|
|
|
|
rec = extract_field_value(first_record, "Recommended", ns)
|
|
assert rec == "Y"
|
|
|
|
def test_extract_rfc_draft_field(self) -> None:
|
|
"""Test extracting RFC/Draft field via xref processing."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
records = registry.findall("iana:record", ns)
|
|
first_record = records[0]
|
|
|
|
rfc_draft = extract_field_value(first_record, "RFC/Draft", ns)
|
|
assert "rfc:rfc8446" in rfc_draft
|
|
|
|
|
|
class TestExtractUpdatedDate:
|
|
"""Tests for extracting updated date from XML."""
|
|
|
|
def test_extract_updated_from_tls_xml(self) -> None:
|
|
"""Test extracting updated date from TLS parameters XML."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
|
|
with open(xml_path, encoding="utf-8") as f:
|
|
xml_content = f.read()
|
|
|
|
lines = xml_content.split("\n")[:10]
|
|
updated_line = [line for line in lines if "<updated>" in line]
|
|
|
|
assert len(updated_line) == 1
|
|
assert "2025-12-03" in updated_line[0]
|
|
|
|
|
|
class TestIsUnassigned:
|
|
"""Tests for unassigned entry detection."""
|
|
|
|
def test_is_unassigned_numeric_range(self) -> None:
|
|
"""Test detection of numeric range values."""
|
|
xml_str = """
|
|
<record xmlns="http://www.iana.org/assignments">
|
|
<value>42-255</value>
|
|
<description>Unassigned</description>
|
|
</record>
|
|
"""
|
|
record = ET.fromstring(xml_str)
|
|
ns = {"iana": "http://www.iana.org/assignments"}
|
|
|
|
assert is_unassigned(record, ns) is True
|
|
|
|
def test_is_unassigned_hex_range(self) -> None:
|
|
"""Test detection of hex range values."""
|
|
xml_str = """
|
|
<record xmlns="http://www.iana.org/assignments">
|
|
<value>0x0000-0x0200</value>
|
|
<description>Reserved for backward compatibility</description>
|
|
</record>
|
|
"""
|
|
record = ET.fromstring(xml_str)
|
|
ns = {"iana": "http://www.iana.org/assignments"}
|
|
|
|
assert is_unassigned(record, ns) is True
|
|
|
|
def test_is_unassigned_false(self) -> None:
|
|
"""Test that assigned entries return False."""
|
|
xml_path = "tests/fixtures/iana_xml/tls-parameters-minimal.xml"
|
|
root, ns = parse_xml_with_namespace_support(xml_path)
|
|
|
|
registry = find_registry(root, "tls-parameters-4", ns)
|
|
records = registry.findall("iana:record", ns)
|
|
first_record = records[0]
|
|
|
|
assert is_unassigned(first_record, ns) is False
|