From 9e85c3f421c6f6d8b68f9db8d04e1aa45ede4aea Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Wed, 9 Aug 2023 16:08:56 -0600 Subject: [PATCH 01/11] Add short descriptions to conn module Add result_description to results with shorter wording for UI usage --- modules/test/base/python/src/test_module.py | 14 ++++++- modules/test/conn/conf/module_config.json | 44 ++++++++++++++++----- modules/test/conn/python/requirements.txt | 3 +- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 8bee611b9..9a967a62b 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """Base class for all core test module functions""" import json import logger @@ -107,6 +106,19 @@ def run_tests(self): test['result_details'] = result[1] else: test['result'] = 'skipped' + + # Generate the short result description based on result value + if test['result'] == 'compliant': + test['result_description'] = test[ + 'short_description'] if 'short_description' in test else test[ + 'name'] + ' passed - see result details for more info' + elif test['result'] == 'non-compliant': + test['result_description'] = test[ + 'name'] + ' failed - see result details for more info' + else: + test['result_description'] = test[ + 'name'] + ' skipped - see result details for more info' + test['end'] = datetime.now().isoformat() duration = datetime.fromisoformat(test['end']) - datetime.fromisoformat( test['start']) diff --git a/modules/test/conn/conf/module_config.json b/modules/test/conn/conf/module_config.json index 3e06cc891..860b04e0b 100644 --- a/modules/test/conn/conf/module_config.json +++ b/modules/test/conn/conf/module_config.json @@ -6,31 +6,48 @@ "description": "Connection tests" }, "network": true, + "interface_control": true, "docker": { "depends_on": "base", "enable_container": true, "timeout": 600 }, "tests": [ + { + "name": "connection.dhcp.disconnect", + "description": "The device under test has received an IP address from the DHCP server and responds to an ICMP echo (ping) request", + "expected_behavior": "The device is not setup with a static IP address. The device accepts an IP address from a DHCP server (RFC 2131) and responds succesfully to an ICMP echo (ping) request.", + "short_description": "Device has received an IP address after port disconnect" + }, + { + "name": "connection.dhcp.disconnect_ip_change", + "description": "Update device IP on the DHCP server and reconnect the device. Does the device receive the new IP address?", + "expected_behavior": "Device recieves a new IP address within the range that is specified on the DHCP server. Device should respond to aping on this new address.", + "short_description": "Device has received new IP address after port disconnect" + }, { "name": "connection.dhcp_address", "description": "The device under test has received an IP address from the DHCP server and responds to an ICMP echo (ping) request", - "expected_behavior": "The device is not setup with a static IP address. The device accepts an IP address from a DHCP server (RFC 2131) and responds succesfully to an ICMP echo (ping) request." + "expected_behavior": "The device is not setup with a static IP address. The device accepts an IP address from a DHCP server (RFC 2131) and responds succesfully to an ICMP echo (ping) request.", + "short_description": "Device has received a DHCP provided IP address" }, { "name": "connection.mac_address", "description": "Check and note device physical address.", - "expected_behavior": "N/A" + "expected_behavior": "N/A", + "short_description": "Device MAC address resolved" }, { "name": "connection.mac_oui", "description": "The device under test hs a MAC address prefix that is registered against a known manufacturer.", - "expected_behavior": "The MAC address prefix is registered in the IEEE Organizationally Unique Identifier database." + "expected_behavior": "The MAC address prefix is registered in the IEEE Organizationally Unique Identifier database.", + "short_description": "OUI for MAC address resolved" }, { "name": "connection.private_address", "description": "The device under test accepts an IP address that is compliant with RFC 1918 Address Allocation for Private Internets.", "expected_behavior": "The device under test accepts IP addresses within all ranges specified in RFC 1918 and communicates using these addresses. The Internet Assigned Numbers Authority (IANA) has reserved the following three blocks of the IP address space for private internets. 10.0.0.0 - 10.255.255.255.255 (10/8 prefix). 172.16.0.0 - 172.31.255.255 (172.16/12 prefix). 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)", + "short_description": "Device supports private addresses", "config": { "ranges": [ { @@ -52,6 +69,7 @@ "name": "connection.shared_address", "description": "Ensure the device supports RFC 6598 IANA-Reserved IPv4 Prefix for Shared Address Space", "expected_behavior": "The device under test accepts IP addresses within the ranges specified in RFC 6598 and communicates using these addresses", + "short_description": "Device supports shared address space", "config": { "ranges": [ { @@ -64,32 +82,38 @@ { "name": "connection.single_ip", "description": "The network switch port connected to the device reports only one IP address for the device under test.", - "expected_behavior": "The device under test does not behave as a network switch and only requets one IP address. This test is to avoid that devices implement network switches that allow connecting strings of daisy chained devices to one single network port, as this would not make 802.1x port based authentication possible." + "expected_behavior": "The device under test does not behave as a network switch and only requets one IP address. This test is to avoid that devices implement network switches that allow connecting strings of daisy chained devices to one single network port, as this would not make 802.1x port based authentication possible.", + "short_description": "Device only reports one IP address" }, { "name": "connection.target_ping", "description": "The device under test responds to an ICMP echo (ping) request.", - "expected_behavior": "The device under test responds to an ICMP echo (ping) request." + "expected_behavior": "The device under test responds to an ICMP echo (ping) request.", + "short_description": "Device responds to a ping request" }, { "name": "connection.ipaddr.ip_change", - "description": "The device responds to a ping (ICMP echo request) to the new IP address it has received after the initial dHCP lease has expired.", - "expected_behavior": "If the lease expires before the client receiveds a DHCPACK, the client moves to INIT state, MUST immediately stop any other network processing and requires network initialization parameters as if the client were uninitialized. If the client then receives a DHCPACK allocating the client its previous network addres, the client SHOULD continue network processing. If the client is given a new network address, it MUST NOT continue using the previous network address and SHOULD notify the local users of the problem." + "description": "The device responds to a ping (ICMP echo request) to the new IP address it has received after the initial DHCP lease has expired.", + "expected_behavior": "If the lease expires before the client receiveds a DHCPACK, the client moves to INIT state, MUST immediately stop any other network processing and requires network initialization parameters as if the client were uninitialized. If the client then receives a DHCPACK allocating the client its previous network addres, the client SHOULD continue network processing. If the client is given a new network address, it MUST NOT continue using the previous network address and SHOULD notify the local users of the problem.", + "short_description": "Device receives an IP change from the DHCP server" }, { "name": "connection.ipaddr.dhcp_failover", "description": "The device has requested a DHCPREQUEST/REBIND to the DHCP failover server after the primary DHCP server has been brought down.", - "expected_behavior": "" + "expected_behavior": "", + "short_description": "Device receives IP address from primary and failover DHCP servers" }, { "name": "connection.ipv6_slaac", "description": "The device forms a valid IPv6 address as a combination of the IPv6 router prefix and the device interface identifier", - "expected_behavior": "The device under test complies with RFC4862 and forms a valid IPv6 SLAAC address" + "expected_behavior": "The device under test complies with RFC4862 and forms a valid IPv6 SLAAC address", + "short_description": "Device uses an IPv6 address using SLAAC" }, { "name": "connection.ipv6_ping", "description": "The device responds to an IPv6 ping (ICMPv6 Echo) request to the SLAAC address", - "expected_behavior": "The device responds to the ping as per RFC4443" + "expected_behavior": "The device responds to the ping as per RFC4443", + "short_description": "Device responds to an IPv6 SLAAC address ping request" } ] } diff --git a/modules/test/conn/python/requirements.txt b/modules/test/conn/python/requirements.txt index 2b8d18750..c523787b9 100644 --- a/modules/test/conn/python/requirements.txt +++ b/modules/test/conn/python/requirements.txt @@ -1 +1,2 @@ -pyOpenSSL \ No newline at end of file +pyOpenSSL +scapy \ No newline at end of file From 74340d9d27a6e07988232e7c8b60d93c54a4c9fb Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Wed, 9 Aug 2023 17:24:20 -0600 Subject: [PATCH 02/11] Add result details to ipv6 tests --- .../test/conn/python/src/connection_module.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/modules/test/conn/python/src/connection_module.py b/modules/test/conn/python/src/connection_module.py index 169fb98c3..248edc536 100644 --- a/modules/test/conn/python/src/connection_module.py +++ b/modules/test/conn/python/src/connection_module.py @@ -253,6 +253,7 @@ def _get_oui_manufacturer(self, mac_address): def _connection_ipv6_slaac(self): LOGGER.info('Running connection.ipv6_slaac') + result = None packet_capture = rdpcap(MONITOR_CAPTURE_FILE) sends_ipv6 = False @@ -265,27 +266,31 @@ def _connection_ipv6_slaac(self): if ipv6_addr.startswith(SLAAC_PREFIX): self._device_ipv6_addr = ipv6_addr LOGGER.info(f'Device has formed SLAAC address {ipv6_addr}') - return True - - if sends_ipv6: - LOGGER.info('Device does not support IPv6 SLAAC') - else: - LOGGER.info('Device does not support IPv6') - return False + result = True, f'Device has formed SLAAC address {ipv6_addr}' + if result is None: + if sends_ipv6: + LOGGER.info('Device does not support IPv6 SLAAC') + result = False, 'Device does not support IPv6 SLAAC' + else: + LOGGER.info('Device does not support IPv6') + result = False, 'Device does not support IPv6' + return result def _connection_ipv6_ping(self): LOGGER.info('Running connection.ipv6_ping') - + result = None + if self._device_ipv6_addr is None: LOGGER.info('No IPv6 SLAAC address found. Cannot ping') - return - - if self._ping(self._device_ipv6_addr): - LOGGER.info(f'Device responds to IPv6 ping on {self._device_ipv6_addr}') - return True + result = None, 'No IPv6 SLAAc address found. Cannot ping' else: - LOGGER.info('Device does not respond to IPv6 ping') - return False + if self._ping(self._device_ipv6_addr): + LOGGER.info(f'Device responds to IPv6 ping on {self._device_ipv6_addr}') + result = True, f'Device responds to IPv6 ping on {self._device_ipv6_addr}' + else: + LOGGER.info('Device does not respond to IPv6 ping') + result = False, 'Device does not respond to IPv6 ping' + return result def _ping(self, host): cmd = 'ping -c 1 ' + str(host) From a42e2a64d45139b715bd55f983d02d9c291bfda3 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 08:13:16 -0600 Subject: [PATCH 03/11] Update test descriptions in baseline module --- modules/test/baseline/conf/module_config.json | 9 ++++++--- .../baseline/python/src/baseline_module.py | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/test/baseline/conf/module_config.json b/modules/test/baseline/conf/module_config.json index 4c0cd08d8..f4daf0e36 100644 --- a/modules/test/baseline/conf/module_config.json +++ b/modules/test/baseline/conf/module_config.json @@ -15,17 +15,20 @@ { "name": "baseline.pass", "description": "Simulate a compliant test", - "expected_behavior": "A compliant test result is generated" + "expected_behavior": "A compliant test result is generated", + "short_description": "A compliant test result is generated" }, { "name": "baseline.fail", "description": "Simulate a non-compliant test", - "expected_behavior": "A non-compliant test result is generated" + "expected_behavior": "A non-compliant test result is generated", + "short_description": "A non-compliant test result is generated" }, { "name": "baseline.skip", "description": "Simulate a skipped test", - "expected_behavior": "A skipped test result is generated" + "expected_behavior": "A skipped test result is generated", + "short_description": "A skipped test result is generated" } ] } diff --git a/modules/test/baseline/python/src/baseline_module.py b/modules/test/baseline/python/src/baseline_module.py index 22555d369..c838fe853 100644 --- a/modules/test/baseline/python/src/baseline_module.py +++ b/modules/test/baseline/python/src/baseline_module.py @@ -15,7 +15,7 @@ """Baseline test module""" from test_module import TestModule -LOG_NAME = "test_baseline" +LOG_NAME = 'test_baseline' LOGGER = None @@ -28,15 +28,16 @@ def __init__(self, module): LOGGER = self._get_logger() def _baseline_pass(self): - LOGGER.info("Running baseline pass test") - LOGGER.info("Baseline pass test finished") - return True + LOGGER.info('Running baseline pass test') + LOGGER.info('Baseline pass test finished') + return True, 'Baseline pass test ran succesfully' def _baseline_fail(self): - LOGGER.info("Running baseline pass test") - LOGGER.info("Baseline pass test finished") - return False + LOGGER.info('Running baseline fail test') + LOGGER.info('Baseline fail test finished') + return False, 'Baseline fail test ran succesfully' def _baseline_skip(self): - LOGGER.info("Running baseline pass test") - LOGGER.info("Baseline pass test finished") + LOGGER.info("Running baseline skip test") + LOGGER.info("Baseline skip test finished") + return None, 'Baseline skip test ran succesfully' From 400d8c227f48b58389b965d326877fd15366710e Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 12:35:41 -0600 Subject: [PATCH 04/11] Update dns test result details --- modules/test/dns/conf/module_config.json | 9 ++-- modules/test/dns/python/src/dns_module.py | 62 +++++++++++++++-------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/modules/test/dns/conf/module_config.json b/modules/test/dns/conf/module_config.json index 177537b69..b5e3c8420 100644 --- a/modules/test/dns/conf/module_config.json +++ b/modules/test/dns/conf/module_config.json @@ -15,16 +15,19 @@ { "name": "dns.network.from_device", "description": "Verify the device sends DNS requests", - "expected_behavior": "The device sends DNS requests." + "expected_behavior": "The device sends DNS requests.", + "short_description": "The device sends DNS requests." }, { "name": "dns.network.from_dhcp", "description": "Verify the device allows for a DNS server to be entered automatically", - "expected_behavior": "The device sends DNS requests to the DNS server provided by the DHCP server" + "expected_behavior": "The device sends DNS requests to the DNS server provided by the DHCP server", + "short_description": "The device sends DNS requests to local DNS server." }, { "name": "dns.mdns", - "description": "If the device has MDNS (or any kind of IP multicast), can it be disabled" + "description": "If the device has MDNS (or any kind of IP multicast), can it be disabled", + "short_description": "MDNS traffic detected from device" } ] } diff --git a/modules/test/dns/python/src/dns_module.py b/modules/test/dns/python/src/dns_module.py index aecbd5bd1..bc9adde8f 100644 --- a/modules/test/dns/python/src/dns_module.py +++ b/modules/test/dns/python/src/dns_module.py @@ -32,7 +32,7 @@ def __init__(self, module): global LOGGER LOGGER = self._get_logger() - def _check_dns_traffic(self, tcpdump_filter): + def _has_dns_traffic(self, tcpdump_filter): dns_server_queries = self._exec_tcpdump(tcpdump_filter,DNS_SERVER_CAPTURE_FILE) LOGGER.info('DNS Server queries found: ' + str(len(dns_server_queries))) @@ -43,48 +43,66 @@ def _check_dns_traffic(self, tcpdump_filter): LOGGER.info('Monitor DNS queries found: ' + str(len(dns_monitor_queries))) num_query_dns = len(dns_server_queries) + len(dns_startup_queries) + len(dns_monitor_queries) - LOGGER.info('DNS queries found: ' + str(num_query_dns)) - dns_traffic_detected = num_query_dns > 0 - LOGGER.info('DNS traffic detected: ' + str(dns_traffic_detected)) - return dns_traffic_detected + + return num_query_dns > 0 def _dns_network_from_dhcp(self): LOGGER.info("Running dns.network.from_dhcp") + result = None LOGGER.info('Checking DNS traffic for configured DHCP DNS server: ' + self._dns_server) - # Check if the device DNS traffic is to appropriate server + # Check if the device DNS traffic is to appropriate local DHCP provided server tcpdump_filter = f'dst port 53 and dst host {self._dns_server} and ether src {self._device_mac}' - - result = self._check_dns_traffic(tcpdump_filter=tcpdump_filter) - - LOGGER.info('DNS traffic detected to configured DHCP DNS server: ' + - str(result)) + dns_packets_local = self._has_dns_traffic(tcpdump_filter=tcpdump_filter) + + # Check if the device sends any DNS traffic to non-DHCP provided server + tcpdump_filter = f'dst port 53 and dst not host {self._dns_server} ether src {self._device_mac}' + dns_packets_not_local = self._has_dns_traffic(tcpdump_filter=tcpdump_filter) + + if dns_packets_local or dns_packets_not_local: + if dns_packets_not_local: + result = False, 'DNS traffic detected to non-DHCP provided server' + else: + LOGGER.info('DNS traffic detected only to configured DHCP DNS server') + result = True, 'DNS traffic detected only to DHCP provided server' + else: + LOGGER.info('No DNS traffic detected from the device') + result = None, 'No DNS traffic detected from the device' return result def _dns_network_from_device(self): LOGGER.info("Running dns.network.from_device") + result = None LOGGER.info('Checking DNS traffic from device: ' + self._device_mac) - # Check if the device DNS traffic is to appropriate server + # Check if the device DNS traffic tcpdump_filter = f'dst port 53 and ether src {self._device_mac}' - - result = self._check_dns_traffic(tcpdump_filter=tcpdump_filter) - - LOGGER.info('DNS traffic detected from device: ' + str(result)) + dns_packetes = self._has_dns_traffic(tcpdump_filter=tcpdump_filter) + + if dns_packetes: + LOGGER.info('DNS traffic detected from device') + result = True, 'DNS traffic detected from device' + else: + LOGGER.info('No DNS traffic detected from the device') + result = False, 'No DNS traffic detected from the device' return result def _dns_mdns(self): LOGGER.info("Running dns.mdns") - + result = None # Check if the device sends any MDNS traffic tcpdump_filter = f'udp port 5353 and ether src {self._device_mac}' - - result = self._check_dns_traffic(tcpdump_filter=tcpdump_filter) - - LOGGER.info('MDNS traffic detected from device: ' + str(result)) - return not result + dns_packetes = self._has_dns_traffic(tcpdump_filter=tcpdump_filter) + + if dns_packetes: + LOGGER.info('MDNS traffic detected from device') + result = True, 'MDNS traffic detected from device' + else: + LOGGER.info('No MDNS traffic detected from the device') + result = None, 'No MDNS traffic detected from the device' + return result def _exec_tcpdump(self, tcpdump_filter, capture_file): From 487cd38f74727360601da842cd1c41319f2b2d45 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 12:36:42 -0600 Subject: [PATCH 05/11] Update skip results to include details when present --- modules/test/base/python/src/test_module.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 9a967a62b..519fb2433 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -101,6 +101,8 @@ def run_tests(self): else: if result[0] is None: test['result'] = 'skipped' + if len(result)>1: + test['result_details'] = result[1] else: test['result'] = 'compliant' if result[0] else 'non-compliant' test['result_details'] = result[1] From d1ea45ee86ba33c40814b89f28a752b21ada2921 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 12:50:33 -0600 Subject: [PATCH 06/11] dns module formatting --- modules/test/dns/python/src/dns_module.py | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/modules/test/dns/python/src/dns_module.py b/modules/test/dns/python/src/dns_module.py index bc9adde8f..bc56c3718 100644 --- a/modules/test/dns/python/src/dns_module.py +++ b/modules/test/dns/python/src/dns_module.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """DNS test module""" import subprocess from test_module import TestModule @@ -33,32 +32,39 @@ def __init__(self, module): LOGGER = self._get_logger() def _has_dns_traffic(self, tcpdump_filter): - dns_server_queries = self._exec_tcpdump(tcpdump_filter,DNS_SERVER_CAPTURE_FILE) + dns_server_queries = self._exec_tcpdump(tcpdump_filter, + DNS_SERVER_CAPTURE_FILE) LOGGER.info('DNS Server queries found: ' + str(len(dns_server_queries))) - dns_startup_queries = self._exec_tcpdump(tcpdump_filter,STARTUP_CAPTURE_FILE) + dns_startup_queries = self._exec_tcpdump(tcpdump_filter, + STARTUP_CAPTURE_FILE) LOGGER.info('Startup DNS queries found: ' + str(len(dns_startup_queries))) - dns_monitor_queries = self._exec_tcpdump(tcpdump_filter,MONITOR_CAPTURE_FILE) + dns_monitor_queries = self._exec_tcpdump(tcpdump_filter, + MONITOR_CAPTURE_FILE) LOGGER.info('Monitor DNS queries found: ' + str(len(dns_monitor_queries))) - num_query_dns = len(dns_server_queries) + len(dns_startup_queries) + len(dns_monitor_queries) + num_query_dns = len(dns_server_queries) + len(dns_startup_queries) + len( + dns_monitor_queries) LOGGER.info('DNS queries found: ' + str(num_query_dns)) return num_query_dns > 0 def _dns_network_from_dhcp(self): - LOGGER.info("Running dns.network.from_dhcp") + LOGGER.info('Running dns.network.from_dhcp') result = None LOGGER.info('Checking DNS traffic for configured DHCP DNS server: ' + self._dns_server) - # Check if the device DNS traffic is to appropriate local DHCP provided server - tcpdump_filter = f'dst port 53 and dst host {self._dns_server} and ether src {self._device_mac}' + # Check if the device DNS traffic is to appropriate local + # DHCP provided server + tcpdump_filter = (f'dst port 53 and dst host {self._dns_server} ' + + 'and ether src {self._device_mac}') dns_packets_local = self._has_dns_traffic(tcpdump_filter=tcpdump_filter) # Check if the device sends any DNS traffic to non-DHCP provided server - tcpdump_filter = f'dst port 53 and dst not host {self._dns_server} ether src {self._device_mac}' + tcpdump_filter = (f'dst port 53 and dst not host {self._dns_server} ' + + 'ether src {self._device_mac}') dns_packets_not_local = self._has_dns_traffic(tcpdump_filter=tcpdump_filter) if dns_packets_local or dns_packets_not_local: @@ -73,7 +79,7 @@ def _dns_network_from_dhcp(self): return result def _dns_network_from_device(self): - LOGGER.info("Running dns.network.from_device") + LOGGER.info('Running dns.network.from_device') result = None LOGGER.info('Checking DNS traffic from device: ' + self._device_mac) @@ -90,7 +96,7 @@ def _dns_network_from_device(self): return result def _dns_mdns(self): - LOGGER.info("Running dns.mdns") + LOGGER.info('Running dns.mdns') result = None # Check if the device sends any MDNS traffic tcpdump_filter = f'udp port 5353 and ether src {self._device_mac}' @@ -104,7 +110,6 @@ def _dns_mdns(self): result = None, 'No MDNS traffic detected from the device' return result - def _exec_tcpdump(self, tcpdump_filter, capture_file): """ Args From 24dea52b67d4825800833d1509bf50dde0d8859f Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 13:09:45 -0600 Subject: [PATCH 07/11] add result details to nmap tests --- modules/test/nmap/conf/module_config.json | 1 + modules/test/nmap/python/src/nmap_module.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/test/nmap/conf/module_config.json b/modules/test/nmap/conf/module_config.json index 292eced8b..b03e9511c 100644 --- a/modules/test/nmap/conf/module_config.json +++ b/modules/test/nmap/conf/module_config.json @@ -16,6 +16,7 @@ "name": "security.nmap.ports", "description": "Run an nmap scan of open ports", "expected_behavior": "Report all open ports", + "short_description": "NMAP scan reports no unallowed ports open", "config": { "security.services.ftp": { "tcp_ports": { diff --git a/modules/test/nmap/python/src/nmap_module.py b/modules/test/nmap/python/src/nmap_module.py index f998f302a..6bcbd141a 100644 --- a/modules/test/nmap/python/src/nmap_module.py +++ b/modules/test/nmap/python/src/nmap_module.py @@ -40,6 +40,7 @@ def __init__(self, module): def _security_nmap_ports(self, config): LOGGER.info("Running security.nmap.ports test") + result = None # Delete the enabled key from the config if it exists # to prevent it being treated as a test key @@ -74,10 +75,14 @@ def _security_nmap_ports(self, config): LOGGER.info("Unallowed Ports Detected: " + str(self._unallowed_ports)) self._check_unallowed_port(self._unallowed_ports,config) LOGGER.info("Unallowed Ports: " + str(self._unallowed_ports)) - return len(self._unallowed_ports) == 0 + if len(self._unallowed_ports) > 0: + result = False, 'Some allowed ports detected: ' + str(self._unallowed_ports) + else: + result = True, 'No unallowed ports detected' else: LOGGER.info("Device ip address not resolved, skipping") - return None + result = None, "Device ip address not resolved" + return result def _process_port_results(self, tests): scan_results = {} From 90d6b51ff5e7534242e7368b7c0f267a58303489 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 13:46:56 -0600 Subject: [PATCH 08/11] add result details to ntp tests --- modules/test/ntp/conf/module_config.json | 6 ++-- modules/test/ntp/python/src/ntp_module.py | 42 ++++++++++++++++------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/modules/test/ntp/conf/module_config.json b/modules/test/ntp/conf/module_config.json index 288474868..c20d2067b 100644 --- a/modules/test/ntp/conf/module_config.json +++ b/modules/test/ntp/conf/module_config.json @@ -15,12 +15,14 @@ { "name": "ntp.network.ntp_support", "description": "Does the device request network time sync as client as per RFC 5905 - Network Time Protocol Version 4: Protocol and Algorithms Specification", - "expected_behavior": "The device sends an NTPv4 request to the configured NTP server." + "expected_behavior": "The device sends an NTPv4 request to the configured NTP server.", + "short_description": "The device sends NTPv4 requests" }, { "name": "ntp.network.ntp_dhcp", "description": "Accept NTP address over DHCP", - "expected_behavior": "Device can accept NTP server address, provided by the DHCP server (DHCP OFFER PACKET)" + "expected_behavior": "Device can accept NTP server address, provided by the DHCP server (DHCP OFFER PACKET)", + "short_descriiption": "Accepts NTP address over DHCP" } ] } diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 4053ce98a..b49d001f0 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """NTP test module""" from test_module import TestModule from scapy.all import rdpcap, NTP, IP @@ -22,6 +21,7 @@ MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' LOGGER = None + class NTPModule(TestModule): """NTP Test module""" @@ -35,7 +35,7 @@ def __init__(self, module): def _ntp_network_ntp_support(self): LOGGER.info('Running ntp.network.ntp_support') - + result = None packet_capture = rdpcap(STARTUP_CAPTURE_FILE) + rdpcap(MONITOR_CAPTURE_FILE) device_sends_ntp4 = False @@ -52,28 +52,44 @@ def _ntp_network_ntp_support(self): LOGGER.info(f'Device sent NTPv3 request to {packet[IP].dst}') if not (device_sends_ntp3 or device_sends_ntp4): - LOGGER.info('Device has not sent any NTP requests') - - return device_sends_ntp4 and not device_sends_ntp3 + result = False, 'Device has not sent any NTP requests' + elif device_sends_ntp3 and device_sends_ntp4: + result = False, 'Device sent NTPv3 and NTPv4 packets. NTPv3 is not allowed.' + elif device_sends_ntp3: + result = False, 'Device sent NTPv3 packets. NTPv3 is not allowed.' + elif device_sends_ntp4: + result = True, 'Device sent NTPv4 packets.' + LOGGER.info(result[1]) + return result def _ntp_network_ntp_dhcp(self): LOGGER.info('Running ntp.network.ntp_dhcp') - + result = None packet_capture = rdpcap(STARTUP_CAPTURE_FILE) + rdpcap(MONITOR_CAPTURE_FILE) device_sends_ntp = False + ntp_to_local = False + ntp_to_remote = False for packet in packet_capture: - if NTP in packet and packet.src == self._device_mac: device_sends_ntp = True if packet[IP].dst == self._ntp_server: LOGGER.info('Device sent NTP request to DHCP provided NTP server') - return True - - if not device_sends_ntp: - LOGGER.info('Device has not sent any NTP requests') + ntp_to_local = True + else: + LOGGER.info('Device sent NTP request to non-DHCP provided NTP server') + ntp_to_remote = True + + if device_sends_ntp: + if ntp_to_local and ntp_to_remote: + result = False, 'Device sent NTP request to DHCP provided server and non-DHCP provided server' + elif ntp_to_remote: + result = False, 'Device sent NTP request to non-DHCP provided server' + elif ntp_to_local: + result = True, 'Device sent NTP request to DHCP provided server' else: - LOGGER.info('Device has not sent NTP requests to DHCP provided NTP server') + result = False, 'Device has not sent any NTP requests' - return False + LOGGER.info(result[1]) + return result From d3bc5755b662276fe30a87a228629fd14f7ae11f Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 13:58:16 -0600 Subject: [PATCH 09/11] Add short descriptions to tls module and formatting --- modules/test/tls/conf/module_config.json | 12 ++++--- .../test/tls/python/src/tls_module_test.py | 31 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/modules/test/tls/conf/module_config.json b/modules/test/tls/conf/module_config.json index 59e5a839d..f71f39914 100644 --- a/modules/test/tls/conf/module_config.json +++ b/modules/test/tls/conf/module_config.json @@ -15,22 +15,26 @@ { "name": "security.tls.v1_2_server", "description": "Check the device web server TLS 1.2 & certificate is valid", - "expected_behavior": "TLS 1.2 certificate is issued to the web browser client when accessed" + "expected_behavior": "TLS 1.2 certificate is issued to the web browser client when accessed", + "short_description": "TLS 1.2 server certificate is valid" }, { "name": "security.tls.v1_3_server", "description": "Check the device web server TLS 1.3 & certificate is valid", - "expected_behavior": "TLS 1.3 certificate is issued to the web browser client when accessed" + "expected_behavior": "TLS 1.3 certificate is issued to the web browser client when accessed", + "short_description": "TLS 1.3 server certificate is valid" }, { "name": "security.tls.v1_2_client", "description": "Device uses TLS with connection to an external service on port 443 (or any other port which could be running the webserver-HTTPS)", - "expected_behavior": "The packet indicates a TLS connection with at least TLS 1.2 and support for ECDH and ECDSA ciphers" + "expected_behavior": "The packet indicates a TLS connection with at least TLS 1.2 and support for ECDH and ECDSA ciphers", + "short_description": "TLS 1.2 outbound connection valid" }, { "name": "security.tls.v1_3_client", "description": "Device uses TLS with connection to an external service on port 443 (or any other port which could be running the webserver-HTTPS)", - "expected_behavior": "The packet indicates a TLS connection with at least TLS 1.3" + "expected_behavior": "The packet indicates a TLS connection with at least TLS 1.3", + "short_description": "TLS 1.3 outbound connection valid" } ] } diff --git a/modules/test/tls/python/src/tls_module_test.py b/modules/test/tls/python/src/tls_module_test.py index 84a1c70eb..099956f4e 100644 --- a/modules/test/tls/python/src/tls_module_test.py +++ b/modules/test/tls/python/src/tls_module_test.py @@ -31,6 +31,7 @@ class TLSModuleTest(unittest.TestCase): """Contains and runs all the unit tests concerning TLS behaviors""" + @classmethod def setUpClass(cls): log = logger.get_logger(MODULE_NAME) @@ -129,13 +130,29 @@ def security_tls_v1_3_client_test(self): self.assertTrue(test_results[0]) def client_hello_packets_test(self): - packet_fail = {'dst_ip': '10.10.10.1', 'src_ip': '10.10.10.14', 'dst_port': '443', 'cipher_support': {'ecdh': False, 'ecdsa': True}} - packet_success = {'dst_ip': '10.10.10.1', 'src_ip': '10.10.10.14', 'dst_port': '443', 'cipher_support': {'ecdh': True, 'ecdsa': True}} - hello_packets = [packet_fail,packet_success] - hello_results = TLS_UTIL.process_hello_packets(hello_packets,'1.2') - print("Hello packets test results: " + str(hello_results)) - expected = {'valid':[packet_success],'invalid':[]} - self.assertEqual(hello_results,expected) + packet_fail = { + 'dst_ip': '10.10.10.1', + 'src_ip': '10.10.10.14', + 'dst_port': '443', + 'cipher_support': { + 'ecdh': False, + 'ecdsa': True + } + } + packet_success = { + 'dst_ip': '10.10.10.1', + 'src_ip': '10.10.10.14', + 'dst_port': '443', + 'cipher_support': { + 'ecdh': True, + 'ecdsa': True + } + } + hello_packets = [packet_fail, packet_success] + hello_results = TLS_UTIL.process_hello_packets(hello_packets, '1.2') + print('Hello packets test results: ' + str(hello_results)) + expected = {'valid': [packet_success], 'invalid': []} + self.assertEqual(hello_results, expected) def test_client_tls(self, tls_version, From 19c9b0daac1d12ffb6bce2eeaa4c2109698160a4 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 14:12:14 -0600 Subject: [PATCH 10/11] misc test module formatting --- modules/test/baseline/python/src/baseline_module.py | 4 ++-- modules/test/nmap/python/src/run.py | 4 ++-- modules/test/ntp/python/src/ntp_module.py | 9 ++++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/modules/test/baseline/python/src/baseline_module.py b/modules/test/baseline/python/src/baseline_module.py index c838fe853..51e81182e 100644 --- a/modules/test/baseline/python/src/baseline_module.py +++ b/modules/test/baseline/python/src/baseline_module.py @@ -38,6 +38,6 @@ def _baseline_fail(self): return False, 'Baseline fail test ran succesfully' def _baseline_skip(self): - LOGGER.info("Running baseline skip test") - LOGGER.info("Baseline skip test finished") + LOGGER.info('Running baseline skip test') + LOGGER.info('Baseline skip test finished') return None, 'Baseline skip test ran succesfully' diff --git a/modules/test/nmap/python/src/run.py b/modules/test/nmap/python/src/run.py index 5e33451d9..e68b52525 100644 --- a/modules/test/nmap/python/src/run.py +++ b/modules/test/nmap/python/src/run.py @@ -20,7 +20,7 @@ from nmap_module import NmapModule -LOG_NAME = "nmap_runner" +LOG_NAME = 'nmap_runner' LOGGER = logger.get_logger(LOG_NAME) class NmapModuleRunner: @@ -39,7 +39,7 @@ def __init__(self, module): self._test_module = NmapModule(module) self._test_module.run_tests() - LOGGER.info("nmap test module finished") + LOGGER.info('nmap test module finished') def add_logger(self, module): global LOGGER diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index b49d001f0..6a577d1a6 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -54,9 +54,11 @@ def _ntp_network_ntp_support(self): if not (device_sends_ntp3 or device_sends_ntp4): result = False, 'Device has not sent any NTP requests' elif device_sends_ntp3 and device_sends_ntp4: - result = False, 'Device sent NTPv3 and NTPv4 packets. NTPv3 is not allowed.' + result = False, ('Device sent NTPv3 and NTPv4 packets. ' + + 'NTPv3 is not allowed.') elif device_sends_ntp3: - result = False, 'Device sent NTPv3 packets. NTPv3 is not allowed.' + result = False, ('Device sent NTPv3 packets. ' + 'NTPv3 is not allowed.') elif device_sends_ntp4: result = True, 'Device sent NTPv4 packets.' LOGGER.info(result[1]) @@ -83,7 +85,8 @@ def _ntp_network_ntp_dhcp(self): if device_sends_ntp: if ntp_to_local and ntp_to_remote: - result = False, 'Device sent NTP request to DHCP provided server and non-DHCP provided server' + result = False, ('Device sent NTP request to DHCP provided ' + + 'server and non-DHCP provided server') elif ntp_to_remote: result = False, 'Device sent NTP request to non-DHCP provided server' elif ntp_to_local: From a9d780b4226944e81b8f56807988970da85bd86d Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Thu, 10 Aug 2023 14:27:20 -0600 Subject: [PATCH 11/11] fix typo --- modules/test/baseline/python/src/baseline_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/test/baseline/python/src/baseline_module.py b/modules/test/baseline/python/src/baseline_module.py index 51e81182e..978f916fe 100644 --- a/modules/test/baseline/python/src/baseline_module.py +++ b/modules/test/baseline/python/src/baseline_module.py @@ -30,14 +30,14 @@ def __init__(self, module): def _baseline_pass(self): LOGGER.info('Running baseline pass test') LOGGER.info('Baseline pass test finished') - return True, 'Baseline pass test ran succesfully' + return True, 'Baseline pass test ran successfully' def _baseline_fail(self): LOGGER.info('Running baseline fail test') LOGGER.info('Baseline fail test finished') - return False, 'Baseline fail test ran succesfully' + return False, 'Baseline fail test ran successfully' def _baseline_skip(self): LOGGER.info('Running baseline skip test') LOGGER.info('Baseline skip test finished') - return None, 'Baseline skip test ran succesfully' + return None, 'Baseline skip test ran successfully'