def test3_new_year_with_start_year_value(self):
        """This test case checks if a new year is learned successfully with the start year being set."""
        start_year = 2017
        match_context = MatchContext(b'07.02.2018 11:40:00: it still works')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d.%m.%Y %H:%M:%S',
                                                       datetime.timezone.utc,
                                                       None, start_year)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518003600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(
            b'07.02.2018 11:40:00 UTC+0000: it still works')
        date_time_model_element = DateTimeModelElement(
            'path', b'%d.%m.%Y %H:%M:%S %z', datetime.timezone.utc, None,
            start_year)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518003600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(
            b'07.02.2018 11:40:00 UTC+0001: it still works')
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518007200)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(
            b'07.02.2018 11:40:00 UTC+0000: it still works')
        date_time_model_element = DateTimeModelElement(
            'path', b'%d.%m.%Y %H:%M:%S %z', datetime.timezone.utc, None,
            start_year)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518003600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(
            b'07.02.2018 11:40:00 UTC-0001: it still works')
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518000000)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(
            b'07.02.2018 11:40:00 CET+1: it still works')
        date_time_model_element = DateTimeModelElement(
            'path', b'%d.%m.%Y %H:%M:%S %z', datetime.timezone.utc, None,
            start_year)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518003600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(
            b'07.02.2018 11:40:00 UTC+2: it still works')
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518007200)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(
            b'07/Feb/2017:11:40:00 +0000] "GET /login.php HTTP/1.1" 200 2532 "-" "Mozilla/5.0 (X11; Ubuntu; '
            b'Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0')
        date_time_model_element = DateTimeModelElement(
            'path', b'%d/%b/%Y:%H:%M:%S %z', datetime.timezone.utc, None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(
            match_context.match_data,
            b'] "GET /login.php HTTP/1.1" 200 2532 "-" "Mozilla/5.0 (X11; Ubuntu; '
            b'Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0')

        match_context = MatchContext(
            b'07.02.2018 11:40:00 UTC: it still works')
        date_time_model_element = DateTimeModelElement(
            'path', b'%d.%m.%Y %H:%M:%S %z', datetime.timezone.utc, None,
            start_year)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1518003600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
    def test2do_timer(self):
        """In this test case the functionality of the timer is tested. The eventCollectTime must not be 0."""
        description = "Test2DefaultMailNotificationEventHandler"
        default_mail_notification_event_handler = DefaultMailNotificationEventHandler(
            self.analysis_context)
        self.analysis_context.register_component(self, description)

        t = time()
        match_context = MatchContext(self.pid)
        fixed_dme = FixedDataModelElement('s3', self.pid)
        match_element = fixed_dme.get_match_element("match", match_context)

        log_atom = LogAtom(fixed_dme.fixed_data, ParserMatch(match_element), t,
                           self)
        default_mail_notification_event_handler.receive_event(
            self.test % self.__class__.__name__,
            'New value for pathes %s: %s' %
            ('match/s3', repr(match_element.match_object)),
            [log_atom.raw_data], None, log_atom, self)

        t = 0
        default_mail_notification_event_handler.do_timer(t)
        # skipcq: PYL-W1510, BAN-B602
        result = subprocess.run(self.mail_call,
                                shell=True,
                                stdout=subprocess.PIPE)

        self.assertFalse(
            self.__expected_string %
            (match_element.get_path(), match_element.get_match_object(),
             self.__class__.__name__, description, 1,
             match_element.get_match_string().decode() +
             "\n\n") in str(result.stdout, 'utf-8'))

        t = time()
        default_mail_notification_event_handler.next_alert_time = t + 500
        default_mail_notification_event_handler.do_timer(t)

        # skipcq: PYL-W1510, BAN-B602
        result = subprocess.run(self.mail_call,
                                shell=True,
                                stdout=subprocess.PIPE)
        self.assertFalse(
            self.__expected_string %
            (match_element.get_path(), match_element.get_match_object(),
             self.__class__.__name__, description, 1,
             match_element.get_match_string().decode() +
             "\n\n") in str(result.stdout, 'utf-8'))

        default_mail_notification_event_handler.next_alert_time = t
        default_mail_notification_event_handler.do_timer(t)

        sleep(2)
        # skipcq: PYL-W1510, BAN-B602
        result = subprocess.run(self.mail_call,
                                shell=True,
                                stdout=subprocess.PIPE)
        # skipcq: PYL-W1510, BAN-B602
        subprocess.run(self.mail_delete_call,
                       shell=True,
                       stdout=subprocess.PIPE)

        if datetime.fromtimestamp(t).strftime(
                self.datetime_format_string) not in str(
                    result.stdout, 'utf-8'):
            print("ERROR: %s t not found in mail!" % description,
                  file=sys.stderr)

        self.assertTrue(
            self.__expected_string %
            (match_element.get_path(), match_element.get_match_object(),
             self.__class__.__name__, description, 1,
             match_element.get_match_string().decode() + "\n\n")
            in str(result.stdout, 'utf-8'),
            msg="%s vs \n %s" %
            (self.__expected_string %
             (match_element.get_path(), match_element.get_match_object(),
              self.__class__.__name__, description, 1,
              match_element.get_match_string().decode() + "\n\n"),
             str(result.stdout, 'utf-8')))
class NewMatchPathDetectorTest(TestBase):
    """Unittests for the NewMatchPathDetector."""

    __expected_string = '%s New path(es) detected\n%s: "%s" (%d lines)\n  %s\n%s\n\n'
    match_path_s1 = "['/s1']"
    match_path_d1 = "['/d1']"

    datetime_format_string = '%Y-%m-%d %H:%M:%S'
    analysis = 'Analysis.%s'
    pid = " pid="
    uid = " uid=2"

    match_context_fixed_dme = MatchContext(b' pid=')
    fixed_dme = FixedDataModelElement('s1', b' pid=')
    match_element_fixed_dme = fixed_dme.get_match_element(
        "", match_context_fixed_dme)

    match_context_decimal_integer_value_me = MatchContext(b'25537 uid=2')
    decimal_integer_value_me = DecimalIntegerValueModelElement(
        'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
        DecimalIntegerValueModelElement.PAD_TYPE_NONE)
    match_element_decimal_integer_value_me = decimal_integer_value_me.get_match_element(
        "", match_context_decimal_integer_value_me)

    def test1_log_atom_not_known(self):
        """
        This test case checks the correct processing of unknown log lines, which in reality means that an anomaly has been found.
        The output is directed to an output stream and compared for accuracy. The auto_include_flag is False and the output must be
        repeatable on second run.
        """
        description = "Test1NewMatchPathDetector"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = round(time.time(), 3)
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        log_atom_decimal_integer_value_me = LogAtom(
            self.match_context_decimal_integer_value_me.match_data,
            ParserMatch(self.match_element_decimal_integer_value_me), t,
            new_match_path_detector)

        self.assertTrue(
            new_match_path_detector.receive_atom(log_atom_fixed_dme))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector.__class__.__name__, description, 1,
             self.match_path_s1, self.pid))
        self.reset_output_stream()

        # repeating should produce the same result
        self.assertTrue(
            new_match_path_detector.receive_atom(log_atom_fixed_dme))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector.__class__.__name__, description, 1,
             self.match_path_s1, self.pid))
        self.reset_output_stream()

        # other MatchElement
        self.assertTrue(
            new_match_path_detector.receive_atom(
                log_atom_decimal_integer_value_me))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector.__class__.__name__, description, 1,
             self.match_path_d1, self.uid))

    def test2_log_atom_known(self):
        """
        This test case checks the functionality of the auto_include_flag.
        If the same MatchElement is processed a second time and the auto_include_flag was True, no event must be triggered.
        """
        description = "Test2NewMatchPathDetector"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = round(time.time(), 3)

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        log_atom_decimal_integer_value_me = LogAtom(
            self.match_context_decimal_integer_value_me.match_data,
            ParserMatch(self.match_element_decimal_integer_value_me), t,
            new_match_path_detector)

        self.assertTrue(
            new_match_path_detector.receive_atom(log_atom_fixed_dme))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector.__class__.__name__, description, 1,
             self.match_path_s1, self.pid))
        self.reset_output_stream()

        # repeating should NOT produce the same result
        self.assertTrue(
            new_match_path_detector.receive_atom(log_atom_fixed_dme))
        self.assertEqual(self.output_stream.getvalue(), '')
        self.reset_output_stream()

        # other MatchElement
        self.assertTrue(
            new_match_path_detector.receive_atom(
                log_atom_decimal_integer_value_me))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector.__class__.__name__, description, 1,
             self.match_path_d1, self.uid))

    def test3_log_atom_known_from_persisted_data(self):
        """The persisting and reading of permitted log lines should be checked with this test."""
        description = "Test3NewMatchPathDetector"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = round(time.time(), 3)
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        self.assertTrue(
            new_match_path_detector.receive_atom(log_atom_fixed_dme))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector.__class__.__name__, description, 1,
             self.match_path_s1, self.pid))
        new_match_path_detector.do_persist()
        self.reset_output_stream()

        otherNewMatchPathDetector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        otherLogAtomFixedDME = LogAtom(
            self.fixed_dme.fixed_data,
            ParserMatch(self.match_element_fixed_dme), t,
            otherNewMatchPathDetector)

        self.assertTrue(
            otherNewMatchPathDetector.receive_atom(otherLogAtomFixedDME))
        self.assertEqual(self.output_stream.getvalue(), '')

    def test4_get_time_trigger_class(self):
        """
        The known paths are to be periodically stored after a certain time. This requires a synchronization class.
        The return of the correct class is to be checked in this test case.
        """
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.assertEqual(new_match_path_detector.get_time_trigger_class(), 1)

    # skipcq: PYL-W0105
    """The following test cases should check if the doTimer() method is working properly.This includes the updating of nextPersistTime.
    As it is not updated directly in the method this test cases are not correct. Due to that they are commented."""

    # def test5_do_timer_next_persist_time_none(self):
    #     """During initialization, the next time is not determined (the value is initialized with None). In this case, the persistence is
    #     expected to occur after 600 milliseconds."""
    #     self.new_match_path_detector = NewMatchPathDetector(self.aminer_config, [self.stream_printer_event_handler], 'Default', True,
    #                                                         output_log_line=False)
    #     self.assertEqual(self.new_match_path_detector.do_timer(200), 600)
    #     self.assertEqual(self.new_match_path_detector.do_timer(400), 600)
    #     self.assertEqual(self.new_match_path_detector.do_timer(10000), 600)
    #
    # def test6_do_timer_delta_smaller_or_equal_zero(self):
    #     """If the NextPersistTime is less than or equal to zero, the data must be saved."""
    #     self.new_match_path_detector = NewMatchPathDetector(self.aminer_config, [self.stream_printer_event_handler], 'Default', True,
    #                                                         output_log_line=False)
    #     self.new_match_path_detector.nextPersistTime = 400
    #     self.assertEqual(self.new_match_path_detector.do_timer(400), 600)
    #     self.assertEqual(self.new_match_path_detector.do_timer(1000), 600)
    #
    # def test7_do_timer_delta_greater_zero(self):
    #     """If the delta does not fall below the limit value, only the delta value should be returned."""
    #     # this test fails due to the missing update of the nextPersistTime variable in the doTimer method
    #     self.new_match_path_detector = NewMatchPathDetector(self.aminer_config, [self.stream_printer_event_handler], 'Default', True,
    #                                                         output_log_line=False)
    #     self.new_match_path_detector.nextPersistTime = 400
    #     self.assertEqual(self.new_match_path_detector.do_timer(200), 200)
    #     self.assertEqual(self.new_match_path_detector.do_timer(200), 600)
    #     self.assertEqual(self.new_match_path_detector.do_timer(100), 500)

    def test8_allowlist_event_type_exception(self):
        """This test case checks whether an exception is thrown when entering an event of another class."""
        description = "Test8NewMatchPathDetector"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = round(time.time(), 3)
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        new_match_path_detector.receive_atom(log_atom_fixed_dme)
        new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, [], [self.stream_printer_event_handler],
            'Default', True, True)
        self.assertRaises(
            Exception, new_match_path_detector.allowlist_event, self.analysis %
            new_match_path_value_combo_detector.__class__.__name__,
            self.output_stream.getvalue(), None)

    def test9_allowlist_event_allowlisting_data_exception(self):
        """The NewMatchPathDetector can not handle allowlisting data and therefore an exception is expected."""
        description = "Test9NewMatchPathDetector"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = round(time.time(), 3)
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        new_match_path_detector.receive_atom(log_atom_fixed_dme)
        self.assertRaises(
            Exception, new_match_path_detector.allowlist_event,
            self.analysis % new_match_path_detector.__class__.__name__,
            self.output_stream.getvalue(), ['random', 'Data'])

    def test10_allowlist_event_with_known_and_unknown_paths(self):
        """This test case checks in which cases an event is triggered and compares with expected results."""
        description = "Test10NewMatchPathDetector"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = round(time.time(), 3)
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        new_match_path_detector.receive_atom(log_atom_fixed_dme)
        self.assertEqual(
            new_match_path_detector.allowlist_event(
                self.analysis % new_match_path_detector.__class__.__name__,
                self.match_element_fixed_dme.get_path(),
                None), 'Allowlisted path(es) %s in %s.' %
            (self.match_element_fixed_dme.get_path(),
             self.analysis % new_match_path_detector.__class__.__name__))

        new_match_path_detector.auto_include_flag = False
        self.assertEqual(
            new_match_path_detector.allowlist_event(
                self.analysis % new_match_path_detector.__class__.__name__,
                self.match_element_decimal_integer_value_me.get_path(),
                None), 'Allowlisted path(es) %s in %s.' %
            (self.match_element_decimal_integer_value_me.path,
             self.analysis % new_match_path_detector.__class__.__name__))
class NewMatchPathValueDetectorTest(TestBase):
    __expected_string = '%s New value(s) detected\n%s: "%s" (%d lines)\n  %s\n\n'

    datetime_format_string = '%Y-%m-%d %H:%M:%S'
    string = b'25537 uid=2'
    first_f1_s1 = 'first/f1/s1'
    string2 = "{'first/f1/s1': '25537 uid=2'}\nb'25537 uid=2'"

    fixed_dme = FixedDataModelElement('s1', string)
    decimalIntegerValueME = DecimalIntegerValueModelElement(
        'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
        DecimalIntegerValueModelElement.PAD_TYPE_NONE)

    match_context_first_match_me = MatchContext(string)
    first_match_me = FirstMatchModelElement('f1',
                                            [fixed_dme, decimalIntegerValueME])
    match_element_first_match_me = first_match_me.get_match_element(
        'first', match_context_first_match_me)

    match_context_first_match_me2 = MatchContext(string)
    first_match_me2 = FirstMatchModelElement(
        'f2', [decimalIntegerValueME, fixed_dme])
    match_element_first_match_me2 = first_match_me2.get_match_element(
        'second', match_context_first_match_me2)

    def test1_log_atom_not_known(self):
        """This test case checks the correct processing of unknown log lines, which in reality means that an anomaly has been found. The
        output is directed to an output stream and compared for accuracy. The auto_include_flag is False and the output must be repeatable
        on second run."""
        description = "Test1NewMatchPathValueDetector"
        new_match_path_value_detector = NewMatchPathValueDetector(
            self.aminer_config, [self.first_f1_s1],
            [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_value_detector,
                                                 description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.fixed_dme.fixed_data,
            ParserMatch(self.match_element_first_match_me), t,
            new_match_path_value_detector)
        new_match_path_value_detector.receive_atom(log_atom_sequence_me)
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_detector.__class__.__name__, description, 1,
             self.string2))
        self.reset_output_stream()

        # repeating should produce the same result
        new_match_path_value_detector.receive_atom(log_atom_sequence_me)
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_detector.__class__.__name__, description, 1,
             self.string2))
        self.reset_output_stream()

        new_match_path_value_detector2 = NewMatchPathValueDetector(
            self.aminer_config, ['second/f2/d1'],
            [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_detector2, description + "2")
        log_atom_sequence_me2 = LogAtom(
            b'25537', ParserMatch(self.match_element_first_match_me2), t,
            new_match_path_value_detector2)

        # other MatchElement
        new_match_path_value_detector2.receive_atom(log_atom_sequence_me2)
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_detector.__class__.__name__,
             description + "2", 1, "{'second/f2/d1': 25537}\nb'25537'"))

    def test2_log_atom_known(self):
        """This test case checks the functionality of the auto_include_flag. If the same MatchElement is processed a second time and the
        auto_include_flag was True, no event must be triggered."""
        description = "Test2NewMatchPathValueDetector"
        new_match_path_value_detector = NewMatchPathValueDetector(
            self.aminer_config, [self.first_f1_s1],
            [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_value_detector,
                                                 description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.fixed_dme.fixed_data,
            ParserMatch(self.match_element_first_match_me), t,
            new_match_path_value_detector)
        new_match_path_value_detector.receive_atom(log_atom_sequence_me)
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_detector.__class__.__name__, description, 1,
             self.string2))
        self.reset_output_stream()

        # repeating should NOT produce the same result
        new_match_path_value_detector.receive_atom(log_atom_sequence_me)
        self.assertEqual(self.output_stream.getvalue(), '')
        self.reset_output_stream()

        new_match_path_value_detector2 = NewMatchPathValueDetector(
            self.aminer_config, ['second/f2/d1'],
            [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_detector2, description + "2")
        log_atom_sequence_me2 = LogAtom(
            b'25537', ParserMatch(self.match_element_first_match_me2), t,
            new_match_path_value_detector2)

        # other MatchElement
        new_match_path_value_detector2.receive_atom(log_atom_sequence_me2)
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_detector.__class__.__name__,
             description + "2", 1, "{'second/f2/d1': 25537}\nb'25537'"))

    def test3_log_atom_known_from_persisted_data(self):
        """The persisting and reading of permitted log lines should be checked with this test."""
        description = "Test3NewMatchPathValueDetector"
        new_match_path_value_detector = NewMatchPathValueDetector(
            self.aminer_config, [self.first_f1_s1],
            [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_value_detector,
                                                 description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.fixed_dme.fixed_data,
            ParserMatch(self.match_element_first_match_me), t,
            new_match_path_value_detector)
        new_match_path_value_detector.receive_atom(log_atom_sequence_me)
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_detector.__class__.__name__, description, 1,
             self.string2))
        new_match_path_value_detector.do_persist()
        self.reset_output_stream()

        other_new_match_path_value_detector = NewMatchPathValueDetector(
            self.aminer_config, [self.first_f1_s1],
            [self.stream_printer_event_handler],
            'Default',
            True,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_value_detector,
                                                 description + "2")
        other_log_atom_fixed_dme = LogAtom(
            self.fixed_dme.fixed_data,
            ParserMatch(self.match_element_first_match_me), t,
            other_new_match_path_value_detector)

        other_new_match_path_value_detector.receive_atom(
            other_log_atom_fixed_dme)
        self.assertEqual(self.output_stream.getvalue(), '')
Exemple #5
0
    def test9_receive_atom_list_missing_value(self):
        """This test case checks if missing values are reported correctly."""
        description = "Test90MissingMatchPathValueDetector"
        match_context_fixed_dme = MatchContext(self.pid)
        fixed_dme = FixedDataModelElement('s1', self.pid)
        match_element_fixed_dme = fixed_dme.get_match_element(
            "match1", match_context_fixed_dme)
        match_context_decimal_integer_value_me = MatchContext(self.string)
        decimal_integer_value_me = DecimalIntegerValueModelElement(
            'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
            DecimalIntegerValueModelElement.PAD_TYPE_NONE)
        match_element_decimal_integer_value_me = decimal_integer_value_me.get_match_element(
            "match2", match_context_decimal_integer_value_me)

        missing_match_path_list_value_detector = MissingMatchPathListValueDetector(
            self.aminer_config, [
                match_element_fixed_dme.get_path(),
                match_element_decimal_integer_value_me.get_path()
            ], [self.stream_printer_event_handler], 'Default', True,
            self.__default_interval, self.__realert_interval)
        self.analysis_context.register_component(
            missing_match_path_list_value_detector, description)

        log_atom_fixed_dme = LogAtom(fixed_dme.fixed_data,
                                     ParserMatch(match_element_fixed_dme),
                                     round(time.time()),
                                     missing_match_path_list_value_detector)
        self.assertTrue(
            missing_match_path_list_value_detector.receive_atom(
                log_atom_fixed_dme))

        past_time = 4000
        missing_match_path_list_value_detector = MissingMatchPathListValueDetector(
            self.aminer_config, [
                match_element_fixed_dme.get_path(),
                match_element_decimal_integer_value_me.get_path()
            ], [self.stream_printer_event_handler], 'Default', True,
            missing_match_path_list_value_detector.default_interval -
            past_time, self.__realert_interval)
        self.analysis_context.register_component(
            missing_match_path_list_value_detector, description + "2")

        log_atom_fixed_dme = LogAtom(fixed_dme.fixed_data,
                                     ParserMatch(match_element_fixed_dme),
                                     round(time.time()) + past_time,
                                     missing_match_path_list_value_detector)
        self.assertTrue(
            missing_match_path_list_value_detector.receive_atom(
                log_atom_fixed_dme))
        self.assertTrue(
            (self.output_stream.getvalue() == self.__expected_string %
             (datetime.fromtimestamp(time.time() + past_time).strftime(
                 self.datetime_format_string),
              missing_match_path_list_value_detector.__class__.__name__,
              description + "2", 1,
              "match1/s1, match2/d1: b' pid=' overdue 400s (interval -400)"))
            or
            (self.output_stream.getvalue(), self.__expected_string %
             (datetime.fromtimestamp(time.time() + past_time + 1).strftime(
                 self.datetime_format_string),
              missing_match_path_list_value_detector.__class__.__name__,
              description + "2", 1,
              "match1/s1, match2/d1: b' pid=' overdue 400s (interval -400)")))
 def test1receive_atoms_with_default_values(self):
     """
     In this test case multiple log_atoms are received with default values of the EventTypeDetector.
     path_list is empty and all paths are learned dynamically in variable_key_list.
     """
     event_type_detector = EventTypeDetector(self.aminer_config, [self.stream_printer_event_handler])
     log_atoms = []
     for line in self.log_lines:
         t = time.time()
         log_atoms.append(
             LogAtom(line, ParserMatch(self.parsing_model.get_match_element('parser', MatchContext(line))), t, self.__class__.__name__))
     for i, log_atom in enumerate(log_atoms):
         self.assertTrue(event_type_detector.receive_atom(log_atom))
         self.assertEqual(event_type_detector.total_records, i + 1)
Exemple #7
0
    def test4has_idle_source(self):
        """In this test case a source becomes idle and expires."""
        description = "Test4SimpleMultisourceAtomSync"
        sync_wait_time = 3

        any_byte_data_model_element = AnyByteDataModelElement('a1')
        new_match_path_detector1 = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector1,
                                                 description)
        new_match_path_detector2 = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector2,
                                                 description + "2")

        simple_multisource_atom_sync = SimpleMultisourceAtomSync(
            [new_match_path_detector1], sync_wait_time)
        t = time()
        match_context = MatchContext(self.calculation)
        match_element = any_byte_data_model_element.get_match_element(
            'match', match_context)
        log_atom1 = LogAtom(match_element.match_object,
                            ParserMatch(match_element), t,
                            new_match_path_detector1)
        log_atom2 = LogAtom(match_element.match_object,
                            ParserMatch(match_element), t,
                            new_match_path_detector2)

        self.assertTrue(
            not simple_multisource_atom_sync.receive_atom(log_atom1))
        self.assertTrue(
            not simple_multisource_atom_sync.receive_atom(log_atom2))
        sleep(sync_wait_time + 1)

        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom1))
        # log_atom1 is handled now, so new_match_path_detector1 should be deleted after waiting the sync_wait_time.
        self.assertTrue(
            not simple_multisource_atom_sync.receive_atom(log_atom2))
        sleep(sync_wait_time + 1)
        self.assertTrue(
            not simple_multisource_atom_sync.receive_atom(log_atom2))
        self.assertEqual(
            simple_multisource_atom_sync.sources_dict, {
                new_match_path_detector1: [log_atom1.get_timestamp(), None],
                new_match_path_detector2:
                [log_atom2.get_timestamp(), log_atom2]
            })

        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom1))
        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom1))
        sleep(sync_wait_time + 1)
        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom1))
        self.assertEqual(
            simple_multisource_atom_sync.sources_dict, {
                new_match_path_detector1: [log_atom1.get_timestamp(), None],
                new_match_path_detector2:
                [log_atom2.get_timestamp(), log_atom2]
            })
        log_atom1 = LogAtom(match_element.match_object,
                            ParserMatch(match_element), t + 1,
                            new_match_path_detector1)
        self.assertTrue(
            not simple_multisource_atom_sync.receive_atom(log_atom1))
        self.assertEqual(
            simple_multisource_atom_sync.sources_dict, {
                new_match_path_detector1:
                [log_atom1.get_timestamp() - 1, log_atom1],
                new_match_path_detector2:
                [log_atom2.get_timestamp(), log_atom2]
            })

        log_atom1 = LogAtom(match_element.match_object,
                            ParserMatch(match_element), t - 1,
                            new_match_path_detector1)
        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom1))
Exemple #8
0
    def check_anomaly_detection(self, ecd, t, diff):
        """Check if anomalies were detected as expected."""
        for char in self.alphabet:
            self.reset_output_stream()
            char = bytes([char])
            parser_match = ParserMatch(self.alphabet_model.get_match_element('parser', MatchContext(char)))
            t += 5 * 3
            ecd.receive_atom(LogAtom(char, parser_match, t, self.__class__.__name__))
            # another LogAtom must be received to check the follow anomalies.
            t += 5 * 3
            ecd.receive_atom(LogAtom(char, parser_match, t, self.__class__.__name__))
            # print(self.output_stream.getvalue())

            # precede anomaly
            for i in range(1, int(5 / diff) + 1, 1):
                # print("in")
                # print(bytes([self.alphabet[(self.alphabet.index(char) - i) % len(self.alphabet)]]))
                self.assertIn('Event %s is missing, but should precede event %s' % (
                    bytes([self.alphabet[(self.alphabet.index(char) - i) % len(self.alphabet)]]), char), self.output_stream.getvalue())
            for i in range(int(5 / diff) + 1, len(self.alphabet), 1):
                # print("not in")
                # print(bytes([self.alphabet[(self.alphabet.index(char) - i) % len(self.alphabet)]]))
                self.assertNotIn('Event %s is missing, but should precede event %s' % (
                    bytes([self.alphabet[(self.alphabet.index(char) - i) % len(self.alphabet)]]), char), self.output_stream.getvalue())

            # follow anomaly
            for i in range(1, int(5 / diff) + 1, 1):
                # print("in")
                # print(bytes([self.alphabet[(self.alphabet.index(char) + i) % len(self.alphabet)]]))
                self.assertIn('Event %s is missing, but should follow event %s' % (
                    bytes([self.alphabet[(self.alphabet.index(char) + i) % len(self.alphabet)]]), char), self.output_stream.getvalue())
            for i in range(int(5 / diff) + 1, len(self.alphabet), 1):
                # print("not in")
                # print(bytes([self.alphabet[(self.alphabet.index(char) + i) % len(self.alphabet)]]))
                self.assertNotIn('Event %s is missing, but should follow event %s' % (
                    bytes([self.alphabet[(self.alphabet.index(char) + i) % len(self.alphabet)]]), char), self.output_stream.getvalue())
Exemple #9
0
 def generate_perfect_data(self, iterations, diff):
     """Generate data without any error."""
     log_atoms = []
     t = time()
     for i in range(1, iterations+1):
         char = bytes([self.alphabet[i % len(self.alphabet)]])
         parser_match = ParserMatch(self.alphabet_model.get_match_element('parser', MatchContext(char)))
         t += diff
         log_atoms.append(LogAtom(char, parser_match, t, self.__class__.__name__))
     return log_atoms
Exemple #10
0
    def test3unsorted_log_atom(self):
        """In this test case multiple, UNSORTED LogAtoms of different sources are received by the class."""
        description = "Test3SimpleMultisourceAtomSync"
        sync_wait_time = 3

        any_byte_data_model_element = AnyByteDataModelElement('a1')
        new_match_path_detector1 = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector1,
                                                 description)
        new_match_path_detector2 = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default',
            False,
            output_log_line=False)
        self.analysis_context.register_component(new_match_path_detector2,
                                                 description + "2")

        simple_multisource_atom_sync = SimpleMultisourceAtomSync(
            [new_match_path_detector1, new_match_path_detector2],
            sync_wait_time)
        t = time()
        match_context = MatchContext(self.calculation)
        match_element = any_byte_data_model_element.get_match_element(
            'match', match_context)
        log_atom1 = LogAtom(match_element.match_object,
                            ParserMatch(match_element), t,
                            new_match_path_detector1)
        log_atom2 = LogAtom(match_element.match_object,
                            ParserMatch(match_element), t - 1,
                            new_match_path_detector1)

        self.assertTrue(
            not simple_multisource_atom_sync.receive_atom(log_atom1))
        sleep(sync_wait_time)

        # unsorted, should be accepted
        self.reset_output_stream()
        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom2))
        self.assertTrue(simple_multisource_atom_sync.receive_atom(log_atom1))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t - 1).strftime(
                self.datetime_format_string),
             new_match_path_detector1.__class__.__name__, description, 1,
             self.match_path, self.calculation.decode()) +
            self.__expected_string %
            (datetime.fromtimestamp(t - 1).strftime(
                self.datetime_format_string),
             new_match_path_detector1.__class__.__name__, description + "2", 1,
             self.match_path, self.calculation.decode()) +
            self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector1.__class__.__name__, description, 1,
             self.match_path, self.calculation.decode()) +
            self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_detector1.__class__.__name__, description + "2", 1,
             self.match_path, self.calculation.decode()))
Exemple #11
0
class TimeCorrelationDetectorTest(TestBase):
    """Unittests for the TimeCorrlelationDetectorTest."""

    __expected_string = '%s Correlation report\nTimeCorrelationDetector: "%s" (%d lines)\n  '

    string = b'25537 uid=2'
    datetime_format_string = '%Y-%m-%d %H:%M:%S'

    fixed_dme = FixedDataModelElement('s1', string)
    decimal_integer_value_me = DecimalIntegerValueModelElement('d1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
                                                               DecimalIntegerValueModelElement.PAD_TYPE_NONE)

    match_context_first_match_me = MatchContext(string)
    first_match_me = FirstMatchModelElement('f1', [fixed_dme, decimal_integer_value_me])
    match_element_first_match_me = first_match_me.get_match_element('first', match_context_first_match_me)

    match_context_first_match_me2 = MatchContext(string)
    first_match_me2 = FirstMatchModelElement('f2', [decimal_integer_value_me, fixed_dme])
    match_element_first_match_me2 = first_match_me2.get_match_element('second', match_context_first_match_me2)

    def test1_normal_report(self):
        """
        This test case unit the creation of a report.
        As the rules are chosen randomly this test can not be very specific in checking he actual values of the report.
        """
        description = "Test1TimeCorrelationDetector"
        parallel_check_count = 2
        record_count_before_event = 10
        output_logline = True
        use_path_match = True
        use_value_match = True
        min_rule_attributes = 1
        max_rule_attributes = 5
        time_correlation_detector = TimeCorrelationDetector(self.aminer_config, [self.stream_printer_event_handler], parallel_check_count,
                                                            'Default', record_count_before_event, output_logline, use_path_match,
                                                            use_value_match, min_rule_attributes, max_rule_attributes)
        self.analysis_context.register_component(time_correlation_detector, component_name=description)

        t = time.time()
        for i in range(0, 10):
            logAtomSequenceME = LogAtom(self.fixed_dme.fixed_data, ParserMatch(self.match_element_first_match_me), t,
                                        time_correlation_detector)
            time_correlation_detector.receive_atom(logAtomSequenceME)
        self.assertTrue(self.output_stream.getvalue().startswith(
            self.__expected_string % (datetime.fromtimestamp(t).strftime(self.datetime_format_string), description, 10)))
        self.reset_output_stream()

        for i in range(0, 10):
            logAtomSequenceME = LogAtom(self.fixed_dme.fixed_data, ParserMatch(self.match_element_first_match_me), t + i,
                                        time_correlation_detector)
            time_correlation_detector.receive_atom(logAtomSequenceME)
        self.assertTrue(self.output_stream.getvalue().startswith(
            self.__expected_string % (datetime.fromtimestamp(t + 9).strftime(self.datetime_format_string), description, 20)))
        self.reset_output_stream()

        for i in range(10, 15):
            logAtomSequenceME = LogAtom(self.fixed_dme.fixed_data, ParserMatch(self.match_element_first_match_me), t + i,
                                        time_correlation_detector)
            time_correlation_detector.receive_atom(logAtomSequenceME)
            logAtomSequenceME2 = LogAtom(self.fixed_dme.fixed_data, ParserMatch(self.match_element_first_match_me2), t + i,
                                         time_correlation_detector)
            time_correlation_detector.receive_atom(logAtomSequenceME2)
        self.assertTrue(self.output_stream.getvalue().startswith(
            self.__expected_string % (datetime.fromtimestamp(t + 14).strftime(self.datetime_format_string), description, 30)))
    def test4receive_match_after_max_allowed_time_diff_without_auto_include_flag(
            self):
        """This test case checks if log_atoms are deleted after the maximal allowed time difference with the auto_include_flag=False."""
        description = 'test4newMatchIdValueComboDetectorTest'
        output_stream_empty_results = [
            True, False, True, False, True, False, True, True, False, True,
            False, True, True, True, False, False, False, True, False, True,
            False
        ]
        id_dict_current_results = [{
            1: {
                'parser/type/syscall/syscall': 1
            }
        }, {}, {
            2: {
                'parser/type/syscall/syscall': 2
            }
        }, {}, {
            3: {
                'parser/type/syscall/syscall': 3
            }
        }, {}, {
            100: {
                'parser/type/syscall/syscall': 1
            }
        }, {
            100: {
                'parser/type/syscall/syscall': 1
            },
            4: {
                'parser/type/syscall/syscall': 1
            }
        }, {
            100: {
                'parser/type/syscall/syscall': 1
            }
        }, {
            5: {
                'parser/type/path/name': 'two'
            },
            100: {
                'parser/type/syscall/syscall': 1
            }
        }, {}, {
            6: {
                'parser/type/syscall/syscall': 4
            }
        }, {
            6: {
                'parser/type/syscall/syscall': 4
            },
            7: {
                'parser/type/path/name': 'five'
            }
        }, {
            6: {
                'parser/type/syscall/syscall': 4
            },
            7: {
                'parser/type/path/name': 'five'
            },
            8: {
                'parser/type/syscall/syscall': 6
            }
        }, {
            7: {
                'parser/type/path/name': 'five'
            },
            8: {
                'parser/type/syscall/syscall': 6
            }
        }, {}, {}, {
            9: {
                'parser/type/syscall/syscall': 2
            }
        }, {}, {
            10: {
                'parser/type/path/name': 'one'
            }
        }, {}]
        id_dict_old_results = [{}] * 10 + [{
            100: {
                'parser/type/syscall/syscall': 1
            }
        }] * 5 + [{
            8: {
                'parser/type/syscall/syscall': 6
            }
        }] + [{}] * 5
        min_allowed_time_diff = 5
        log_atoms = []
        t = time.time()
        for line in self.log_lines:
            log_atoms.append(
                LogAtom(
                    line,
                    ParserMatch(
                        self.parsing_model.get_match_element(
                            'parser', MatchContext(line))), t,
                    self.__class__.__name__))
            t = t + min_allowed_time_diff * 0.25

        new_match_id_value_combo_detector = NewMatchIdValueComboDetector(
            self.aminer_config,
            ['parser/type/path/name', 'parser/type/syscall/syscall'],
            [self.stream_printer_event_handler],
            id_path_list=['parser/type/path/id', 'parser/type/syscall/id'],
            min_allowed_time_diff=min_allowed_time_diff,
            auto_include_flag=False,
            allow_missing_values_flag=True,
            persistence_id='audit_type_path',
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_id_value_combo_detector, description)

        for i, log_atom in enumerate(log_atoms):
            self.assertTrue(
                new_match_id_value_combo_detector.receive_atom(log_atom))
            self.assertEqual(self.output_stream.getvalue() == "",
                             output_stream_empty_results[i], log_atom.raw_data)
            self.assertEqual(new_match_id_value_combo_detector.id_dict_current,
                             id_dict_current_results[i], log_atom.raw_data)
            self.assertEqual(new_match_id_value_combo_detector.id_dict_old,
                             id_dict_old_results[i])
            self.assertEqual(new_match_id_value_combo_detector.known_values,
                             [])
            self.reset_output_stream()
Exemple #13
0
    def test20_suppress_output(self):
        """
        Check if the suppress property and SuppressNewMatchPathDetector are working as expected.
        This test only includes the StreamPrinterEventHandler.
        """
        __expected_string1 = '%s New path(es) detected\n%s: "%s" (%d lines)\n  %s\n%s\n\n'
        t = time()
        fixed_dme = FixedDataModelElement('s1', b' pid=')
        match_context_fixed_dme = MatchContext(b' pid=')
        match_element_fixed_dme = fixed_dme.get_match_element(
            "", match_context_fixed_dme)
        log_atom_fixed_dme = LogAtom(fixed_dme.fixed_data,
                                     ParserMatch(match_element_fixed_dme), t,
                                     'DefaultNewMatchPathDetector')
        datetime_format_string = '%Y-%m-%d %H:%M:%S'
        match_path_s1 = "['/s1']"
        pid = "b' pid='"
        __expected_string2 = '%s New value combination(s) detected\n%s: "%s" (%d lines)\n%s\n\n'
        fixed_dme2 = FixedDataModelElement('s1', b'25537 uid=')
        decimal_integer_value_me = DecimalIntegerValueModelElement(
            'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
            DecimalIntegerValueModelElement.PAD_TYPE_NONE)
        match_context_sequence_me = MatchContext(b'25537 uid=2')
        seq = SequenceModelElement('seq',
                                   [fixed_dme2, decimal_integer_value_me])
        match_element_sequence_me = seq.get_match_element(
            'first', match_context_sequence_me)
        string2 = "  (b'25537 uid=', 2)\nb'25537 uid=2'"

        spec = importlib.util.spec_from_file_location(
            'aminer_config',
            '/usr/lib/logdata-anomaly-miner/aminer/YamlConfig.py')
        aminer_config = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(aminer_config)
        aminer_config.load_yaml('unit/data/configfiles/suppress_config.yml')
        context = AnalysisContext(aminer_config)
        context.build_analysis_pipeline()

        context.aminer_config.yaml_data['SuppressNewMatchPathDetector'] = False
        self.stream_printer_event_handler = context.atomizer_factory.event_handler_list[
            0]
        self.stream_printer_event_handler.stream = self.output_stream
        default_nmpd = context.registered_components[1][0]
        default_nmpd.output_log_line = False
        self.assertTrue(default_nmpd.receive_atom(log_atom_fixed_dme))
        self.assertEqual(
            self.output_stream.getvalue(), __expected_string1 %
            (datetime.fromtimestamp(t).strftime(datetime_format_string),
             default_nmpd.__class__.__name__, 'DefaultNewMatchPathDetector', 1,
             match_path_s1, pid))
        self.reset_output_stream()

        context.aminer_config.yaml_data['SuppressNewMatchPathDetector'] = True
        context = AnalysisContext(aminer_config)
        context.build_analysis_pipeline()
        self.stream_printer_event_handler = context.atomizer_factory.event_handler_list[
            0]
        self.stream_printer_event_handler.stream = self.output_stream
        default_nmpd = context.registered_components[1][0]
        default_nmpd.output_log_line = False
        self.assertTrue(default_nmpd.receive_atom(log_atom_fixed_dme))
        self.assertEqual(self.output_stream.getvalue(), "")
        self.reset_output_stream()

        value_combo_det = context.registered_components[2][0]
        log_atom_sequence_me = LogAtom(
            match_element_sequence_me.get_match_string(),
            ParserMatch(match_element_sequence_me), t, value_combo_det)
        self.stream_printer_event_handler = context.atomizer_factory.event_handler_list[
            0]
        self.stream_printer_event_handler.stream = self.output_stream
        self.assertTrue(value_combo_det.receive_atom(log_atom_sequence_me))
        self.assertEqual(
            self.output_stream.getvalue(), __expected_string2 %
            (datetime.fromtimestamp(t).strftime(datetime_format_string),
             value_combo_det.__class__.__name__, 'ValueComboDetector', 1,
             string2))
        self.reset_output_stream()

        context.aminer_config.yaml_data['Analysis'][0]['suppress'] = True
        context = AnalysisContext(aminer_config)
        context.build_analysis_pipeline()
        value_combo_det = context.registered_components[1][0]
        self.stream_printer_event_handler = context.atomizer_factory.event_handler_list[
            0]
        self.stream_printer_event_handler.stream = self.output_stream
        self.assertTrue(value_combo_det.receive_atom(log_atom_sequence_me))
        self.assertEqual(self.output_stream.getvalue(), "")
        self.reset_output_stream()
    def test2start_year_value(self):
        """This test checks if they class is parsing dates without year values correctly."""
        match_context = MatchContext(b'07.02 11:40:00: it still works')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d.%m %H:%M:%S',
                                                       datetime.timezone.utc,
                                                       None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(
            b'07.02 11:40:00 UTC+0000: it still works')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d.%m %H:%M:%S %z',
                                                       datetime.timezone.utc,
                                                       None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(
            b'07.02 11:40:00 UTC+0001: it still works')
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486471200)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(
            b'07.02 11:40:00 UTC+0000: it still works')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d.%m %H:%M:%S %z',
                                                       datetime.timezone.utc,
                                                       None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(
            b'07.02 11:40:00 UTC-0001: it still works')
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486464000)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(b'07.02 11:40:00 CET+1: it still works')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d.%m %H:%M:%S %z',
                                                       datetime.timezone.utc,
                                                       None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
        match_context = MatchContext(b'07.02 11:40:00 CET+2: it still works')
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486471200)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)

        match_context = MatchContext(
            b'07/Feb:11:40:00 +0000] "GET /login.php HTTP/1.1" 200 2532 "-" "Mozilla/5.0 (X11; Ubuntu; '
            b'Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d/%b:%H:%M:%S %z',
                                                       datetime.timezone.utc,
                                                       None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(
            match_context.match_data,
            b'] "GET /login.php HTTP/1.1" 200 2532 "-" "Mozilla/5.0 (X11; Ubuntu; '
            b'Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0')

        match_context = MatchContext(b'07.02 11:40:00 UTC: it still works')
        date_time_model_element = DateTimeModelElement('path',
                                                       b'%d.%m %H:%M:%S %z',
                                                       datetime.timezone.utc,
                                                       None, 2017)
        self.assertEqual(
            date_time_model_element.get_match_element(
                'match1', match_context).get_match_object(), 1486467600)
        self.assertEqual(match_context.match_data,
                         self.__expected_match_context)
class TimeCorrelationViolationDetectorTest(TestBase):
    __expected_string = '%s Correlation rule "%s" violated\nTimeCorrelationViolationDetector: "%s" (%d lines)\n  FAIL: '
    __expected_string_not_found = __expected_string + 'B-Event for "%s" (%s) not found!\n\n'
    __expected_string_too_early = __expected_string + 'B-Event for "%s" (%s) was found too early!\n\n\n'
    __expected_string_too_late = __expected_string + 'B-Event for "%s" (%s) was not found in time!\n\n\n'
    __expected_string_different_attributes = __expected_string + '"%s" (%s) %d is not equal %d\n\n\n'

    model = '/model'
    datetime_format_string = '%Y-%m-%d %H:%M:%S'

    service_children1 = [
        FixedDataModelElement('Value1Key', b'Value1: '), FixedDataModelElement('Value1Value', b'fixed Value1'),
        FixedDataModelElement('Value2Key', b', Value2: '), DecimalIntegerValueModelElement('Value2Value'),
        FixedDataModelElement('Value3Key', b', Value3: '), FixedDataModelElement('Value3Value', b'fixed Value3'),
        FixedDataModelElement('Value4Key', b', Value4: '), FixedDataModelElement('Value4Value', b'fixed Value4')]

    service_children2 = [
        FixedDataModelElement('Value1Key', b'Value1: '), FixedDataModelElement('Value1Value', b'fixed Value1'),
        FixedDataModelElement('Value2Key', b', Value2: '), FixedDataModelElement('Value2Value', b'fixed Value2'),
        FixedDataModelElement('Value3Key', b', Value3: '), DecimalIntegerValueModelElement('Value3Value'),
        FixedDataModelElement('Value4Key', b', Value4: '), FixedDataModelElement('Value4Value', b'fixed Value4')]

    match_context1 = MatchContext(b'Value1: fixed Value1, Value2: 22500, Value3: fixed Value3, Value4: fixed Value4')
    match_context2 = MatchContext(b'Value1: fixed Value1, Value2: fixed Value2, Value3: 22500, Value4: fixed Value4')
    match_context2_different = MatchContext(b'Value1: fixed Value1, Value2: fixed Value2, Value3: 22501, Value4: fixed Value4')

    seq1 = SequenceModelElement('sequence1', service_children1)
    seq2 = SequenceModelElement('sequence2', service_children2)

    match_element1 = seq1.get_match_element(model, match_context1)
    match_element2 = seq2.get_match_element(model, match_context2)
    match_element2_different = seq2.get_match_element(model, match_context2_different)

    def setUp(self):
        TestBase.setUp(self)
        self.correlation_rule = CorrelationRule('Correlation', 1, 1.2, max_artefacts_a_for_single_b=1, artefact_match_parameters=[
            ('/model/sequence1/Value2Value', '/model/sequence2/Value3Value')])
        self.a_class_selector = EventClassSelector('Selector1', [self.correlation_rule], None)
        self.b_class_selector = EventClassSelector('Selector2', None, [self.correlation_rule])
        self.rules = []
        self.rules.append(Rules.PathExistsMatchRule('/model/sequence1/Value2Key', self.a_class_selector))
        self.rules.append(Rules.PathExistsMatchRule('/model/sequence2/Value3Key', self.b_class_selector))

    def test1_check_status_ok(self):
        """In this test case the status is OK after receiving the expected data and no error message is returned. The output of the
        do_timer-method is also tested in this test case. """
        description = "Test1TimeCorrelationViolationDetector"
        time_correlation_violation_detector = TimeCorrelationViolationDetector(self.analysis_context.aminer_config, self.rules,
                                                                               [self.stream_printer_event_handler])
        self.analysis_context.register_component(time_correlation_violation_detector, component_name=description)

        log_atom1 = LogAtom(self.match_context1.match_data, ParserMatch(self.match_element1), time.time(), self)
        time_correlation_violation_detector.receive_atom(log_atom1)
        log_atom2 = LogAtom(self.match_context2.match_data, ParserMatch(self.match_element2), time.time() + 1, self)
        time_correlation_violation_detector.receive_atom(log_atom2)

        time_correlation_violation_detector.do_timer(time.time())
        self.assertEqual(self.output_stream.getvalue(), "")

    def test2_check_status_not_found_error(self):
        """In this test case the second log line is not found and an appropriate error message is expected from the check_status-method.
        The output of the do_timer-method is also tested in this test case. """
        description = "Test2TimeCorrelationViolationDetector"
        time_correlation_violation_detector = TimeCorrelationViolationDetector(self.analysis_context.aminer_config, self.rules,
                                                                               [self.stream_printer_event_handler])
        self.analysis_context.register_component(time_correlation_violation_detector, component_name=description)
        t = time.time()
        log_atom1 = LogAtom(self.match_context1.match_data, ParserMatch(self.match_element1), t, self)
        time_correlation_violation_detector.receive_atom(log_atom1)
        r = self.correlation_rule.check_status(t + 2)
        self.assertEqual(r[0], 'FAIL: B-Event for "%s" (%s) was not found in time!\n' % (
            self.match_element1.get_match_string().decode(), self.a_class_selector.action_id))

    def test3_check_status_before_expected_timespan(self):
        """In this test case the second log line is found too early and an appropriate error message is expected from the
        check_status-method. The output of the do_timer-method is also tested in this test case."""
        description = "Test3TimeCorrelationViolationDetector"
        time_correlation_violation_detector = TimeCorrelationViolationDetector(self.analysis_context.aminer_config, self.rules,
                                                                               [self.stream_printer_event_handler])
        self.analysis_context.register_component(time_correlation_violation_detector, component_name=description)

        t = time.time()
        log_atom1 = LogAtom(self.match_context1.match_data, ParserMatch(self.match_element1), t, self)
        time_correlation_violation_detector.receive_atom(log_atom1)
        log_atom2 = LogAtom(self.match_context2.match_data, ParserMatch(self.match_element2), time.time(), self)
        time_correlation_violation_detector.receive_atom(log_atom2)
        time_correlation_violation_detector.do_timer(time.time())
        self.assertEqual(self.output_stream.getvalue(), self.__expected_string_too_early % (
            datetime.fromtimestamp(t).strftime(self.datetime_format_string), self.correlation_rule.rule_id, description, 1,
            self.match_element1.get_match_string().decode(), self.a_class_selector.action_id))

    def test4_check_status_after_expected_timespan(self):
        """In this test case the second log line is found too late and an appropriate error message is expected from the
        check_status-method. The output of the do_timer-method is also tested in this test case."""
        description = "Test4TimeCorrelationViolationDetector"
        time_correlation_violation_detector = TimeCorrelationViolationDetector(self.analysis_context.aminer_config, self.rules,
                                                                               [self.stream_printer_event_handler])
        self.analysis_context.register_component(time_correlation_violation_detector, component_name=description)

        t = time.time()
        log_atom1 = LogAtom(self.match_context1.match_data, ParserMatch(self.match_element1), t, self)
        time_correlation_violation_detector.receive_atom(log_atom1)
        log_atom2 = LogAtom(self.match_context2.match_data, ParserMatch(self.match_element2), t + 5, self)
        time_correlation_violation_detector.receive_atom(log_atom2)
        time_correlation_violation_detector.do_timer(time.time())
        self.assertEqual(self.output_stream.getvalue(), self.__expected_string_too_late % (
            datetime.fromtimestamp(t).strftime(self.datetime_format_string), self.correlation_rule.rule_id, description, 1,
            self.match_element1.get_match_string().decode(), self.a_class_selector.action_id))

    def test5_check_status_attributes_not_matching(self):
        """In this test case the second log line has different attributes than expected and an appropriate error message is expected from
        the check_status-method. The output of the do_timer-method is also tested in this test case."""
        description = "Test5TimeCorrelationViolationDetector"
        time_correlation_violation_detector = TimeCorrelationViolationDetector(self.analysis_context.aminer_config, self.rules,
                                                                               [self.stream_printer_event_handler])
        self.analysis_context.register_component(time_correlation_violation_detector, component_name=description)

        t = time.time()
        log_atom1 = LogAtom(self.match_context1.match_data, ParserMatch(self.match_element1), t, self)
        time_correlation_violation_detector.receive_atom(log_atom1)
        log_atom2 = LogAtom(self.match_context2.match_data, ParserMatch(self.match_element2_different), t + 1, self)
        time_correlation_violation_detector.receive_atom(log_atom2)
        time_correlation_violation_detector.do_timer(time.time())
        self.assertEqual(self.output_stream.getvalue(), self.__expected_string_different_attributes % (
            datetime.fromtimestamp(t).strftime(self.datetime_format_string), self.correlation_rule.rule_id, description, 1,
            self.match_element1.get_match_string().decode(), self.a_class_selector.action_id, 22500, 22501))

    def test6_prepare_history_entry(self):
        """In this test case the prepare_history_entry-method is tested with multiple artefact_match_parameters. Also the case of not
        finding a parameter is tested."""
        t = time.time()
        p1 = ParserMatch(self.match_element1)
        p2 = ParserMatch(self.match_element2)
        log_atom1 = LogAtom(self.match_context1.match_data, p1, t, self)
        log_atom2 = LogAtom(self.match_context2.match_data, p2, t + 5, self)

        result = self.correlation_rule.prepare_history_entry(self.a_class_selector, log_atom1)
        self.assertEqual(result, [t, 0, self.a_class_selector, p1, 22500])
        result = self.correlation_rule.prepare_history_entry(self.b_class_selector, log_atom2)
        self.assertEqual(result, [t + 5, 0, self.b_class_selector, p2, 22500])
Exemple #16
0
 def generate_errored_data(self, iterations, diff, error_rate):
     """Generate data with errors according to the error_rate."""
     log_atoms = []
     t = time()
     divisor = 1
     while error_rate * divisor < 1:
         divisor = divisor * 10
     err = divisor * error_rate
     divisor //= err
     for i in range(1, iterations+1):
         if i % divisor == 0 and i != 0:
             char = bytes([self.alphabet[int(i + random.uniform(diff+1, len(self.alphabet))) % len(self.alphabet)]])
         else:
             char = bytes([self.alphabet[i % len(self.alphabet)]])
         parser_match = ParserMatch(self.alphabet_model.get_match_element('parser', MatchContext(char)))
         t += diff
         log_atoms.append(LogAtom(char, parser_match, t, self.__class__.__name__))
     return log_atoms
 def test2receive_atoms_with_defined_path_list(self):
     """
     In this test case multiple log_atoms are received with default values of the EventTypeDetector.
     path_list is set to a static list of paths and variable_key_list should not be used.
     """
     event_type_detector = EventTypeDetector(
         self.aminer_config, [self.stream_printer_event_handler], path_list=['parser/type/path/nametype'])
     results = [True, False, True, False, True, False, True, True, False, False, True, True, False, True, False, True, False, True,
                False, False, True]
     log_atoms = []
     for line in self.log_lines:
         t = time.time()
         log_atoms.append(
             LogAtom(line, ParserMatch(self.parsing_model.get_match_element('parser', MatchContext(line))), t, self.__class__.__name__))
     for i, log_atom in enumerate(log_atoms):
         old_vals = (event_type_detector.num_events, event_type_detector.num_eventlines,
                     event_type_detector.total_records, event_type_detector.longest_path)
         self.assertEqual(event_type_detector.receive_atom(log_atom), not results[i], i)
         if results[i]:
             self.assertEqual(old_vals, (
                 event_type_detector.num_events, event_type_detector.num_eventlines,
                 event_type_detector.total_records, event_type_detector.longest_path))
Exemple #18
0
class RuleTest(TestBase):
    """
    NOTE: DebugMatchRule and DebugHistoryMatchRule are intentionally not tested, as there is not much to be tested.
    ParallelMatchRule is also not tested as it is very similar to the OrMatchRule.
    """

    __expected_string = '%s This message was generated, when the unit were successful.\n%s: "%s" (%d lines)\n  %s\n\n'

    match_s1 = 'match/s1'
    fixed_string = b'fixed String'
    match_any = 'match/any'
    alphabet = 'There are 26 letters in the english alphabet'
    model_syslog_time = '/model/syslog/time'
    model_syslog = '/model/syslog'
    match_ipv4 = 'match/IPv4'

    match_context_fixed_dme = MatchContext(b'25000')
    fixed_dme = FixedDataModelElement('s1', b'25000')
    match_element_fixed_dme = fixed_dme.get_match_element(
        "fixed", match_context_fixed_dme)

    def test1event_generation_match_action(self):
        """This test case checks if events are generated and pushed to all event handlers."""
        description = "Test1Rules"
        output_stream2 = StringIO()
        message = 'This message was generated, when the unit were successful.'

        match_context = MatchContext(b'25537')
        decimal_integer_value_me = DecimalIntegerValueModelElement(
            'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
            DecimalIntegerValueModelElement.PAD_TYPE_NONE)
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        stream_printer_event_handler2 = StreamPrinterEventHandler(
            self.analysis_context, output_stream2)

        t = time()
        event_generation_match_action = EventGenerationMatchAction(
            'Test.%s' % self.__class__.__name__, message,
            [self.stream_printer_event_handler, stream_printer_event_handler2])
        self.analysis_context.register_component(event_generation_match_action,
                                                 description)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), t,
                           event_generation_match_action)
        event_generation_match_action.match_action(log_atom)

        self.assertEqual(self.output_stream.getvalue(),
                         output_stream2.getvalue())
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime("%Y-%m-%d %H:%M:%S"),
             event_generation_match_action.__class__.__name__, description, 1,
             log_atom.parser_match.match_element.annotate_match('')))

    def test2atom_filter_match_action(self):
        """This test case proves the functionality of the AtomFilters."""
        description = "Test2Rules"
        newMatchPathDetector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        logAtomFixedDME = LogAtom(self.fixed_dme.fixed_data,
                                  ParserMatch(self.match_element_fixed_dme),
                                  time(), newMatchPathDetector)
        subhandlerFilter = SubhandlerFilter([newMatchPathDetector])
        self.analysis_context.register_component(subhandlerFilter, description)
        self.analysis_context.register_component(newMatchPathDetector,
                                                 description + "2")

        self.assertTrue(subhandlerFilter.receive_atom(logAtomFixedDME))

    def test3path_exists_match_rule(self):
        """This case unit the PathExistsMatchRule."""
        description = "Test3Rules"
        path_exists_match_rule = PathExistsMatchRule(self.match_s1, None)
        self.analysis_context.register_component(path_exists_match_rule,
                                                 description)
        self.fixed_dme = FixedDataModelElement('s1', self.fixed_string)
        t = time()

        match_context = MatchContext(self.fixed_string)
        match_element = self.fixed_dme.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), t,
                           path_exists_match_rule)

        self.assertTrue(path_exists_match_rule.match(log_atom))

        self.fixed_dme = FixedDataModelElement('s2', self.fixed_string)
        match_context = MatchContext(self.fixed_string)
        match_element = self.fixed_dme.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), t,
                           path_exists_match_rule)

        self.assertTrue(not path_exists_match_rule.match(log_atom))

    def test4value_match_rule(self):
        """This case unit the ValueMatchRule."""
        description = "Test4Rules"
        value_match_rule = ValueMatchRule(self.match_s1, self.fixed_string,
                                          None)
        self.analysis_context.register_component(value_match_rule, description)
        self.fixed_dme = FixedDataModelElement('s1', self.fixed_string)

        match_context = MatchContext(self.fixed_string)
        match_element = self.fixed_dme.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1, value_match_rule)
        self.assertTrue(value_match_rule.match(log_atom))

        self.fixed_dme = FixedDataModelElement('s1', b'another fixed String')
        match_context = MatchContext(b'another fixed String')
        match_element = self.fixed_dme.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1, value_match_rule)
        self.assertTrue(not value_match_rule.match(log_atom))

    def test5value_list_match_rule(self):
        """This case unit the ValueListMatchRule."""
        description = "Test5Rules"
        value_list_match_rule = ValueListMatchRule(
            'match/d1', [1, 2, 4, 8, 16, 32, 64, 128, 256, 512], None)
        self.analysis_context.register_component(value_list_match_rule,
                                                 description)
        decimal_integer_value_me = DecimalIntegerValueModelElement(
            'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
            DecimalIntegerValueModelElement.PAD_TYPE_NONE)

        match_context = MatchContext(b'64')
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_list_match_rule)
        self.assertTrue(value_list_match_rule.match(log_atom))

        match_context = MatchContext(b'4711')
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_list_match_rule)
        self.assertTrue(not value_list_match_rule.match(log_atom))

    def test6value_range_match_rule(self):
        """This case unit the ValueRangeMatchRule."""
        description = "Test6Rules"
        value_range_match_rule = ValueRangeMatchRule('match/d1', 1, 1000, None)
        self.analysis_context.register_component(value_range_match_rule,
                                                 description)
        decimal_integer_value_me = DecimalIntegerValueModelElement(
            'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
            DecimalIntegerValueModelElement.PAD_TYPE_NONE)

        match_context = MatchContext(b'1')
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_range_match_rule)
        self.assertTrue(value_range_match_rule.match(log_atom))

        match_context = MatchContext(b'1000')
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_range_match_rule)
        self.assertTrue(value_range_match_rule.match(log_atom))

        match_context = MatchContext(b'0')
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_range_match_rule)
        self.assertTrue(not value_range_match_rule.match(log_atom))

        match_context = MatchContext(b'1001')
        match_element = decimal_integer_value_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_range_match_rule)
        self.assertTrue(not value_range_match_rule.match(log_atom))

    def test7string_regex_match_rule(self):
        """This case unit the StringRegexMatchRule."""
        description = "Test7Rules"
        string_regex_match_rule = StringRegexMatchRule(self.match_any,
                                                       re.compile(r'\w'), None)
        self.analysis_context.register_component(string_regex_match_rule,
                                                 description)
        any_byte_date_me = AnyByteDataModelElement('any')

        match_context = MatchContext(self.alphabet)
        match_element = any_byte_date_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           string_regex_match_rule)
        self.assertTrue(string_regex_match_rule.match(log_atom))

        match_context = MatchContext(
            '--> There are 26 letters in the english alphabet')
        match_element = any_byte_date_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           string_regex_match_rule)
        self.assertTrue(not string_regex_match_rule.match(log_atom))

    def test8modulo_time_match_rule(self):
        """This case unit the ModuloTimeMatchRule."""
        description = "Test8Rules"
        modulo_time_match_rule = ModuloTimeMatchRule(self.model_syslog_time,
                                                     86400, 43200, 86400, None)
        self.analysis_context.register_component(modulo_time_match_rule,
                                                 description)
        date_time_model_element = DateTimeModelElement('time',
                                                       b'%d.%m.%Y %H:%M:%S',
                                                       timezone.utc)

        match_context = MatchContext(b'14.02.2019 13:00:00')
        match_element = date_time_model_element.get_match_element(
            self.model_syslog, match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           date_time_model_element)
        self.assertTrue(modulo_time_match_rule.match(log_atom))

        match_context = MatchContext(b'15.02.2019 00:00:00')
        match_element = date_time_model_element.get_match_element(
            self.model_syslog, match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           date_time_model_element)
        self.assertTrue(not modulo_time_match_rule.match(log_atom))

        match_context = MatchContext(b'14.02.2019 12:00:00')
        match_element = date_time_model_element.get_match_element(
            self.model_syslog, match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           date_time_model_element)
        self.assertTrue(modulo_time_match_rule.match(log_atom))

        match_context = MatchContext(b'15.02.2019 01:00:00')
        match_element = date_time_model_element.get_match_element(
            self.model_syslog, match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           date_time_model_element)
        self.assertTrue(not modulo_time_match_rule.match(log_atom))

    def test9value_dependent_modulo_time_match_rule(self):
        """This case unit the ValueDependentModuloTimeMatchRule. Limit look up not working with tuples."""
        description = "Test9Rules"
        value_dependent_modulo_time_match_rule = ValueDependentModuloTimeMatchRule(
            self.model_syslog_time, 86400, [self.model_syslog_time],
            {1550145600: [43200, 86400]})
        self.analysis_context.register_component(
            value_dependent_modulo_time_match_rule, description)
        date_time_model_element = DateTimeModelElement('time',
                                                       b'%d.%m.%Y %H:%M:%S',
                                                       timezone.utc)

        match_context = MatchContext(b'14.02.2019 12:00:00')
        match_element = date_time_model_element.get_match_element(
            self.model_syslog, match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1550138400,
                           date_time_model_element)
        self.assertTrue(value_dependent_modulo_time_match_rule.match(log_atom))

    def test10ipv4_in_rfc1918_match_rule(self):
        """This case unit the ValueDependentModuloTimeMatchRule."""
        description = "Test10Rules"
        i_pv4_in_rfc1918_match_rule = IPv4InRFC1918MatchRule(self.match_ipv4)
        self.analysis_context.register_component(i_pv4_in_rfc1918_match_rule,
                                                 description)
        ip_address_data_model_element = IpAddressDataModelElement('IPv4')

        # private addresses
        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'192.168.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.16.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.31.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'10.0.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'10.255.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        # public addresses
        match_context = MatchContext(b'192.167.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'192.169.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.15.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(not match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.32.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'9.255.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'11.0.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

    def test11and_match_rule(self):
        """This case unit the AndMatchRule."""
        description = "Test11Rules"
        path_exists_match_rule = PathExistsMatchRule(self.match_ipv4, None)
        self.analysis_context.register_component(path_exists_match_rule,
                                                 description)
        i_pv4_in_rfc1918_match_rule = IPv4InRFC1918MatchRule(self.match_ipv4)
        self.analysis_context.register_component(i_pv4_in_rfc1918_match_rule,
                                                 description + "2")
        and_match_rule = AndMatchRule(
            [path_exists_match_rule, i_pv4_in_rfc1918_match_rule])
        self.analysis_context.register_component(and_match_rule,
                                                 description + "3")
        ip_address_data_model_element = IpAddressDataModelElement('IPv4')

        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(), and_match_rule)
        self.assertTrue(and_match_rule.match(log_atom))

        # changing to IPv6
        path_exists_match_rule = PathExistsMatchRule('match/IPv6', None)
        and_match_rule = AndMatchRule(
            [path_exists_match_rule, i_pv4_in_rfc1918_match_rule])
        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(), and_match_rule)
        self.assertTrue(not and_match_rule.match(log_atom))

    def test12or_match_rule(self):
        """This case unit the OrMatchRule."""
        description = "Test12Rules"
        path_exists_match_rule = PathExistsMatchRule(self.match_ipv4, None)
        self.analysis_context.register_component(path_exists_match_rule,
                                                 description)
        i_pv4_in_rfc1918_match_rule = IPv4InRFC1918MatchRule(self.match_ipv4)
        self.analysis_context.register_component(i_pv4_in_rfc1918_match_rule,
                                                 description + "2")
        or_match_rule = OrMatchRule(
            [path_exists_match_rule, i_pv4_in_rfc1918_match_rule])
        self.analysis_context.register_component(or_match_rule,
                                                 description + "3")
        ip_address_data_model_element = IpAddressDataModelElement('IPv4')

        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(), or_match_rule)
        self.assertTrue(or_match_rule.match(log_atom))

        # changing to IPv6
        path_exists_match_rule = PathExistsMatchRule('match/IPv6', None)
        or_match_rule = OrMatchRule(
            [path_exists_match_rule, i_pv4_in_rfc1918_match_rule])
        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(), or_match_rule)
        self.assertTrue(or_match_rule.match(log_atom))

    def test13value_dependent_delegated_match_rule(self):
        """This case unit the ValueDependentDelegatedMatchRule."""
        description = "Test13Rules"
        string_regex_match_rule = StringRegexMatchRule(self.match_any,
                                                       re.compile(r'\w'), None)
        self.analysis_context.register_component(string_regex_match_rule,
                                                 description)
        any_byte_date_me = AnyByteDataModelElement('any')

        i_pv4_in_rfc1918_match_rule = IPv4InRFC1918MatchRule(self.match_ipv4)
        self.analysis_context.register_component(i_pv4_in_rfc1918_match_rule,
                                                 description + "2")
        ip_address_data_model_element = IpAddressDataModelElement('IPv4')

        value_dependent_delegated_match_rule = ValueDependentDelegatedMatchRule(
            [self.match_any, self.match_ipv4], {
                (self.alphabet, None): string_regex_match_rule,
                (None, 3232235520): i_pv4_in_rfc1918_match_rule
            })
        self.analysis_context.register_component(
            value_dependent_delegated_match_rule, description + "3")

        match_context = MatchContext(self.alphabet)
        match_element = any_byte_date_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_dependent_delegated_match_rule)
        self.assertTrue(value_dependent_delegated_match_rule.match(log_atom))

        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_dependent_delegated_match_rule)
        self.assertTrue(value_dependent_delegated_match_rule.match(log_atom))

        # not matching values
        match_context = MatchContext(
            '.There are 26 letters in the english alphabet')
        match_element = any_byte_date_me.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_dependent_delegated_match_rule)
        self.assertTrue(
            not value_dependent_delegated_match_rule.match(log_atom))

        match_context = MatchContext(b'192.168.0.1')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           value_dependent_delegated_match_rule)
        self.assertTrue(
            not value_dependent_delegated_match_rule.match(log_atom))

    def test14negation_match_rule(self):
        """This case unit the NegationMatchRule."""
        description = "Test14Rules"
        path_exists_match_rule = PathExistsMatchRule(self.match_s1, None)
        self.analysis_context.register_component(path_exists_match_rule,
                                                 description)
        negation_match_rule = NegationMatchRule(path_exists_match_rule)
        self.analysis_context.register_component(negation_match_rule,
                                                 description + "2")
        self.fixed_dme = FixedDataModelElement('s1', self.fixed_string)

        match_context = MatchContext(self.fixed_string)
        match_element = self.fixed_dme.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), 1,
                           path_exists_match_rule)
        self.assertTrue(path_exists_match_rule.match(log_atom))
        self.assertTrue(not negation_match_rule.match(log_atom))
    def consume_data(self, stream_data, end_of_stream_flag=False):
        """
        Consume data from the underlying stream for atomizing.
        @return the number of consumed bytes, 0 if the atomizer would need more data for a complete atom or -1 when no data was
        consumed at the moment but data might be consumed later on.
        """
        # Loop until as much streamData as possible was processed and then return a result. The correct processing of endOfStreamFlag
        # is tricky: by default, even when all data was processed, do one more iteration to handle also the flag.
        consumed_length = 0
        while True:
            if self.last_unconsumed_log_atom is not None:
                # Keep length before dispatching: dispatch will reset the field.
                data_length = len(self.last_unconsumed_log_atom.raw_data)
                if self.dispatch_atom(self.last_unconsumed_log_atom):
                    consumed_length += data_length + len(self.eol_sep)
                    continue
                # Nothing consumed, tell upstream to wait if appropriate.
                if consumed_length == 0:
                    consumed_length = -1
                break

            line_end = None
            global breakout  # skipcq: PYL-W0603
            breakout = False
            global data  # skipcq: PYL-W0603
            data = None
            valid_json = False
            if self.json_format:
                state = json_machine(found_json)
                i = 0
                for i, char in enumerate(stream_data[consumed_length:]):
                    state = state(char)
                    if breakout or state is None or i > self.max_line_length:
                        break
                # check if the json is still valid, but the stream_data is at the end
                if not breakout and state is not None and i + consumed_length == len(
                        stream_data) - 1 and not end_of_stream_flag:
                    return consumed_length
                if 0 < i <= self.max_line_length and b'{' in stream_data[
                        consumed_length:consumed_length + i +
                        1] and data is not None:
                    line_end = consumed_length + i + 1
                    valid_json = True
                elif i > self.max_line_length:
                    self.in_overlong_line_flag = True
            if line_end is None:
                line_end = stream_data.find(self.eol_sep, consumed_length)

            if self.in_overlong_line_flag:
                if line_end < 0:
                    consumed_length = len(stream_data)
                    if end_of_stream_flag:
                        self.dispatch_event(
                            'Overlong line terminated by end of stream',
                            stream_data)
                        self.in_overlong_line_flag = False
                    break
                consumed_length = line_end + len(self.eol_sep)
                self.in_overlong_line_flag = False
                continue

            # This is the valid start of a normal/incomplete/overlong line.
            if line_end < 0:
                tail_length = len(stream_data) - consumed_length
                if tail_length > self.max_line_length:
                    self.dispatch_event('Start of overlong line detected',
                                        stream_data[consumed_length:])
                    self.in_overlong_line_flag = True
                    consumed_length = len(stream_data)
                    # Stay in loop to handle also endOfStreamFlag!
                    continue
                if end_of_stream_flag and (tail_length != 0):
                    self.dispatch_event('Incomplete last line',
                                        stream_data[consumed_length:])
                    consumed_length = len(stream_data)
                break

            # This is at least a complete/overlong line.
            line_length = line_end + len(self.eol_sep) - consumed_length
            if line_length > self.max_line_length and not valid_json:
                self.dispatch_event('Overlong line detected',
                                    stream_data[consumed_length:line_end])
                consumed_length = line_end + len(self.eol_sep)
                continue

            # This is a normal line.
            line_data = stream_data[consumed_length:line_end]
            log_atom = LogAtom(line_data, None, None, self)
            if self.parsing_model is not None:
                match_context = MatchContext(line_data)
                match_element = self.parsing_model.get_match_element(
                    '', match_context)
                if (match_element
                        is not None) and not match_context.match_data:
                    log_atom.parser_match = ParserMatch(match_element)
                    for default_timestamp_path in self.default_timestamp_paths:
                        ts_match = log_atom.parser_match.get_match_dictionary(
                        ).get(default_timestamp_path, None)
                        if ts_match is not None:
                            log_atom.set_timestamp(ts_match.match_object)
                            break
            if self.dispatch_atom(log_atom):
                consumed_length = line_end + len(self.eol_sep) - (
                    valid_json
                    and stream_data[line_end:line_end + len(self.eol_sep)] !=
                    self.eol_sep)
                continue
            if consumed_length == 0:
                # Downstream did not want the data, so tell upstream to block for a while.
                consumed_length = -1
            break
        return consumed_length
Exemple #20
0
    def test10ipv4_in_rfc1918_match_rule(self):
        """This case unit the ValueDependentModuloTimeMatchRule."""
        description = "Test10Rules"
        i_pv4_in_rfc1918_match_rule = IPv4InRFC1918MatchRule(self.match_ipv4)
        self.analysis_context.register_component(i_pv4_in_rfc1918_match_rule,
                                                 description)
        ip_address_data_model_element = IpAddressDataModelElement('IPv4')

        # private addresses
        match_context = MatchContext(b'192.168.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'192.168.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.16.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.31.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'10.0.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'10.255.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(i_pv4_in_rfc1918_match_rule.match(log_atom))

        # public addresses
        match_context = MatchContext(b'192.167.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'192.169.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.15.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(not match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'172.32.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'9.255.255.255')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))

        match_context = MatchContext(b'11.0.0.0')
        match_element = ip_address_data_model_element.get_match_element(
            'match', match_context)
        log_atom = LogAtom(match_context.match_data,
                           ParserMatch(match_element), time(),
                           i_pv4_in_rfc1918_match_rule)
        self.assertTrue(not i_pv4_in_rfc1918_match_rule.match(log_atom))
Exemple #21
0
class PersistenceUtilTest(TestBase):
    """Unittests for the PersistenceUtil class."""

    string = b'25537 uid=2'

    match_context_fixed_dme = MatchContext(b' pid=')
    fixed_dme = FixedDataModelElement('s1', b' pid=')
    match_element_fixed_dme = fixed_dme.get_match_element(
        "", match_context_fixed_dme)

    match_context_decimal_integer_value_me = MatchContext(string)
    decimal_integer_value_me = DecimalIntegerValueModelElement(
        'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
        DecimalIntegerValueModelElement.PAD_TYPE_NONE)
    match_element_decimal_integer_value_me = decimal_integer_value_me.get_match_element(
        "", match_context_decimal_integer_value_me)

    fixed_dme = FixedDataModelElement('s1', string)

    decimal_integer_value_me = DecimalIntegerValueModelElement(
        'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
        DecimalIntegerValueModelElement.PAD_TYPE_NONE)

    match_context_first_match_me = MatchContext(string)
    first_match_me = FirstMatchModelElement(
        'f1', [fixed_dme, decimal_integer_value_me])
    match_element_first_match_me = first_match_me.get_match_element(
        'first', match_context_first_match_me)

    match_context_first_match_me2 = MatchContext(string)
    first_match_me2 = FirstMatchModelElement(
        'f2', [decimal_integer_value_me, fixed_dme])
    match_element_first_match_me2 = first_match_me2.get_match_element(
        'second', match_context_first_match_me2)

    def test1persist_multiple_objects_of_single_class(self):
        """In this test case multiple instances of one class are to be persisted and loaded."""
        description = "Test1PersistenceUtil"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            True)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)

        t = time.time()
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        log_atom_decimal_integer_value_me = LogAtom(
            self.match_context_decimal_integer_value_me.match_data,
            ParserMatch(self.match_element_decimal_integer_value_me), t,
            new_match_path_detector)
        new_match_path_detector.receive_atom(log_atom_fixed_dme)
        new_match_path_detector.receive_atom(log_atom_decimal_integer_value_me)

        other_new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'otherDetector', True)
        self.analysis_context.register_component(other_new_match_path_detector,
                                                 description + "2")
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, other_new_match_path_detector)
        other_new_match_path_detector.receive_atom(log_atom_fixed_dme)

        PersistenceUtil.persist_all()
        persistence_data = PersistenceUtil.load_json(
            new_match_path_detector.persistence_file_name)
        self.assertTrue(persistence_data in ([
            self.match_element_fixed_dme.get_path(),
            self.match_element_decimal_integer_value_me.get_path()
        ], [
            self.match_element_decimal_integer_value_me.get_path(),
            self.match_element_fixed_dme.get_path()
        ]))
        self.assertEqual(
            PersistenceUtil.load_json(
                other_new_match_path_detector.persistence_file_name),
            [self.match_element_fixed_dme.get_path()])

    def test2persist_multiple_objects_of_multiple_class(self):
        """In this test case multiple instances of multiple classes are to be persisted and loaded."""
        description = "Test2PersistenceUtil"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'Default2', True)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)

        t = time.time()
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        log_atom_decimal_integer_value_me = LogAtom(
            self.match_context_decimal_integer_value_me.match_data,
            ParserMatch(self.match_element_decimal_integer_value_me), t,
            new_match_path_detector)
        new_match_path_detector.receive_atom(log_atom_fixed_dme)
        new_match_path_detector.receive_atom(log_atom_decimal_integer_value_me)

        other_new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler],
            'otherDetector2', True)
        self.analysis_context.register_component(other_new_match_path_detector,
                                                 description + "2")
        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, other_new_match_path_detector)
        other_new_match_path_detector.receive_atom(log_atom_fixed_dme)

        new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, ['first/f1/s1'],
            [self.stream_printer_event_handler], 'Default', False, True)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector, description + "3")
        log_atom_sequence_me = LogAtom(
            self.fixed_dme.fixed_data,
            ParserMatch(self.match_element_first_match_me), t,
            new_match_path_value_combo_detector)
        new_match_path_value_combo_detector.receive_atom(log_atom_sequence_me)

        PersistenceUtil.persist_all()
        persistence_data = PersistenceUtil.load_json(
            new_match_path_detector.persistence_file_name)
        self.assertTrue(persistence_data in ([
            self.match_element_fixed_dme.get_path(),
            self.match_element_decimal_integer_value_me.get_path()
        ], [
            self.match_element_decimal_integer_value_me.get_path(),
            self.match_element_fixed_dme.get_path()
        ]))
        self.assertEqual(
            PersistenceUtil.load_json(
                other_new_match_path_detector.persistence_file_name),
            [self.match_element_fixed_dme.get_path()])
        self.assertEqual(
            PersistenceUtil.load_json(
                new_match_path_value_combo_detector.persistence_file_name),
            ([[log_atom_sequence_me.raw_data]]))
    def test4max_items_overflow(self):
        """In this test case more events than the VolatileLogarithmicBackoffEventHistory can handle are received."""
        deviation = 0.05
        size = 100000
        msg = "%s=%f is not between %f and %f"

        match_context = MatchContext(self.pid)
        fixed_dme = FixedDataModelElement('s1', self.pid)
        match_element = fixed_dme.get_match_element("match", match_context)

        t = time()
        log_atom = LogAtom(fixed_dme.fixed_data, ParserMatch(match_element), t, self)
        message = self.new_val % (self.match_s1, self.match_s2, repr(match_element.match_object))
        first = 0
        second = 0
        third = 0
        fourth = 0

        for _ in range(size):
            volatile_logarithmic_backoff_event_history = VolatileLogarithmicBackoffEventHistory(2)
            volatile_logarithmic_backoff_event_history.receive_event(self.test % self.__class__.__name__, message, [
                log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)

            volatile_logarithmic_backoff_event_history.receive_event(self.test % self.__class__.__name__, message, [
                log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)

            volatile_logarithmic_backoff_event_history.receive_event(self.test % self.__class__.__name__, message, [
                log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)

            volatile_logarithmic_backoff_event_history.receive_event(self.test % self.__class__.__name__, message, [
                log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)

            volatile_logarithmic_backoff_event_history.receive_event(self.test % self.__class__.__name__, message, [
                log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)

            history = volatile_logarithmic_backoff_event_history.get_history()
            if history == [(0, self.test % self.__class__.__name__, message, [log_atom.raw_data, log_atom.raw_data], None,
                           log_atom.get_parser_match(), self), (4, self.test % self.__class__.__name__, message,
                           [log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)]:
                first += 1
            elif history == [(1, self.test % self.__class__.__name__, message, [log_atom.raw_data, log_atom.raw_data], None,
                             log_atom.get_parser_match(), self), (4, self.test % self.__class__.__name__, message,
                             [log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)]:
                second += 1
            elif history == [(2, self.test % self.__class__.__name__, message, [log_atom.raw_data, log_atom.raw_data], None,
                             log_atom.get_parser_match(), self), (4, self.test % self.__class__.__name__, message,
                             [log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)]:
                third += 1
            elif history == [(3, self.test % self.__class__.__name__, message, [log_atom.raw_data, log_atom.raw_data], None,
                             log_atom.get_parser_match(), self), (4, self.test % self.__class__.__name__, message,
                             [log_atom.raw_data, log_atom.raw_data], None, log_atom.get_parser_match(), self)]:
                fourth += 1
        val = 0.5 * 0.5 * 0.5
        minimum = size * val * (1 - deviation)
        maximum = size * val * (1 + deviation)
        self.assertTrue(minimum <= first <= maximum, msg % ("first", first, minimum, maximum))

        val = 0.5 * 0.5 * 0.5
        minimum = size * val * (1 - deviation)
        maximum = size * val * (1 + deviation)
        self.assertTrue(minimum <= second <= maximum, msg % ("second", second, minimum, maximum))

        val = 2 * 0.5 * 0.5 * 0.5
        minimum = size * val * (1 - deviation)
        maximum = size * val * (1 + deviation)
        self.assertTrue(minimum <= third <= maximum, msg % ("third", third, minimum, maximum))

        val = 0.5
        minimum = size * val * (1 - deviation)
        maximum = size * val * (1 + deviation)
        self.assertTrue(minimum <= fourth <= maximum, msg % ("fourth", fourth, minimum, maximum))
Exemple #23
0
class NewMatchPathValueComboDetectorTest(TestBase):
    """Unittests for the NewMatchPathValueComboDetector."""

    __expected_string = '%s New value combination(s) detected\n%s: "%s" (%d lines)\n%s\n\n'
    fixed_dme = FixedDataModelElement('s1', b'25537 uid=')
    fixed_dme2 = FixedDataModelElement('s2', b' uid=2')
    datetime_format_string = '%Y-%m-%d %H:%M:%S'
    first_seq_s1 = 'first/seq/s1'
    first_seq_d1 = 'first/seq/d1'
    string = "  first/seq: b'25537 uid=2'\n  " + first_seq_s1 + ": 25537 uid=\n  " + first_seq_d1 + ": 2\n(b'25537 uid=', 2)"
    string2 = "  (b'25537 uid=', 2)\n25537 uid=2"

    decimal_integer_value_me = DecimalIntegerValueModelElement(
        'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
        DecimalIntegerValueModelElement.PAD_TYPE_NONE)

    match_context_sequence_me = MatchContext(b'25537 uid=2')
    seq = SequenceModelElement('seq', [fixed_dme, decimal_integer_value_me])
    match_element_sequence_me = seq.get_match_element(
        'first', match_context_sequence_me)

    match_context_sequence_me2 = MatchContext(b'25537 uid=2')
    seq2 = SequenceModelElement('seq2', [decimal_integer_value_me, fixed_dme2])
    match_element_sequence_me2 = seq2.get_match_element(
        'second', match_context_sequence_me2)

    def test1_log_atom_not_known(self):
        """
        This test case checks the correct processing of unknown log lines, which in reality means that an anomaly has been found.
        The output is directed to an output stream and compared for accuracy. The auto_include_flag is False and the output must be
        repeatable on second run.
        """
        description = "Test1NewMatchPathValueComboDetector"
        new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, [self.first_seq_s1, self.first_seq_d1],
            [self.stream_printer_event_handler],
            'Default',
            False,
            False,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector, description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.match_element_sequence_me.get_match_string(),
            ParserMatch(self.match_element_sequence_me), t,
            new_match_path_value_combo_detector)

        self.assertTrue(
            new_match_path_value_combo_detector.receive_atom(
                log_atom_sequence_me))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_combo_detector.__class__.__name__,
             description, 1, self.string2))
        self.reset_output_stream()

        # repeating should produce the same result
        self.assertTrue(
            new_match_path_value_combo_detector.receive_atom(
                log_atom_sequence_me))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_combo_detector.__class__.__name__,
             description, 1, self.string2))
        self.reset_output_stream()

        new_match_path_value_combo_detector2 = NewMatchPathValueComboDetector(
            self.aminer_config, ['second/seq2/d1', 'second/seq2/s2'],
            [self.stream_printer_event_handler],
            'Default',
            False,
            False,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector2, description + "2")

        log_atom_sequence_me2 = LogAtom(
            self.match_element_sequence_me2.get_match_string(),
            ParserMatch(self.match_element_sequence_me2), t,
            new_match_path_value_combo_detector2)

        # other MatchElement
        self.assertTrue(
            new_match_path_value_combo_detector2.receive_atom(
                log_atom_sequence_me2))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_combo_detector.__class__.__name__,
             description + "2", 1, "  (25537, b' uid=2')\n25537 uid=2"))

    def test2_log_atom_known(self):
        """
        This test case checks the functionality of the auto_include_flag.
        If the same MatchElement is processed a second time and the auto_include_flag was True, no event must be triggered.
        """
        description = "Test2NewMatchPathValueComboDetector"
        new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, [self.first_seq_s1, self.first_seq_d1],
            [self.stream_printer_event_handler],
            'Default',
            False,
            True,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector, description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.match_element_sequence_me.get_match_string(),
            ParserMatch(self.match_element_sequence_me), t,
            new_match_path_value_combo_detector)

        self.assertTrue(
            new_match_path_value_combo_detector.receive_atom(
                log_atom_sequence_me))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_combo_detector.__class__.__name__,
             description, 1, self.string2))
        self.reset_output_stream()

        # repeating should NOT produce the same result
        self.assertTrue(
            new_match_path_value_combo_detector.receive_atom(
                log_atom_sequence_me))
        self.assertEqual(self.output_stream.getvalue(), '')
        self.reset_output_stream()

        new_match_path_value_combo_detector2 = NewMatchPathValueComboDetector(
            self.aminer_config, ['second/seq2/d1', 'second/seq2/s2'],
            [self.stream_printer_event_handler],
            'Default',
            False,
            False,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector2, description + "2")

        log_atom_sequence_me2 = LogAtom(
            self.match_element_sequence_me2.get_match_string(),
            ParserMatch(self.match_element_sequence_me2), t,
            new_match_path_value_combo_detector2)

        # other MatchElement
        self.assertTrue(
            new_match_path_value_combo_detector2.receive_atom(
                log_atom_sequence_me2))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_combo_detector.__class__.__name__,
             description + "2", 1, "  (25537, b' uid=2')\n25537 uid=2"))

    def test3_log_atom_known_from_persisted_data(self):
        """The persisting and reading of permitted log lines should be checked with this test."""
        description = "Test3NewMatchPathValueComboDetector"
        new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, [self.first_seq_s1, self.first_seq_d1],
            [self.stream_printer_event_handler],
            'Default',
            False,
            True,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector, description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.match_element_sequence_me.get_match_string(),
            ParserMatch(self.match_element_sequence_me), t,
            new_match_path_value_combo_detector)

        self.assertTrue(
            new_match_path_value_combo_detector.receive_atom(
                log_atom_sequence_me))
        self.assertEqual(
            self.output_stream.getvalue(), self.__expected_string %
            (datetime.fromtimestamp(t).strftime(self.datetime_format_string),
             new_match_path_value_combo_detector.__class__.__name__,
             description, 1, self.string2))
        new_match_path_value_combo_detector.do_persist()
        self.reset_output_stream()

        other_new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, [self.first_seq_s1, self.first_seq_d1],
            [self.stream_printer_event_handler],
            'Default',
            False,
            True,
            output_log_line=False)
        self.analysis_context.register_component(
            other_new_match_path_value_combo_detector, description + "2")
        other_log_atom_fixed_dme = LogAtom(
            self.match_element_sequence_me.get_match_string(),
            ParserMatch(self.match_element_sequence_me), t,
            other_new_match_path_value_combo_detector)

        self.assertTrue(
            other_new_match_path_value_combo_detector.receive_atom(
                other_log_atom_fixed_dme))
        self.assertEqual(self.output_stream.getvalue(), '')

    def test4_allowlist_event_with_known_and_unknown_paths(self):
        """This test case checks in which cases an event is triggered and compares with expected results."""
        description = "Test4NewMatchPathValueComboDetector"
        new_match_path_value_combo_detector = NewMatchPathValueComboDetector(
            self.aminer_config, [self.first_seq_s1, self.first_seq_d1],
            [self.stream_printer_event_handler],
            'Default',
            False,
            True,
            output_log_line=False)
        self.analysis_context.register_component(
            new_match_path_value_combo_detector, description)

        t = time.time()
        log_atom_sequence_me = LogAtom(
            self.match_element_sequence_me.get_match_string(),
            ParserMatch(self.match_element_sequence_me), t,
            new_match_path_value_combo_detector)
        new_match_path_value_combo_detector.receive_atom(log_atom_sequence_me)
        self.assertEqual(
            new_match_path_value_combo_detector.allowlist_event(
                'Analysis.%s' %
                new_match_path_value_combo_detector.__class__.__name__,
                self.match_element_sequence_me.get_path(), None),
            'Allowlisted path(es) %s with %s.' %
            (", ".join(new_match_path_value_combo_detector.target_path_list),
             self.match_element_sequence_me.get_path()))

        new_match_path_value_combo_detector.auto_include_flag = False
        self.assertEqual(
            new_match_path_value_combo_detector.allowlist_event(
                'Analysis.%s' %
                new_match_path_value_combo_detector.__class__.__name__,
                self.match_element_sequence_me2.get_path(), None),
            'Allowlisted path(es) %s with %s.' %
            (", ".join(new_match_path_value_combo_detector.target_path_list),
             self.match_element_sequence_me2.path))
class JsonConverterHandlerTest(TestBase):
    """Unittests for the JsonConverterHandler."""

    output_log_line = True
    match_context = MatchContext(b' pid=')
    fixed_dme = FixedDataModelElement('s1', b' pid=')
    match_element = fixed_dme.get_match_element("match", match_context)
    t = time.time()

    test_detector = 'Analysis.TestDetector'
    event_message = 'An event happened!'
    sorted_log_lines = ['Event happend at /path/ 5 times.', '', '', '', '']
    persistence_id = 'Default'
    description = 'jsonConverterHandlerDescription'
    expected_string = '%s %s\n%s: "%s" (5 lines)\n  {\n  "AnalysisComponent": {\n    "AnalysisComponentIdentifier": 0,\n' \
                      '    "AnalysisComponentType": "%s",\n    "AnalysisComponentName": "%s",\n    "Message": "%s",\n' \
                      '    "PersistenceFileName": "%s",\n    "AffectedParserPaths": [\n      "test/path/1",\n' \
                      '      "test/path/2"\n    ]\n  },\n  "LogData": {\n    "RawLogData": [\n      " pid="\n    ],\n    ' \
                      '"Timestamps": [\n      %s\n    ],\n    "DetectionTimestamp": %s,\n    "LogLinesCount": 5,\n' \
                      '    "AnnotatedMatchElement": "match/s1: b\' pid=\'"\n  }%s\n}\n\n'

    def test1receive_expected_event(self):
        """In this test case a normal Event happens and the json output should be sent to a StreamPrinterEventHandler."""
        json_converter_handler = JsonConverterHandler(
            [self.stream_printer_event_handler], self.analysis_context)
        log_atom = LogAtom(self.fixed_dme.fixed_data,
                           ParserMatch(self.match_element), self.t, self)
        self.analysis_context.register_component(self, self.description)
        event_data = {
            'AnalysisComponent': {
                'AffectedParserPaths': ['test/path/1', 'test/path/2']
            }
        }
        json_converter_handler.receive_event(self.test_detector,
                                             self.event_message,
                                             self.sorted_log_lines, event_data,
                                             log_atom, self)
        detection_timestamp = None
        for line in self.output_stream.getvalue().split('\n'):
            if "DetectionTimestamp" in line:
                detection_timestamp = line.split(':')[1].strip(' ,')
        self.assertEqual(
            self.output_stream.getvalue(), self.expected_string %
            (datetime.fromtimestamp(self.t).strftime("%Y-%m-%d %H:%M:%S"),
             self.event_message, self.__class__.__name__, self.description,
             self.__class__.__name__, self.description, self.event_message,
             self.persistence_id, round(self.t, 2), detection_timestamp, ""))

    def test2receive_event_with_same_event_data_attributes(self):
        """In this test case an attribute of AnalysisComponent is overwritten and an JsonError attribute is expected."""
        json_converter_handler = JsonConverterHandler(
            [self.stream_printer_event_handler], self.analysis_context)
        log_atom = LogAtom(self.fixed_dme.fixed_data,
                           ParserMatch(self.match_element), self.t, self)
        self.analysis_context.register_component(self, self.description)
        event_data = {
            'AnalysisComponent': {
                'AffectedParserPaths': ['test/path/1', 'test/path/2'],
                'Message': 'An other event happened too!'
            }
        }
        json_converter_handler.receive_event(self.test_detector,
                                             self.event_message,
                                             self.sorted_log_lines, event_data,
                                             log_atom, self)
        detection_timestamp = None
        for line in self.output_stream.getvalue().split('\n'):
            if "DetectionTimestamp" in line:
                detection_timestamp = line.split(':')[1].strip(' ,')
        self.assertEqual(
            self.output_stream.getvalue(), self.expected_string %
            (datetime.fromtimestamp(self.t).strftime("%Y-%m-%d %H:%M:%S"),
             self.event_message, self.__class__.__name__, self.description,
             self.__class__.__name__, self.description, self.event_message,
             self.persistence_id, round(self.t, 2), detection_timestamp,
             ',\n  "JsonError": "AnalysisComponent attribute \'Message\' is already in use and can not be overwritten!\\n"'
             ))
Exemple #25
0
    def test10_missing_value_on_persisted(self):
        """Persisting lists is tested in this test case."""
        description = "Test91MissingMatchPathValueDetector"
        match_context_fixed_dme = MatchContext(self.pid)
        fixed_dme = FixedDataModelElement('s2', self.pid)
        match_element_fixed_dme = fixed_dme.get_match_element(
            "match3", match_context_fixed_dme)

        match_context_decimal_integer_value_me = MatchContext(self.string)
        decimal_integer_value_me = DecimalIntegerValueModelElement(
            'd2', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
            DecimalIntegerValueModelElement.PAD_TYPE_NONE)
        match_element_decimal_integer_value_me = decimal_integer_value_me.get_match_element(
            "match4", match_context_decimal_integer_value_me)
        missing_match_path_list_value_detector = MissingMatchPathListValueDetector(
            self.aminer_config, [
                match_element_fixed_dme.get_path(),
                match_element_decimal_integer_value_me.get_path()
            ], [self.stream_printer_event_handler], 'Default', True,
            self.__default_interval, self.__realert_interval)
        self.analysis_context.register_component(
            missing_match_path_list_value_detector, description)
        log_atom_fixed_dme = LogAtom(fixed_dme.fixed_data,
                                     ParserMatch(match_element_fixed_dme),
                                     round(time.time()),
                                     missing_match_path_list_value_detector)
        self.assertTrue(
            missing_match_path_list_value_detector.receive_atom(
                log_atom_fixed_dme))
        missing_match_path_list_value_detector.do_persist()

        past_time = 4000
        t = time.time()
        other_missing_match_path_list_value_detector = MissingMatchPathListValueDetector(
            self.aminer_config, [
                match_element_fixed_dme.get_path(),
                match_element_decimal_integer_value_me.get_path()
            ], [self.stream_printer_event_handler], 'Default', True,
            self.__default_interval, self.__realert_interval)
        self.analysis_context.register_component(
            other_missing_match_path_list_value_detector, description + "2")
        other_missing_match_path_list_value_detector.set_check_value(
            other_missing_match_path_list_value_detector.get_channel_key(
                log_atom_fixed_dme), self.__default_interval - past_time)

        log_atom_fixed_dme = LogAtom(
            fixed_dme.fixed_data, ParserMatch(match_element_fixed_dme),
            round(time.time()) + past_time,
            other_missing_match_path_list_value_detector)
        self.assertTrue(
            other_missing_match_path_list_value_detector.receive_atom(
                log_atom_fixed_dme))
        self.assertTrue(
            (self.output_stream.getvalue() == self.__expected_string %
             (datetime.fromtimestamp(t + past_time).strftime(
                 self.datetime_format_string),
              other_missing_match_path_list_value_detector.__class__.__name__,
              description + "2", 1,
              "match3/s2, match4/d2: b' pid=' overdue 400s (interval -400)"))
            or
            (self.output_stream.getvalue(), self.__expected_string %
             (datetime.fromtimestamp(t + past_time + 1).strftime(
                 self.datetime_format_string),
              other_missing_match_path_list_value_detector.__class__.__name__,
              description + "2", 1,
              "match3/s2, match4/d2: b' pid=' overdue 400s (interval -400)")))
class AtomFiltersTest(TestBase):
    match_context_fixed_dme = MatchContext(b'25000')
    fixed_dme = FixedDataModelElement('s1', b'25000')
    match_element_fixed_dme = fixed_dme.get_match_element(
        "fixed", match_context_fixed_dme)

    match_context_decimal_integer_value_me = MatchContext(b'25000')
    decimal_integer_value_me = DecimalIntegerValueModelElement(
        'd1', DecimalIntegerValueModelElement.SIGN_TYPE_NONE,
        DecimalIntegerValueModelElement.PAD_TYPE_NONE)
    match_element_decimal_integer_value_me = decimal_integer_value_me.get_match_element(
        "integer", match_context_decimal_integer_value_me)

    def test1_no_list_or_no_atom_handler_list(self):
        """This test case verifies, that exceptions are raised when using wrong parameters."""
        self.assertRaises(
            Exception, SubhandlerFilter,
            NewMatchPathDetector(self.aminer_config, [], 'Default', True),
            False)
        self.assertRaises(Exception, SubhandlerFilter,
                          FixedDataModelElement('fixed', b'gesuchter String'),
                          False)

    def test2receive_atom_unhandled(self):
        """In this test case no handler can handle the log atom."""
        description = "Test2AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        subhandler_filter = SubhandlerFilter([], True)
        self.assertTrue(not subhandler_filter.receive_atom(log_atom_fixed_dme))

    def test3receive_atom_handled_by_more_handlers(self):
        """In this test case more than one handler can handle the log atom. The impact of the stop_when_handled flag is tested."""
        description = "Test3AtomFilters"
        other_description = "Test3OtherAtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        other_new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(other_new_match_path_detector,
                                                 other_description)

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        subhandler_filter = SubhandlerFilter(
            [new_match_path_detector, other_new_match_path_detector], False)
        self.assertTrue(subhandler_filter.receive_atom(log_atom_fixed_dme))
        result = self.output_stream.getvalue()
        self.reset_output_stream()

        new_match_path_detector.receive_atom(log_atom_fixed_dme)
        resultFixedDME = self.output_stream.getvalue()
        self.reset_output_stream()

        other_new_match_path_detector.receive_atom(log_atom_fixed_dme)
        result_decimal_integer_value_me = self.output_stream.getvalue()

        self.assertEqual(result,
                         resultFixedDME + result_decimal_integer_value_me)

    def test4match_path_filter_receive_atom_path_in_dictionary(self):
        """There is a path in the dictionary and the handler is not None. The default_parsed_atom_handler is None."""
        description = "Test4AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        match_path_filter = MatchPathFilter([
            (self.match_element_fixed_dme.get_path(), new_match_path_detector)
        ], None)
        self.assertTrue(match_path_filter.receive_atom(log_atom_fixed_dme))

    def test5match_path_filter_receive_atom_path_not_in_dictionary(self):
        """The searched path is not in the dictionary. The default_parsed_atom_handler is None."""
        description = "Test5AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        match_path_filter = MatchPathFilter(
            [(self.match_element_decimal_integer_value_me.get_path(),
              new_match_path_detector)], None)
        self.assertTrue(not match_path_filter.receive_atom(log_atom_fixed_dme))

    def test6match_path_filter_receive_atom_path_not_in_dictionary_default_set(
            self):
        """The searched path is not in the dictionary. The default_parsed_atom_handler is set."""
        description = "Test6AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        match_path_filter = MatchPathFilter(
            [(self.match_element_decimal_integer_value_me.get_path(),
              new_match_path_detector)], new_match_path_detector)
        self.assertTrue(match_path_filter.receive_atom(log_atom_fixed_dme))

    def test7match_value_filter_receive_atom_target_value_and_handler_found(
            self):
        """A target_value and a handler, which can handle the matchObject is found."""
        description = "Test7AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        match_value_filter = MatchValueFilter(
            self.match_element_fixed_dme.get_path(),
            {self.fixed_dme.fixed_data: new_match_path_detector}, None)
        self.assertTrue(match_value_filter.receive_atom(log_atom_fixed_dme))

    def test8match_value_filter_receive_atom_target_value_found_handler_not_found(
            self):
        """A target_value was found, but no handler can handle it. DefaultParsedAtomHandler = None"""
        description = "Test8AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(self.fixed_dme.fixed_data,
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)

        match_value_filter = MatchValueFilter(
            self.match_element_fixed_dme.get_path(),
            {self.fixed_dme.fixed_data: None}, None)
        self.assertTrue(
            not match_value_filter.receive_atom(log_atom_fixed_dme))

    def test9match_value_filter_receive_atom_target_value_not_found(self):
        """No target_value was found in the dictionary."""
        description = "Test9AtomFilters"
        new_match_path_detector = NewMatchPathDetector(
            self.aminer_config, [self.stream_printer_event_handler], 'Default',
            False)
        self.analysis_context.register_component(new_match_path_detector,
                                                 description)
        t = time.time()

        log_atom_fixed_dme = LogAtom(b'24999',
                                     ParserMatch(self.match_element_fixed_dme),
                                     t, new_match_path_detector)
        match_value_filter = MatchValueFilter(
            self.match_element_fixed_dme.get_path(),
            {self.fixed_dme.fixed_data: None}, None)
        self.assertTrue(
            not match_value_filter.receive_atom(log_atom_fixed_dme))
    def test1log_multiple_lines_event(self):
        """
        In this test case multiple lines should be received, before sending an email to root@localhost.
        Make sure no mail notifications are in /var/spool/mail/root, before running this test. This test case must wait some time to
        ensure, that the mail can be read.
        """
        description = "Test1DefaultMailNotificationEventHandler"
        match_context = MatchContext(self.pid)
        fixed_dme = FixedDataModelElement('s1', self.pid)
        match_element = fixed_dme.get_match_element("match", match_context)

        match_context = MatchContext(self.pid)
        fixed_dme2 = FixedDataModelElement('s2', self.pid)
        match_element2 = fixed_dme2.get_match_element("match", match_context)

        default_mail_notification_event_handler = DefaultMailNotificationEventHandler(
            self.analysis_context)
        self.analysis_context.register_component(self, description)

        t = time()
        log_atom = LogAtom(fixed_dme.fixed_data, ParserMatch(match_element), t,
                           self)
        default_mail_notification_event_handler.receive_event(
            self.test % self.__class__.__name__,
            'New value for pathes %s, %s: %s' %
            ('match/s1', 'match/s2', repr(match_element.match_object)),
            [log_atom.raw_data, log_atom.raw_data], None, log_atom, self)

        t += 600
        log_atom = LogAtom(fixed_dme.fixed_data, ParserMatch(match_element), t,
                           self)
        # set the next_alert_time instead of sleeping 10 seconds
        default_mail_notification_event_handler.next_alert_time = time()
        default_mail_notification_event_handler.receive_event(
            self.test % self.__class__.__name__,
            'New value for pathes %s, %s: %s' %
            ('match/s1', 'match/s2', repr(match_element.match_object)),
            [log_atom.raw_data, log_atom.raw_data], None, log_atom, self)
        sleep(2)
        # skipcq: PYL-W1510, BAN-B602
        result = subprocess.run(self.mail_call,
                                shell=True,
                                stdout=subprocess.PIPE)
        # skipcq: PYL-W1510, BAN-B602
        subprocess.run(self.mail_delete_call,
                       shell=True,
                       stdout=subprocess.PIPE)

        if datetime.fromtimestamp(t - 600).strftime(
                self.datetime_format_string) not in str(
                    result.stdout, 'utf-8'):
            print("ERROR: %s t-600 not found in mail!" % description,
                  file=sys.stderr)
        if datetime.fromtimestamp(t).strftime(
                self.datetime_format_string) not in str(
                    result.stdout, 'utf-8'):
            print("ERROR: %s t not found in mail!" % description,
                  file=sys.stderr)

        self.assertTrue(
            self.__expected_string %
            ("" + match_element.get_path() + ", " + match_element2.get_path(),
             match_element.get_match_object(), self.__class__.__name__,
             description, 2, match_element.get_match_string().decode() +
             "\n  " + match_element2.get_match_string().decode())
            in str(result.stdout, 'utf-8'),
            msg="%s vs \n %s" %
            (self.__expected_string %
             (match_element.get_path(), match_element.get_match_object(),
              self.__class__.__name__, description, 1,
              match_element.get_match_string().decode() + "\n\n"),
             str(result.stdout, 'utf-8')))

        self.assertTrue(
            self.__expected_string %
            ("" + match_element.get_path() + ", " + match_element2.get_path(),
             match_element.get_match_object(), self.__class__.__name__,
             description, 2, match_element.get_match_string().decode() +
             "\n  " + match_element2.get_match_string().decode() +
             "\n\n") in str(result.stdout, 'utf-8'))
Exemple #28
0
class KafkaEventHandlerTest(TestBase):
    """Unittests for the KafkaEventHandler."""

    output_log_line = True
    kafka_topic = 'test_topic'
    kafka_group = 'test_group'
    consumer = None
    match_context = MatchContext(b' pid=')
    fixed_dme = FixedDataModelElement('s1', b' pid=')
    other_data = 4
    match_element = fixed_dme.get_match_element("match", match_context)
    description = 'jsonConverterHandlerDescription'
    t = time.time()
    persistence_id = 'Default'
    test_detector = 'Analysis.TestDetector'
    event_message = 'An event happened!'
    sorted_log_lines = ['Event happend at /path/ 5 times.', '', '', '', '']
    expected_string = '{\n  "AnalysisComponent": {\n    "AnalysisComponentIdentifier": 0,\n' \
                      '    "AnalysisComponentType": "%s",\n    "AnalysisComponentName": "%s",\n    "Message": "%s",\n' \
                      '    "PersistenceFileName": "%s",\n    "AffectedParserPaths": [\n      "test/path/1",\n' \
                      '      "test/path/2"\n    ]\n  },\n  "LogData": {\n    "RawLogData": [\n      " pid="\n    ],\n    ' \
                      '"Timestamps": [\n      %s\n    ],\n    "DetectionTimestamp": %s,\n    "LogLinesCount": 5,\n' \
                      '    "AnnotatedMatchElement": {\n      "match/s1": " pid="\n    }\n  }%s\n}\n'

    @classmethod
    def setUpClass(cls):
        """Start a KafkaConsumer."""
        cls.consumer = KafkaConsumer(cls.kafka_topic,
                                     bootstrap_servers=['localhost:9092'],
                                     enable_auto_commit=True,
                                     consumer_timeout_ms=10000,
                                     group_id=cls.kafka_group,
                                     value_deserializer=lambda x: x.decode(),
                                     api_version=(2, 0, 1),
                                     auto_offset_reset='earliest')

    @classmethod
    def tearDownClass(cls):
        """Shutdown the KafkaConsumer."""
        cls.consumer.close()

    def test1receive_serialized_data(self):
        """This unittest tests the receive_event method with serialized data from the JsonConverterHandler."""
        json_converter_handler = JsonConverterHandler(
            [self.stream_printer_event_handler], self.analysis_context)
        log_atom = LogAtom(self.fixed_dme.fixed_data,
                           ParserMatch(self.match_element), self.t, self)
        self.analysis_context.register_component(self, self.description)
        event_data = {
            'AnalysisComponent': {
                'AffectedParserPaths': ['test/path/1', 'test/path/2']
            }
        }
        json_converter_handler.receive_event(self.test_detector,
                                             self.event_message,
                                             self.sorted_log_lines, event_data,
                                             log_atom, self)
        output = self.output_stream.getvalue()
        kafka_event_handler = KafkaEventHandler(
            self.analysis_context, self.kafka_topic, {
                'bootstrap_servers': ['localhost:9092'],
                'api_version': (2, 0, 1)
            })
        self.assertTrue(
            kafka_event_handler.receive_event(self.test_detector,
                                              self.event_message,
                                              self.sorted_log_lines, output,
                                              log_atom, self))
        val = self.consumer.__next__().value
        detection_timestamp = None
        for line in val.split('\n'):
            if "DetectionTimestamp" in line:
                detection_timestamp = line.split(':')[1].strip(' ,')
        self.assertEqual(
            val, self.expected_string %
            (self.__class__.__name__, self.description, self.event_message,
             self.persistence_id, round(self.t, 2), detection_timestamp, ""))

    def test2receive_non_serialized_data(self):
        """This unittest tests the receive_event method with not serialized data."""
        log_atom = LogAtom(self.fixed_dme.fixed_data,
                           ParserMatch(self.match_element), self.t, self)
        self.analysis_context.register_component(self, self.description)
        event_data = {
            'AnalysisComponent': {
                'AffectedParserPaths': ['test/path/1', 'test/path/2']
            }
        }
        kafka_event_handler = KafkaEventHandler(
            self.analysis_context, self.kafka_topic, {
                'bootstrap_servers': ['localhost:9092'],
                'api_version': (2, 0, 1)
            })
        self.assertFalse(
            kafka_event_handler.receive_event(self.test_detector,
                                              self.event_message,
                                              self.sorted_log_lines,
                                              event_data, log_atom, self))
        self.assertRaises(StopIteration, self.consumer.__next__)
    def test1update_successful(self):
        """Update the MatchContext and DebugMatchContext with allowed values."""
        data = b"this is an example of a log line."
        match_context = MatchContext(data)
        match_context.update(b"this is an example")
        self.assertEqual(match_context.match_data, b" of a log line.")

        match_context = MatchContext(data)
        match_context.update([b"t", b"h", b"i", b"s"])
        self.assertEqual(match_context.match_data,
                         b" is an example of a log line.")

        match_context = MatchContext(data)
        match_context.update(b"some other text")
        self.assertEqual(match_context.match_data, b"ple of a log line.")

        match_context = DebugMatchContext(data)
        match_context.update(b"this is an example ")
        self.assertEqual(match_context.match_data, b"of a log line.")
        self.assertEqual(
            match_context.get_debug_info(),
            'Starting match update on "this is an example of a log line."\n  Removed: "this is an example ", remaining 14'
            ' bytes\n  Shortest unmatched data: "of a log line."\n')
        self.assertEqual(match_context.get_debug_info(),
                         '  Shortest unmatched data: "of a log line."\n')
        match_context.update(b"of")
        self.assertEqual(
            match_context.get_debug_info(),
            '  Removed: "of", remaining 12 bytes\n  Shortest unmatched data: " a log line."\n'
        )
        match_context.update(b" a log line.")
        self.assertEqual(
            match_context.get_debug_info(),
            '  Removed: " a log line.", remaining 0 bytes\n  Shortest unmatched data: ""\n'
        )
        self.assertRaises(ValueError, match_context.update, b" a log line.")
        self.assertEqual(
            match_context.get_debug_info(),
            '  Current data  does not start with " a log line."\n  Shortest unmatched data: ""\n'
        )
        match_context.update(b"")
 def parse_json_object(self, json_dict, json_match_data, key, split_key,
                       current_path, match_context):  # skipcq: PYL-R0201
     """Parse a literal from the json object."""
     data = json_match_data[split_key]
     enc = "utf-8"
     if isinstance(data, str):
         if self.is_escaped_unicode(data) and self.dec_escapes:
             enc = "unicode-escape"
         data = data.encode(enc)
     elif isinstance(data, bool):
         data = str(data).replace("T", "t").replace("F", "f").encode()
     elif data is None:
         data = b"null"
         if self.is_nullable_key(key) or json_dict[key] == "NULL_OBJECT":
             start = 0
             if "null" in key:
                 start = match_context.match_data.find(data) + 4
             index = match_context.match_data.find(data, start)
             if match_context.match_data[index + 4] == 34:
                 index += 1
             return MatchElement(current_path, data, data,
                                 None), index, data
         return None, -1, data
     elif not isinstance(data, bytes):
         data = str(data).encode()
     if json_dict[key] == "ALLOW_ALL":
         logging.getLogger(DEBUG_LOG_NAME).debug(debug_log_prefix +
                                                 "ALLOW_ALL (DICT)\n" +
                                                 data.decode())
         match_element = MatchElement(current_path, data, data, None)
         last_bracket = match_context.match_data.find(b"}", len(data))
         while match_context.match_data.count(
                 b"{", 0, last_bracket) - match_context.match_data.count(
                     b"}", 0, last_bracket) > 0:
             last_bracket = match_context.match_data.find(
                 b"}", last_bracket) + 1
         index = last_bracket - len(data)
     elif json_dict[key] == "EMPTY_STRING":
         if data == b"":
             match_element = MatchElement(current_path, data, data, None)
             index = match_context.match_data.find(
                 split_key.encode()) + len(split_key)
             index += match_context.match_data[index:].find(b'""') + len(
                 b'""')
         else:
             match_element = None
             index = -1
     else:
         match_element = json_dict[key].get_match_element(
             current_path, MatchContext(data))
         if match_element is not None and len(
                 match_element.match_string) != len(data) and (
                     not isinstance(match_element.match_object, bytes)
                     or len(match_element.match_object) != len(data)):
             logging.getLogger(DEBUG_LOG_NAME).debug(
                 debug_log_prefix +
                 "Data length not matching! match_string: %d, data: %d, data: %s"
                 % (len(match_element.match_string), len(data),
                    data.decode()))
             match_element = None
         index = max([
             match_context.match_data.replace(b"\\",
                                              b"").find(split_key.encode()),
             match_context.match_data.find(split_key.encode()),
             match_context.match_data.decode().find(split_key)
         ])
         index += match_context.match_data[index:].find(
             split_key.encode() + b'":') + len(split_key.encode() + b'":')
         try:
             index += max([
                 match_context.match_data.replace(b"\\",
                                                  b"")[index:].find(data),
                 match_context.match_data[index:].find(data),
                 match_context.match_data.decode(enc)[index:].find(
                     data.decode(enc))
             ])
         except UnicodeDecodeError:
             index += max([
                 match_context.match_data.replace(b"\\",
                                                  b"")[index:].find(data),
                 match_context.match_data[index:].find(data),
                 match_context.match_data.decode()[index:].find(
                     data.decode())
             ])
         index += len(match_context.match_data[index:]) - len(
             match_context.match_data[index:].lstrip(b" \r\t\n"))
         if match_context.match_data[index:].find(b'"') == 0:
             index += len(b'"')
         # for example float scientific representation is converted to normal float..
         if index == -1 and match_element is not None and isinstance(
                 json_match_data[split_key], float):
             indices = [
                 match_context.match_data.find(
                     b",",
                     len(match_element.match_string) // 3),
                 match_context.match_data.find(b"]"),
                 match_context.match_data.find(b"}")
             ]
             indices = [x for x in indices if x >= 0]
             index = min(indices)
         if match_element is None:
             index = -1
     return match_element, index, data