From cb8904ad9a55aa1b1546a56af399f85d46db35ea Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Mon, 31 Aug 2020 09:51:45 +0200 Subject: [PATCH 01/11] Consider individually supplied alive test methods Alive test methods can now be specified not only via bit flag but also via single xml elements for evert method. This commit allows the use of these single elements. --- ospd_openvas/preferencehandler.py | 79 ++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/ospd_openvas/preferencehandler.py b/ospd_openvas/preferencehandler.py index f9c4c1c3..b9abb1f4 100644 --- a/ospd_openvas/preferencehandler.py +++ b/ospd_openvas/preferencehandler.py @@ -101,10 +101,7 @@ def target_options(self) -> Dict: ) return self._target_options - def _get_vts_in_groups( - self, - filters: List[str], - ) -> List[str]: + def _get_vts_in_groups(self, filters: List[str],) -> List[str]: """Return a list of vts which match with the given filter. Arguments: @@ -154,16 +151,12 @@ def check_param_type(vt_param_value: str, param_type: str) -> Optional[int]: """Check if the value of a vt parameter matches with the type founded. """ - if ( - param_type - in [ - 'entry', - 'password', - 'radio', - 'sshlogin', - ] - and isinstance(vt_param_value, str) - ): + if param_type in [ + 'entry', + 'password', + 'radio', + 'sshlogin', + ] and isinstance(vt_param_value, str): return None elif param_type == 'checkbox' and ( vt_param_value == '0' or vt_param_value == '1' @@ -185,8 +178,7 @@ def check_param_type(vt_param_value: str, param_type: str) -> Optional[int]: return 1 def _process_vts( - self, - vts: Dict[str, Dict[str, str]], + self, vts: Dict[str, Dict[str, str]], ) -> Tuple[List[str], Dict[str, str]]: """ Add single VTs and their parameters. """ vts_list = [] @@ -289,9 +281,32 @@ def build_alive_test_opt_as_prefs( in string format to be added to the redis KB. """ target_opt_prefs_list = [] - if target_options and target_options.get('alive_test'): + + alive_test = target_options.get('alive_test') + alive_test_methods = target_options.get('alive_test_methods') + if target_options and alive_test is None and alive_test_methods: + alive_test_enum = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT + if target_options.get('icmp') == '1': + alive_test_enum = alive_test_enum | AliveTest.ALIVE_TEST_ICMP + if target_options.get('tcp_syn') == '1': + alive_test_enum = ( + alive_test_enum | AliveTest.ALIVE_TEST_TCP_SYN_SERVICE + ) + if target_options.get('tcp_ack') == '1': + alive_test_enum = ( + alive_test_enum | AliveTest.ALIVE_TEST_TCP_ACK_SERVICE + ) + if target_options.get('arp') == '1': + alive_test_enum = alive_test_enum | AliveTest.ALIVE_TEST_ARP + if target_options.get('consider_alive') == '1': + alive_test_enum = ( + alive_test_enum | AliveTest.ALIVE_TEST_CONSIDER_ALIVE + ) + alive_test = alive_test_enum + + if target_options and alive_test: try: - alive_test = int(target_options.get('alive_test')) + alive_test = int(alive_test) except ValueError: logger.debug( 'Alive test settings not applied. ' @@ -405,7 +420,35 @@ def prepare_boreas_alive_test(self): if not boreas: return alive_test_ports = self.target_options.get('alive_test_ports') + alive_test_str = self.target_options.get('alive_test') + alive_test_methods = self.target_options.get('alive_test_methods') + if alive_test_str is None and alive_test_methods: + alive_test_bitmask = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT + if self.target_options.get('icmp') == '1': + alive_test_bitmask = ( + alive_test_bitmask | AliveTest.ALIVE_TEST_ICMP + ) + if self.target_options.get('tcp_syn') == '1': + alive_test_bitmask = ( + alive_test_bitmask + | AliveTest.ALIVE_TEST_TCP_SYN_SERVICE + ) + if self.target_options.get('tcp_ack') == '1': + alive_test_bitmask = ( + alive_test_bitmask + | AliveTest.ALIVE_TEST_TCP_ACK_SERVICE + ) + if self.target_options.get('arp') == '1': + alive_test_bitmask = ( + alive_test_bitmask | AliveTest.ALIVE_TEST_ARP + ) + if self.target_options.get('consider_alive') == '1': + alive_test_bitmask = ( + alive_test_bitmask | AliveTest.ALIVE_TEST_CONSIDER_ALIVE + ) + alive_test_str = alive_test_bitmask + if alive_test_str is not None: try: alive_test = int(alive_test_str) From cbbded1ffa821481b63a38296429efb815e01d3c Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Mon, 31 Aug 2020 12:13:11 +0200 Subject: [PATCH 02/11] Use dedicated function for alive test transform Transform the specified alive test methods into a bit field as used in most of the gvm components via a seperate function. --- ospd_openvas/preferencehandler.py | 95 +++++++++++++++---------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/ospd_openvas/preferencehandler.py b/ospd_openvas/preferencehandler.py index b9abb1f4..b0104298 100644 --- a/ospd_openvas/preferencehandler.py +++ b/ospd_openvas/preferencehandler.py @@ -62,6 +62,27 @@ class AliveTest(IntEnum): ALIVE_TEST_TCP_SYN_SERVICE = 16 +def alive_test_methods_to_bit_field( + icmp: bool, tcp_syn: bool, tcp_ack: bool, arp: bool, consider_alive: bool +) -> int: + """Internally a bit field is used as alive test. This function creates + such a bit field out of the supplied alive test methods. + """ + + icmp_enum = AliveTest.ALIVE_TEST_ICMP if icmp else 0 + tcp_syn_enum = AliveTest.ALIVE_TEST_TCP_SYN_SERVICE if tcp_syn else 0 + tcp_ack_enum = AliveTest.ALIVE_TEST_TCP_ACK_SERVICE if tcp_ack else 0 + arp_enum = AliveTest.ALIVE_TEST_ARP if arp else 0 + consider_alive_enum = ( + AliveTest.ALIVE_TEST_CONSIDER_ALIVE if consider_alive else 0 + ) + + bit_field = ( + icmp_enum | tcp_syn_enum | tcp_ack_enum | arp_enum | consider_alive_enum + ) + return bit_field + + def _from_bool_to_str(value: int) -> str: """The OpenVAS scanner use yes and no as boolean values, whereas ospd uses 1 and 0.""" @@ -282,27 +303,18 @@ def build_alive_test_opt_as_prefs( """ target_opt_prefs_list = [] + # Alive test speciefied as bit field. alive_test = target_options.get('alive_test') + # Alive test speciefied as individual methods. alive_test_methods = target_options.get('alive_test_methods') if target_options and alive_test is None and alive_test_methods: - alive_test_enum = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT - if target_options.get('icmp') == '1': - alive_test_enum = alive_test_enum | AliveTest.ALIVE_TEST_ICMP - if target_options.get('tcp_syn') == '1': - alive_test_enum = ( - alive_test_enum | AliveTest.ALIVE_TEST_TCP_SYN_SERVICE - ) - if target_options.get('tcp_ack') == '1': - alive_test_enum = ( - alive_test_enum | AliveTest.ALIVE_TEST_TCP_ACK_SERVICE - ) - if target_options.get('arp') == '1': - alive_test_enum = alive_test_enum | AliveTest.ALIVE_TEST_ARP - if target_options.get('consider_alive') == '1': - alive_test_enum = ( - alive_test_enum | AliveTest.ALIVE_TEST_CONSIDER_ALIVE - ) - alive_test = alive_test_enum + alive_test = alive_test_methods_to_bit_field( + icmp=target_options.get('icmp') == '1', + tcp_syn=target_options.get('tcp_syn') == '1', + tcp_ack=target_options.get('tcp_ack') == '1', + arp=target_options.get('arp') == '1', + consider_alive=target_options.get('consider_alive') == '1', + ) if target_options and alive_test: try: @@ -412,7 +424,7 @@ def prepare_boreas_alive_test(self): """Set alive_test for Boreas if boreas scanner config (BOREAS_SETTING_NAME) was set""" settings = Openvas.get_settings() - alive_test = -1 + alive_test = None alive_test_ports = None if settings: @@ -420,43 +432,28 @@ def prepare_boreas_alive_test(self): if not boreas: return alive_test_ports = self.target_options.get('alive_test_ports') - - alive_test_str = self.target_options.get('alive_test') + # Alive test speciefied as bit field. + alive_test = self.target_options.get('alive_test') + # Alive test speciefied as individual methods. alive_test_methods = self.target_options.get('alive_test_methods') - if alive_test_str is None and alive_test_methods: - alive_test_bitmask = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT - if self.target_options.get('icmp') == '1': - alive_test_bitmask = ( - alive_test_bitmask | AliveTest.ALIVE_TEST_ICMP - ) - if self.target_options.get('tcp_syn') == '1': - alive_test_bitmask = ( - alive_test_bitmask - | AliveTest.ALIVE_TEST_TCP_SYN_SERVICE - ) - if self.target_options.get('tcp_ack') == '1': - alive_test_bitmask = ( - alive_test_bitmask - | AliveTest.ALIVE_TEST_TCP_ACK_SERVICE - ) - if self.target_options.get('arp') == '1': - alive_test_bitmask = ( - alive_test_bitmask | AliveTest.ALIVE_TEST_ARP - ) - if self.target_options.get('consider_alive') == '1': - alive_test_bitmask = ( - alive_test_bitmask | AliveTest.ALIVE_TEST_CONSIDER_ALIVE - ) - alive_test_str = alive_test_bitmask + if alive_test is None and alive_test_methods: + alive_test = alive_test_methods_to_bit_field( + icmp=self.target_options.get('icmp') == '1', + tcp_syn=self.target_options.get('tcp_syn') == '1', + tcp_ack=self.target_options.get('tcp_ack') == '1', + arp=self.target_options.get('arp') == '1', + consider_alive=self.target_options.get('consider_alive') + == '1', + ) - if alive_test_str is not None: + if alive_test is not None: try: - alive_test = int(alive_test_str) + alive_test = int(alive_test) except ValueError: logger.debug( 'Alive test preference for Boreas not set. ' 'Invalid alive test value %s.', - alive_test_str, + alive_test, ) # ALIVE_TEST_SCAN_CONFIG_DEFAULT if no alive_test provided else: From d31927b981fb995ec475f5f58f1df7d8a1b3379a Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Mon, 12 Oct 2020 13:30:19 +0200 Subject: [PATCH 03/11] Check if target_options are None before accessing --- ospd_openvas/preferencehandler.py | 115 +++++++++++++++++------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/ospd_openvas/preferencehandler.py b/ospd_openvas/preferencehandler.py index b0104298..fc7f54dc 100644 --- a/ospd_openvas/preferencehandler.py +++ b/ospd_openvas/preferencehandler.py @@ -122,7 +122,10 @@ def target_options(self) -> Dict: ) return self._target_options - def _get_vts_in_groups(self, filters: List[str],) -> List[str]: + def _get_vts_in_groups( + self, + filters: List[str], + ) -> List[str]: """Return a list of vts which match with the given filter. Arguments: @@ -172,12 +175,16 @@ def check_param_type(vt_param_value: str, param_type: str) -> Optional[int]: """Check if the value of a vt parameter matches with the type founded. """ - if param_type in [ - 'entry', - 'password', - 'radio', - 'sshlogin', - ] and isinstance(vt_param_value, str): + if ( + param_type + in [ + 'entry', + 'password', + 'radio', + 'sshlogin', + ] + and isinstance(vt_param_value, str) + ): return None elif param_type == 'checkbox' and ( vt_param_value == '0' or vt_param_value == '1' @@ -199,7 +206,8 @@ def check_param_type(vt_param_value: str, param_type: str) -> Optional[int]: return 1 def _process_vts( - self, vts: Dict[str, Dict[str, str]], + self, + vts: Dict[str, Dict[str, str]], ) -> Tuple[List[str], Dict[str, str]]: """ Add single VTs and their parameters. """ vts_list = [] @@ -302,19 +310,22 @@ def build_alive_test_opt_as_prefs( in string format to be added to the redis KB. """ target_opt_prefs_list = [] + alive_test = None - # Alive test speciefied as bit field. - alive_test = target_options.get('alive_test') - # Alive test speciefied as individual methods. - alive_test_methods = target_options.get('alive_test_methods') - if target_options and alive_test is None and alive_test_methods: - alive_test = alive_test_methods_to_bit_field( - icmp=target_options.get('icmp') == '1', - tcp_syn=target_options.get('tcp_syn') == '1', - tcp_ack=target_options.get('tcp_ack') == '1', - arp=target_options.get('arp') == '1', - consider_alive=target_options.get('consider_alive') == '1', - ) + if target_options: + # Alive test speciefied as bit field. + alive_test = target_options.get('alive_test') + # Alive test speciefied as individual methods. + alive_test_methods = target_options.get('alive_test_methods') + # alive_test takes precedence over alive_test_methods + if alive_test is None and alive_test_methods: + alive_test = alive_test_methods_to_bit_field( + icmp=target_options.get('icmp') == '1', + tcp_syn=target_options.get('tcp_syn') == '1', + tcp_ack=target_options.get('tcp_ack') == '1', + arp=target_options.get('arp') == '1', + consider_alive=target_options.get('consider_alive') == '1', + ) if target_options and alive_test: try: @@ -426,24 +437,27 @@ def prepare_boreas_alive_test(self): settings = Openvas.get_settings() alive_test = None alive_test_ports = None + target_options = self.target_options if settings: boreas = settings.get(BOREAS_SETTING_NAME) if not boreas: return - alive_test_ports = self.target_options.get('alive_test_ports') - # Alive test speciefied as bit field. - alive_test = self.target_options.get('alive_test') - # Alive test speciefied as individual methods. - alive_test_methods = self.target_options.get('alive_test_methods') + + if target_options: + alive_test_ports = target_options.get('alive_test_ports') + # Alive test was speciefied as bit field. + alive_test = target_options.get('alive_test') + # Alive test was speciefied as individual methods. + alive_test_methods = target_options.get('alive_test_methods') + # takes precedence over if alive_test is None and alive_test_methods: alive_test = alive_test_methods_to_bit_field( - icmp=self.target_options.get('icmp') == '1', - tcp_syn=self.target_options.get('tcp_syn') == '1', - tcp_ack=self.target_options.get('tcp_ack') == '1', - arp=self.target_options.get('arp') == '1', - consider_alive=self.target_options.get('consider_alive') - == '1', + icmp=target_options.get('icmp') == '1', + tcp_syn=target_options.get('tcp_syn') == '1', + tcp_ack=target_options.get('tcp_ack') == '1', + arp=target_options.get('arp') == '1', + consider_alive=target_options.get('consider_alive') == '1', ) if alive_test is not None: @@ -459,27 +473,28 @@ def prepare_boreas_alive_test(self): else: alive_test = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT - # If a valid alive_test was set then the bit mask - # has value between 31 (11111) and 1 (10000) - if 1 <= alive_test <= 31: - pref = "{pref_key}|||{pref_value}".format( - pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test - ) - self.kbdb.add_scan_preferences(self.scan_id, [pref]) + # If a valid alive_test was set then the bit mask + # has value between 31 (11111) and 1 (10000) + if 1 <= alive_test <= 31: + pref = "{pref_key}|||{pref_value}".format( + pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test + ) + self.kbdb.add_scan_preferences(self.scan_id, [pref]) - if alive_test == AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT: - alive_test = AliveTest.ALIVE_TEST_ICMP - pref = "{pref_key}|||{pref_value}".format( - pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test - ) - self.kbdb.add_scan_preferences(self.scan_id, [pref]) + if alive_test == AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT: + alive_test = AliveTest.ALIVE_TEST_ICMP + pref = "{pref_key}|||{pref_value}".format( + pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test + ) + self.kbdb.add_scan_preferences(self.scan_id, [pref]) - # Add portlist if present. Validity is checked on Boreas side. - if alive_test_ports is not None: - pref = "{pref_key}|||{pref_value}".format( - pref_key=BOREAS_ALIVE_TEST_PORTS, pref_value=alive_test_ports - ) - self.kbdb.add_scan_preferences(self.scan_id, [pref]) + # Add portlist if present. Validity is checked on Boreas side. + if alive_test_ports is not None: + pref = "{pref_key}|||{pref_value}".format( + pref_key=BOREAS_ALIVE_TEST_PORTS, + pref_value=alive_test_ports, + ) + self.kbdb.add_scan_preferences(self.scan_id, [pref]) def prepare_reverse_lookup_opt_for_openvas(self): """ Set reverse lookup options in the kb""" From 0bee82e8440924678d51f0b1da7722d57226dff5 Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Mon, 12 Oct 2020 15:52:49 +0200 Subject: [PATCH 04/11] Return if no settings provided and use fallback Use fallback icmp ping method for boreas if no valid method was provided. --- ospd_openvas/preferencehandler.py | 68 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/ospd_openvas/preferencehandler.py b/ospd_openvas/preferencehandler.py index fc7f54dc..cf597278 100644 --- a/ospd_openvas/preferencehandler.py +++ b/ospd_openvas/preferencehandler.py @@ -443,6 +443,8 @@ def prepare_boreas_alive_test(self): boreas = settings.get(BOREAS_SETTING_NAME) if not boreas: return + else: + return if target_options: alive_test_ports = target_options.get('alive_test_ports') @@ -460,41 +462,43 @@ def prepare_boreas_alive_test(self): consider_alive=target_options.get('consider_alive') == '1', ) - if alive_test is not None: - try: - alive_test = int(alive_test) - except ValueError: - logger.debug( - 'Alive test preference for Boreas not set. ' - 'Invalid alive test value %s.', - alive_test, - ) - # ALIVE_TEST_SCAN_CONFIG_DEFAULT if no alive_test provided - else: - alive_test = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT - - # If a valid alive_test was set then the bit mask - # has value between 31 (11111) and 1 (10000) - if 1 <= alive_test <= 31: - pref = "{pref_key}|||{pref_value}".format( - pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test + if alive_test is not None: + try: + alive_test = int(alive_test) + except ValueError: + logger.debug( + 'Alive test preference for Boreas not set. ' + 'Invalid alive test value %s.', + alive_test, ) - self.kbdb.add_scan_preferences(self.scan_id, [pref]) + # Use default alive test as fall back + alive_test = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT + # Use default alive test if no valid alive_test was provided + else: + alive_test = AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT + + # If a valid alive_test was set then the bit mask + # has value between 31 (11111) and 1 (10000) + if 1 <= alive_test <= 31: + pref = "{pref_key}|||{pref_value}".format( + pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test + ) + self.kbdb.add_scan_preferences(self.scan_id, [pref]) - if alive_test == AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT: - alive_test = AliveTest.ALIVE_TEST_ICMP - pref = "{pref_key}|||{pref_value}".format( - pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test - ) - self.kbdb.add_scan_preferences(self.scan_id, [pref]) + if alive_test == AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT: + alive_test = AliveTest.ALIVE_TEST_ICMP + pref = "{pref_key}|||{pref_value}".format( + pref_key=BOREAS_ALIVE_TEST, pref_value=alive_test + ) + self.kbdb.add_scan_preferences(self.scan_id, [pref]) - # Add portlist if present. Validity is checked on Boreas side. - if alive_test_ports is not None: - pref = "{pref_key}|||{pref_value}".format( - pref_key=BOREAS_ALIVE_TEST_PORTS, - pref_value=alive_test_ports, - ) - self.kbdb.add_scan_preferences(self.scan_id, [pref]) + # Add portlist if present. Validity is checked on Boreas side. + if alive_test_ports is not None: + pref = "{pref_key}|||{pref_value}".format( + pref_key=BOREAS_ALIVE_TEST_PORTS, + pref_value=alive_test_ports, + ) + self.kbdb.add_scan_preferences(self.scan_id, [pref]) def prepare_reverse_lookup_opt_for_openvas(self): """ Set reverse lookup options in the kb""" From 3eb071b13a4058d8b0578c8d731122e735fcc412 Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Mon, 12 Oct 2020 15:55:34 +0200 Subject: [PATCH 05/11] Fix tests We use icmp ping as fallback now when an invalid alive test was provided. --- tests/test_preferencehandler.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/test_preferencehandler.py b/tests/test_preferencehandler.py index 1eb76666..362d4d31 100644 --- a/tests/test_preferencehandler.py +++ b/tests/test_preferencehandler.py @@ -243,7 +243,8 @@ def test_set_target(self, mock_kb): p.prepare_target_for_openvas() p.kbdb.add_scan_preferences.assert_called_with( - p.scan_id, ['TARGET|||192.168.0.1'], + p.scan_id, + ['TARGET|||192.168.0.1'], ) @patch('ospd_openvas.db.KbDB') @@ -258,7 +259,8 @@ def test_set_ports(self, mock_kb): p.prepare_ports_for_openvas() p.kbdb.add_scan_preferences.assert_called_with( - p.scan_id, ['port_range|||80,443'], + p.scan_id, + ['port_range|||80,443'], ) @patch('ospd_openvas.db.KbDB') @@ -271,7 +273,8 @@ def test_set_main_kbindex(self, mock_kb): p.prepare_main_kbindex_for_openvas() p.kbdb.add_scan_preferences.assert_called_with( - p.scan_id, ['ov_maindbid|||2'], + p.scan_id, + ['ov_maindbid|||2'], ) @patch('ospd_openvas.db.KbDB') @@ -364,7 +367,8 @@ def test_set_host_options(self, mock_kb): p.prepare_host_options_for_openvas() p.kbdb.add_scan_preferences.assert_called_with( - p.scan_id, ['exclude_hosts|||192.168.0.1'], + p.scan_id, + ['exclude_hosts|||192.168.0.1'], ) @patch('ospd_openvas.db.KbDB') @@ -423,7 +427,10 @@ def test_set_reverse_lookup_opt(self, mock_kb): p.kbdb.add_scan_preferences.assert_called_with( p.scan_id, - ['reverse_lookup_only|||yes', 'reverse_lookup_unify|||no',], + [ + 'reverse_lookup_only|||yes', + 'reverse_lookup_unify|||no', + ], ) @patch('ospd_openvas.db.KbDB') @@ -431,6 +438,8 @@ def test_set_boreas_alive_test_with_settings(self, mock_kb): # No Boreas config setting (BOREAS_SETTING_NAME) set w = DummyDaemon() ov_setting = {'not_the_correct_setting': 1} + t_opt = {} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) with patch.object(Openvas, 'get_settings', return_value=ov_setting): p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) p.scan_id = '456-789' @@ -450,7 +459,8 @@ def test_set_boreas_alive_test_with_settings(self, mock_kb): p.kbdb.add_scan_preferences = MagicMock() p.prepare_boreas_alive_test() - p.kbdb.add_scan_preferences.assert_not_called() + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||2'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) # ALIVE_TEST_TCP_SYN_SERVICE as alive test. w = DummyDaemon() @@ -571,5 +581,6 @@ def test_set_alive_pinghost(self, mock_kb): p.prepare_alive_test_option_for_openvas() p.kbdb.add_scan_preferences.assert_called_with( - p.scan_id, alive_test_out, + p.scan_id, + alive_test_out, ) From cd5285582b0a6e891eb356bdf1ad13d3352a2392 Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Mon, 12 Oct 2020 16:25:09 +0200 Subject: [PATCH 06/11] Add test for alive_test_methods_to_bit_field --- tests/test_preferencehandler.py | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/test_preferencehandler.py b/tests/test_preferencehandler.py index 362d4d31..40f38af6 100644 --- a/tests/test_preferencehandler.py +++ b/tests/test_preferencehandler.py @@ -36,6 +36,7 @@ BOREAS_ALIVE_TEST, BOREAS_ALIVE_TEST_PORTS, PreferenceHandler, + alive_test_methods_to_bit_field, ) @@ -584,3 +585,79 @@ def test_set_alive_pinghost(self, mock_kb): p.scan_id, alive_test_out, ) + + def test_alive_test_methods_to_bit_field(self): + + self.assertEqual( + AliveTest.ALIVE_TEST_TCP_ACK_SERVICE, + alive_test_methods_to_bit_field( + icmp=False, + tcp_ack=True, + tcp_syn=False, + arp=False, + consider_alive=False, + ), + ) + + self.assertEqual( + AliveTest.ALIVE_TEST_ICMP, + alive_test_methods_to_bit_field( + icmp=True, + tcp_ack=False, + tcp_syn=False, + arp=False, + consider_alive=False, + ), + ) + + self.assertEqual( + AliveTest.ALIVE_TEST_ARP, + alive_test_methods_to_bit_field( + icmp=False, + tcp_ack=False, + tcp_syn=False, + arp=True, + consider_alive=False, + ), + ) + + self.assertEqual( + AliveTest.ALIVE_TEST_CONSIDER_ALIVE, + alive_test_methods_to_bit_field( + icmp=False, + tcp_ack=False, + tcp_syn=False, + arp=False, + consider_alive=True, + ), + ) + + self.assertEqual( + AliveTest.ALIVE_TEST_TCP_SYN_SERVICE, + alive_test_methods_to_bit_field( + icmp=False, + tcp_ack=False, + tcp_syn=True, + arp=False, + consider_alive=False, + ), + ) + + all_alive_test_methods = ( + AliveTest.ALIVE_TEST_SCAN_CONFIG_DEFAULT + | AliveTest.ALIVE_TEST_TCP_ACK_SERVICE + | AliveTest.ALIVE_TEST_ICMP + | AliveTest.ALIVE_TEST_ARP + | AliveTest.ALIVE_TEST_CONSIDER_ALIVE + | AliveTest.ALIVE_TEST_TCP_SYN_SERVICE + ) + self.assertEqual( + all_alive_test_methods, + alive_test_methods_to_bit_field( + icmp=True, + tcp_ack=True, + tcp_syn=True, + arp=True, + consider_alive=True, + ), + ) From 5140e4ebcdc25db50e7277503665b65fadf2c03f Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Tue, 13 Oct 2020 08:50:38 +0200 Subject: [PATCH 07/11] Add test for alive test supplied not as enum Alive test methods are supplied via seperate elements. E.g. 1 1 --- tests/test_preferencehandler.py | 141 ++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/tests/test_preferencehandler.py b/tests/test_preferencehandler.py index 40f38af6..41cc929d 100644 --- a/tests/test_preferencehandler.py +++ b/tests/test_preferencehandler.py @@ -525,6 +525,147 @@ def test_set_boreas_alive_test_with_settings(self, mock_kb): ] p.kbdb.add_scan_preferences.assert_has_calls(calls) + @patch('ospd_openvas.db.KbDB') + def test_set_boreas_alive_test_not_as_enum(self, mock_kb): + # No Boreas config setting (BOREAS_SETTING_NAME) set + w = DummyDaemon() + ov_setting = {'not_the_correct_setting': 1} + t_opt = {} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + p.kbdb.add_scan_preferences.assert_not_called() + + # Boreas config setting set but invalid alive_test. + w = DummyDaemon() + t_opt = {'alive_test_methods': "1", 'arp': '-1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||2'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # ICMP was chosen as alive test. + w = DummyDaemon() + t_opt = {'alive_test_methods': "1", 'icmp': '1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||2'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # tcp_syn as alive test. + w = DummyDaemon() + t_opt = {'alive_test_methods': "1", 'tcp_syn': '1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||16'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # tcp_ack as alive test. + w = DummyDaemon() + t_opt = {'alive_test_methods': "1", 'tcp_ack': '1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||1'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # arp as alive test. + w = DummyDaemon() + t_opt = {'alive_test_methods': "1", 'arp': '1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||4'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # arp as alive test. + w = DummyDaemon() + t_opt = {'alive_test_methods': "1", 'consider_alive': '1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||8'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # all alive test methods + w = DummyDaemon() + t_opt = { + 'alive_test_methods': "1", + 'icmp': '1', + 'tcp_ack': '1', + 'tcp_syn': '1', + 'arp': '1', + 'consider_alive': '1', + } + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||31'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + + # TCP-SYN alive test and dedicated port list for alive scan provided. + w = DummyDaemon() + t_opt = { + 'alive_test_ports': "80,137", + 'alive_test_methods': "1", + 'tcp_syn': '1', + } + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + calls = [ + call(p.scan_id, [BOREAS_ALIVE_TEST + '|||16']), + call(p.scan_id, [BOREAS_ALIVE_TEST_PORTS + '|||80,137']), + ] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + @patch('ospd_openvas.db.KbDB') def test_set_boreas_alive_test_without_settings(self, mock_kb): w = DummyDaemon() From 284a4b78d673197d6df95200bfbec98b0e9a573d Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Tue, 13 Oct 2020 09:08:17 +0200 Subject: [PATCH 08/11] Add tests Alive test as enum as precedence over indivually supplied methods. Add more tests for teting when alive test methods were supplied indivually. --- tests/test_preferencehandler.py | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_preferencehandler.py b/tests/test_preferencehandler.py index 41cc929d..7f8c3622 100644 --- a/tests/test_preferencehandler.py +++ b/tests/test_preferencehandler.py @@ -204,6 +204,13 @@ def test_build_alive_test_opt_empty(self): self.assertEqual(ret, []) + # alive test was supplied via seperate xml element + w = DummyDaemon() + target_options_dict = {'alive_test_methods': '1', 'icmp': '0'} + p = PreferenceHandler('1234-1234', None, w.scan_collection, None) + ret = p.build_alive_test_opt_as_prefs(target_options_dict) + self.assertEqual(ret, []) + def test_build_alive_test_opt(self): w = DummyDaemon() @@ -221,6 +228,21 @@ def test_build_alive_test_opt(self): self.assertEqual(ret, alive_test_out) + # alive test was supplied via sepertae xml element + w = DummyDaemon() + alive_test_out = [ + "1.3.6.1.4.1.25623.1.0.100315:1:checkbox:Do a TCP ping|||no", + "1.3.6.1.4.1.25623.1.0.100315:2:checkbox:TCP ping tries also TCP-SYN ping|||no", + "1.3.6.1.4.1.25623.1.0.100315:7:checkbox:TCP ping tries only TCP-SYN ping|||no", + "1.3.6.1.4.1.25623.1.0.100315:3:checkbox:Do an ICMP ping|||yes", + "1.3.6.1.4.1.25623.1.0.100315:4:checkbox:Use ARP|||no", + "1.3.6.1.4.1.25623.1.0.100315:5:checkbox:Mark unrechable Hosts as dead (not scanning)|||yes", + ] + target_options_dict = {'alive_test_methods': '1', 'icmp': '1'} + p = PreferenceHandler('1234-1234', None, w.scan_collection, None) + ret = p.build_alive_test_opt_as_prefs(target_options_dict) + self.assertEqual(ret, alive_test_out) + def test_build_alive_test_opt_fail_1(self): w = DummyDaemon() logging.Logger.debug = Mock() @@ -666,6 +688,26 @@ def test_set_boreas_alive_test_not_as_enum(self, mock_kb): ] p.kbdb.add_scan_preferences.assert_has_calls(calls) + @patch('ospd_openvas.db.KbDB') + def test_set_boreas_alive_test_enum_has_precedence(self, mock_kb): + w = DummyDaemon() + t_opt = { + 'alive_test_methods': "1", + 'consider_alive': '1', + 'alive_test': AliveTest.ALIVE_TEST_ICMP, + } + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + ov_setting = {BOREAS_SETTING_NAME: 1} + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_boreas_alive_test() + + # has icmp and not consider_alive + calls = [call(p.scan_id, [BOREAS_ALIVE_TEST + '|||2'])] + p.kbdb.add_scan_preferences.assert_has_calls(calls) + @patch('ospd_openvas.db.KbDB') def test_set_boreas_alive_test_without_settings(self, mock_kb): w = DummyDaemon() From 53c2504439ac054b54169560b24e8161ec864396 Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Tue, 13 Oct 2020 09:26:55 +0200 Subject: [PATCH 09/11] Add changleog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8766da6f..c16e876f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add dedicated port list for alive detection (Boreas only) as scanner preference if supplied via OSP. [#327](https://github.com/greenbone/ospd-openvas/pull/327) - Add methods for adding VTs to the redis cache.[#337](https://github.com/greenbone/ospd-openvas/pull/337) +- Add support for supplying alive test methods via seperate elements. [#331](https://github.com/greenbone/ospd-openvas/pull/331) ### Changed - Get all results from main kb. [#285](https://github.com/greenbone/ospd-openvas/pull/285) From cafdb74d27949ffc5b76ce0833231bb4f3012020 Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Tue, 13 Oct 2020 12:20:36 +0200 Subject: [PATCH 10/11] Handle alive test not supplied as enum Handle the case when the alive test was supplied via seperate elements. Also fix traceback when no valid alive test was supplied by checking if alive_test_opt is empty before trying to add them to the preferences. --- ospd_openvas/preferencehandler.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ospd_openvas/preferencehandler.py b/ospd_openvas/preferencehandler.py index cf597278..9c36221f 100644 --- a/ospd_openvas/preferencehandler.py +++ b/ospd_openvas/preferencehandler.py @@ -425,11 +425,15 @@ def build_alive_test_opt_as_prefs( def prepare_alive_test_option_for_openvas(self): """ Set alive test option. Overwrite the scan config settings.""" settings = Openvas.get_settings() - if settings and self.target_options.get('alive_test'): + if settings and ( + self.target_options.get('alive_test') + or self.target_options.get('alive_test_methods') + ): alive_test_opt = self.build_alive_test_opt_as_prefs( self.target_options ) - self.kbdb.add_scan_preferences(self.scan_id, alive_test_opt) + if alive_test_opt: + self.kbdb.add_scan_preferences(self.scan_id, alive_test_opt) def prepare_boreas_alive_test(self): """Set alive_test for Boreas if boreas scanner config From 2762801e2492150a1c98e1819e2dd9104dcd9262 Mon Sep 17 00:00:00 2001 From: ArnoStiefvater Date: Tue, 13 Oct 2020 12:27:30 +0200 Subject: [PATCH 11/11] Add more tests Add test for invalid alive test. Add test for alive test supplied via seperete elements. Add test for empty alive test elements. --- tests/test_preferencehandler.py | 82 +++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/test_preferencehandler.py b/tests/test_preferencehandler.py index 7f8c3622..21db86e8 100644 --- a/tests/test_preferencehandler.py +++ b/tests/test_preferencehandler.py @@ -739,6 +739,40 @@ def test_set_alive_no_setting(self, mock_kb): p.kbdb.add_scan_preferences.assert_not_called() + @patch('ospd_openvas.db.KbDB') + def test_set_alive_no_invalid_alive_test(self, mock_kb): + w = DummyDaemon() + + t_opt = {'alive_test': -1} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + + ov_setting = {'some_setting': 1} + + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_alive_test_option_for_openvas() + + p.kbdb.add_scan_preferences.assert_not_called() + + @patch('ospd_openvas.db.KbDB') + def test_set_alive_no_invalid_alive_test_no_enum(self, mock_kb): + w = DummyDaemon() + + t_opt = {'alive_test_methods': '1', 'icmp': '-1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + + ov_setting = {'some_setting': 1} + + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_alive_test_option_for_openvas() + + p.kbdb.add_scan_preferences.assert_not_called() + @patch('ospd_openvas.db.KbDB') def test_set_alive_pinghost(self, mock_kb): w = DummyDaemon() @@ -769,6 +803,54 @@ def test_set_alive_pinghost(self, mock_kb): alive_test_out, ) + @patch('ospd_openvas.db.KbDB') + def test_prepare_alive_test_not_supplied_as_enum(self, mock_kb): + w = DummyDaemon() + + alive_test_out = [ + "1.3.6.1.4.1.25623.1.0.100315:1:checkbox:Do a TCP ping|||no", + "1.3.6.1.4.1.25623.1.0.100315:2:checkbox:TCP ping tries also TCP-SYN ping|||no", + "1.3.6.1.4.1.25623.1.0.100315:7:checkbox:TCP ping tries only TCP-SYN ping|||no", + "1.3.6.1.4.1.25623.1.0.100315:3:checkbox:Do an ICMP ping|||yes", + "1.3.6.1.4.1.25623.1.0.100315:4:checkbox:Use ARP|||no", + "1.3.6.1.4.1.25623.1.0.100315:5:checkbox:Mark unrechable Hosts as dead (not scanning)|||yes", + ] + + t_opt = {'alive_test_methods': '1', 'icmp': '1'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + + ov_setting = {'some_setting': 1} + + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_alive_test_option_for_openvas() + + p.kbdb.add_scan_preferences.assert_called_with( + p.scan_id, + alive_test_out, + ) + + @patch('ospd_openvas.db.KbDB') + def test_prepare_alive_test_no_enum_no_alive_test(self, mock_kb): + w = DummyDaemon() + + t_opt = {'alive_test_methods': '1', 'icmp': '0'} + w.scan_collection.get_target_options = MagicMock(return_value=t_opt) + + ov_setting = {'some_setting': 1} + + with patch.object(Openvas, 'get_settings', return_value=ov_setting): + p = PreferenceHandler('1234-1234', mock_kb, w.scan_collection, None) + + p.scan_id = '456-789' + p.kbdb.add_scan_preferences = MagicMock() + p.prepare_alive_test_option_for_openvas() + + p.kbdb.add_scan_preferences.assert_not_called() + def test_alive_test_methods_to_bit_field(self): self.assertEqual(