def test_udm_method_cannot_mutate_event(self) -> None:
     event = {'src_ip': '', 'extra': {'t': 10}, 'dst': {'ip': '1.2.3.4'}}
     event_copy = deepcopy(event)
     data_model = DataModel({
         'body':
         'def get_source_ip(event):'
         '\n\tif event["src_ip"] == "":'
         '\n\t\tevent["src_ip"] = None'
         '\n\tif event["extra"]["t"] == 10:'
         '\n\t\tevent["extra"]["t"] = 11'
         '\n\treturn (event["src_ip"], event["extra"]["t"])',
         'versionId':
         'version',
         'mappings': [{
             'name': 'destination_ip',
             'path': '$.dst.*'
         }, {
             'name': 'source_ip',
             'method': 'get_source_ip'
         }],
         'id':
         'data_model_id'
     })
     enriched_event = PantherEvent(event, data_model)
     with self.assertRaises(TypeError):
         enriched_event.udm('source_ip')
     self.assertEqual(event_copy, event)
 def test_udm_complex_json_path(self) -> None:
     event = {
         'events': [{
             'parameters': [{
                 'name': 'USER_EMAIL',
                 'value': '*****@*****.**'
             }]
         }]
     }
     data_model = DataModel({
         'body':
         'def get_source_ip(event):\n\treturn "1.2.3.4"',
         'versionId':
         'version',
         'mappings': [{
             'name':
             'email',
             'path':
             '$.events[*].parameters[?(@.name == "USER_EMAIL")].value'
         }, {
             'name': 'source_ip',
             'method': 'get_source_ip'
         }],
         'id':
         'data_model_id'
     })
     enriched_event = PantherEvent(event, data_model)
     self.assertEqual(enriched_event.udm('email'), '*****@*****.**')
 def test_alert_context_too_big(self) -> None:
     # Function should generate alert_context exceeding limit
     alert_context_function = 'def alert_context(event):\n' \
                              '\ttest_dict = {}\n' \
                              '\tfor i in range(300000):\n' \
                              '\t\ttest_dict[str(i)] = "value"\n' \
                              '\treturn test_dict'
     rule_body = 'def rule(event):\n\treturn True\n{}'.format(
         alert_context_function)
     rule = Rule({
         'id': 'test_alert_context_too_big',
         'body': rule_body,
         'versionId': 'versionId',
         'severity': 'INFO'
     })
     expected_alert_context = json.dumps({
         '_error':
         'alert_context size is [5588890] characters, bigger than maximum of [204800] characters'
     })
     expected_result = RuleResult(
         rule_id='test_alert_context_too_big',
         matched=True,
         dedup_output='defaultDedupString:test_alert_context_too_big',
         alert_context=expected_alert_context,
         rule_severity='INFO')
     self.assertEqual(expected_result,
                      rule.run(PantherEvent({}, None), {}, {}))
    def test_rule_with_all_generated_fields(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\n' \
                    'def alert_context(event):\n\treturn {}\n' \
                    'def title(event):\n\treturn "test_rule_with_all_generated_fields"\n' \
                    'def description(event):\n\treturn "test description"\n' \
                    'def severity(event):\n\treturn "HIGH"\n' \
                    'def reference(event):\n\treturn "test reference"\n' \
                    'def runbook(event):\n\treturn "test runbook"\n' \
                    'def destinations(event):\n\treturn []'
        rule = Rule({
            'id': 'test_rule_with_all_generated_fields',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_rule_with_all_generated_fields',
            matched=True,
            alert_context='{}',
            title_output='test_rule_with_all_generated_fields',
            dedup_output='test_rule_with_all_generated_fields',
            description_output='test description',
            severity_output='HIGH',
            reference_output='test reference',
            runbook_output='test runbook',
            destinations_output=["SKIP"],
            rule_severity='INFO',
        )
        self.assertEqual(
            expected_result,
            rule.run(PantherEvent({}, None), {}, {}, batch_mode=False))
    def test_rule_with_invalid_severity(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\n' \
                    'def alert_context(event):\n\treturn {}\n' \
                    'def title(event):\n\treturn "test_rule_with_invalid_severity"\n' \
                    'def severity(event):\n\treturn "CRITICAL-ISH"\n'
        rule = Rule({
            'id': 'test_rule_with_invalid_severity',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_rule_with_invalid_severity',
            matched=True,
            alert_context='{}',
            title_output='test_rule_with_invalid_severity',
            dedup_output='test_rule_with_invalid_severity',
            severity_exception=AssertionError(
                "Expected severity to be any of the following: [['INFO', 'LOW', 'MEDIUM', 'HIGH', "
                "'CRITICAL']], got [CRITICAL-ISH] instead."),
            rule_severity='INFO',
        )
        result = rule.run(PantherEvent({}, None), {}, {}, batch_mode=False)
        self.assertEqual(str(expected_result), str(result))
        self.assertTrue(result.errored)
        self.assertIsNotNone(result.severity_exception)
    def test_rule_with_invalid_destinations_type(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\n' \
                    'def alert_context(event):\n\treturn {}\n' \
                    'def title(event):\n\treturn "test_rule_with_valid_severity_case_insensitive"\n' \
                    'def severity(event):\n\treturn "cRiTiCaL"\n' \
                    'def destinations(event):\n\treturn "bad input"\n'
        rule = Rule({
            'id': 'test_rule_with_valid_severity_case_insensitive',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_rule_with_valid_severity_case_insensitive',
            matched=True,
            alert_context='{}',
            title_output='test_rule_with_valid_severity_case_insensitive',
            dedup_output='test_rule_with_valid_severity_case_insensitive',
            severity_output="CRITICAL",
            destinations_output=None,
            destinations_exception=Exception(
                'rule [{}] function [{}] returned [{}], expected a list'.
                format(rule.rule_id, 'destinations', 'str')),
            rule_severity='INFO')
        result = rule.run(PantherEvent({}, None), {}, {}, batch_mode=False)
        self.assertEqual(str(expected_result), str(result))
        self.assertTrue(result.errored)
        self.assertIsNotNone(result.destinations_exception)
 def test_rule_with_severity_raising_exception_unit_test(self) -> None:
     rule_body = 'def rule(event):\n\treturn True\n' \
                 'def title(event):\n\treturn"test_rule_with_severity_raising_exception_unit_test"\n' \
                 'def severity(event):\n\traise AssertionError("something bad happened")\n'
     rule = Rule({
         'id': 'test_rule_with_severity_raising_exception_unit_test',
         'body': rule_body,
         'versionId': 'versionId',
         'severity': 'INFO'
     })
     expected_result = RuleResult(
         rule_id='test_rule_with_severity_raising_exception_unit_test',
         matched=True,
         title_output='test_rule_with_severity_raising_exception_unit_test',
         dedup_output='test_rule_with_severity_raising_exception_unit_test',
         severity_output=None,
         severity_exception=AssertionError("something bad happened"),
         rule_severity='INFO',
     )
     result = rule.run(PantherEvent({}, None), {}, {}, batch_mode=False)
     self.assertTrue(result.errored)
     self.assertIsNotNone(result.severity_exception)
     # Exception instances cannot be compared
     self.assertDataclassEqual(expected_result,
                               result,
                               fields_as_string=('severity_exception', ))
 def test_assignment_not_allowed_on_udm_access(self) -> None:
     event = {
         'dst_ip': '1.1.1.1',
         'dst_port': '2222',
         'extra': {
             'timestamp': 1,
             'array': [1, 2]
         }
     }
     data_model = DataModel({
         'versionId':
         'version',
         'mappings': [{
             'name': 'destination_ip',
             'path': 'dst_ip'
         }, {
             'name': 'extra_fields',
             'path': 'extra'
         }],
         'id':
         'data_model_id'
     })
     enriched_event = PantherEvent(event, data_model)
     self.assertEqual(ImmutableCaseInsensitiveDict(event['extra']),
                      enriched_event.udm('extra_fields'))
     self.assertIsInstance(enriched_event.udm('extra_fields'),
                           ImmutableCaseInsensitiveDict)
     self.assertIsInstance(
         enriched_event.udm('extra_fields')['array'], ImmutableList)
     with self.assertRaises(TypeError):
         enriched_event.udm('extra_fields')['timestamp'] = 10
 def test_udm_method(self) -> None:
     event = {'dst_ip': '1.1.1.1', 'dst_port': '2222'}
     data_model = DataModel({
         'body':
         'def get_source_ip(event):\n\treturn "1.2.3.4"',
         'versionId':
         'version',
         'mappings': [{
             'name': 'destination_ip',
             'path': 'dst_ip'
         }, {
             'name': 'source_ip',
             'method': 'get_source_ip'
         }],
         'id':
         'data_model_id'
     })
     enriched_event = PantherEvent(event, data_model)
     self.assertEqual(enriched_event.udm('source_ip'), '1.2.3.4')
Exemplo n.º 10
0
 def test_rule_throws_exception(self) -> None:
     rule_body = 'def rule(event):\n\traise Exception("test")'
     rule = Rule({
         'id': 'test_rule_throws_exception',
         'body': rule_body,
         'versionId': 'versionId',
         'severity': 'INFO'
     })
     rule_result = rule.run(PantherEvent({}, None), {}, {})
     self.assertIsNone(rule_result.matched)
     self.assertIsNone(rule_result.dedup_output)
     self.assertIsNotNone(rule_result.rule_exception)
Exemplo n.º 11
0
 def test_rule_doesnt_match(self) -> None:
     rule_body = 'def rule(event):\n\treturn False'
     rule = Rule({
         'id': 'test_rule_doesnt_match',
         'body': rule_body,
         'versionId': 'versionId',
         'severity': 'INFO'
     })
     expected_rule = RuleResult(matched=False,
                                rule_id='test_rule_doesnt_match',
                                rule_severity='INFO')
     self.assertEqual(expected_rule, rule.run(PantherEvent({}, None), {},
                                              {}))
Exemplo n.º 12
0
 def test_rule_with_dedup(self) -> None:
     rule_body = 'def rule(event):\n\treturn True\ndef dedup(event):\n\treturn "testdedup"'
     rule = Rule({
         'id': 'test_rule_with_dedup',
         'body': rule_body,
         'versionId': 'versionId',
         'severity': 'INFO'
     })
     expected_rule = RuleResult(rule_id='test_rule_with_dedup',
                                matched=True,
                                dedup_output='testdedup',
                                rule_severity='INFO')
     self.assertEqual(expected_rule, rule.run(PantherEvent({}, None), {},
                                              {}))
Exemplo n.º 13
0
    def test_dedup_exception_batch_mode(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef dedup(event):\n\traise Exception("test")'
        rule = Rule({
            'id': 'test_dedup_throws_exception',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        actual = rule.run(PantherEvent({}, None), {}, {}, batch_mode=False)

        self.assertTrue(actual.matched)
        self.assertIsNotNone(actual.dedup_exception)
        self.assertTrue(actual.errored)
 def test_udm_multiple_matches(self) -> None:
     exception = False
     event = {'dst': {'ip': '1.1.1.1', 'port': '2222'}}
     data_model = DataModel({
         'body':
         'def get_source_ip(event):\n\treturn "1.2.3.4"',
         'versionId':
         'version',
         'mappings': [{
             'name': 'destination_ip',
             'path': '$.dst.*'
         }, {
             'name': 'source_ip',
             'method': 'get_source_ip'
         }],
         'id':
         'data_model_id'
     })
     enriched_event = PantherEvent(event, data_model)
     try:
         enriched_event.udm('destination_ip')
     except Exception:  # pylint: disable=broad-except
         exception = True
     self.assertTrue(exception)
Exemplo n.º 15
0
    def test_rule_invalid_rule_return(self) -> None:
        rule_body = 'def rule(event):\n\treturn "test"'
        rule = Rule({
            'id': 'test_rule_invalid_rule_return',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })
        rule_result = rule.run(PantherEvent({}, None), {}, {})
        self.assertIsNone(rule_result.matched)
        self.assertIsNone(rule_result.dedup_output)
        self.assertTrue(rule_result.errored)

        expected_short_msg = "Exception('rule [test_rule_invalid_rule_return] function [rule] returned [str], expected [bool]')"
        self.assertEqual(expected_short_msg, rule_result.short_error_message)
        self.assertEqual(rule_result.error_type, 'Exception')
Exemplo n.º 16
0
    def test_dedup_throws_exception(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef dedup(event):\n\traise Exception("test")'
        rule = Rule({
            'id': 'test_dedup_throws_exception',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_rule = RuleResult(
            rule_id='test_dedup_throws_exception',
            matched=True,
            dedup_output='defaultDedupString:test_dedup_throws_exception',
            rule_severity='INFO')
        self.assertEqual(expected_rule, rule.run(PantherEvent({}, None), {},
                                                 {}))
Exemplo n.º 17
0
    def test_invalid_python_syntax(self) -> None:
        rule_body = 'def rule(test):this is invalid python syntax'
        rule = Rule({
            'id': 'test_invalid_python_syntax',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })
        rule_result = rule.run(PantherEvent({}, None), {}, {})
        self.assertIsNone(rule_result.matched)
        self.assertIsNone(rule_result.dedup_output)
        self.assertIsNone(rule_result.rule_exception)

        self.assertTrue(rule_result.errored)
        self.assertEqual(rule_result.error_type, "SyntaxError")
        self.assertIsNotNone(rule_result.short_error_message)
        self.assertIsNotNone(rule_result.error_message)
Exemplo n.º 18
0
    def test_rule_invalid_title_return(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef title(event):\n\treturn {}'
        rule = Rule({
            'id': 'test_rule_invalid_title_return',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_rule_invalid_title_return',
            matched=True,
            dedup_output='test_rule_invalid_title_return',
            title_output='test_rule_invalid_title_return',
            rule_severity='INFO')
        self.assertEqual(rule.run(PantherEvent({}, None), {}, {}),
                         expected_result)
Exemplo n.º 19
0
    def test_rule_dedup_returns_empty_string(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef dedup(event):\n\treturn ""'
        rule = Rule({
            'id': 'test_rule_dedup_returns_empty_string',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_rule_dedup_returns_empty_string',
            matched=True,
            dedup_output=
            'defaultDedupString:test_rule_dedup_returns_empty_string',
            rule_severity='INFO')
        self.assertEqual(rule.run(PantherEvent({}, None), {}, {}),
                         expected_result)
Exemplo n.º 20
0
    def test_alert_context(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef alert_context(event):\n\treturn {"string": "string", "int": 1, "nested": {}}'
        rule = Rule({
            'id': 'test_alert_context',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_alert_context',
            matched=True,
            dedup_output='defaultDedupString:test_alert_context',
            alert_context='{"string": "string", "int": 1, "nested": {}}',
            rule_severity='INFO',
        )
        self.assertEqual(expected_result,
                         rule.run(PantherEvent({}, None), {}, {}))
Exemplo n.º 21
0
    def test_restrict_dedup_size(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef dedup(event):\n\treturn "".join("a" for i in range({}))'. \
            format(MAX_DEDUP_STRING_SIZE + 1)
        rule = Rule({
            'id': 'test_restrict_dedup_size',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_dedup_string_prefix = ''.join(
            'a' for _ in range(MAX_DEDUP_STRING_SIZE -
                               len(TRUNCATED_STRING_SUFFIX)))
        expected_rule = RuleResult(rule_id='test_restrict_dedup_size',
                                   matched=True,
                                   dedup_output=expected_dedup_string_prefix +
                                   TRUNCATED_STRING_SUFFIX,
                                   rule_severity='INFO')
        self.assertEqual(expected_rule, rule.run(PantherEvent({}, None), {},
                                                 {}))
Exemplo n.º 22
0
    def test_rule_with_severity_raising_exception_batch_mode(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\n' \
                    'def title(event):\n\treturn"test_rule_with_severity_raising_exception_batch_mode"\n' \
                    'def severity(event):\n\traise AssertionError("something bad happened")\n'
        rule = Rule({
            'id': 'test_rule_with_severity_raising_exception_batch_mode',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            rule_id='test_rule_with_severity_raising_exception_batch_mode',
            matched=True,
            title_output='test_rule_with_severity_raising_exception_batch_mode',
            dedup_output='test_rule_with_severity_raising_exception_batch_mode',
            severity_output='INFO',
            rule_severity='INFO',
        )
        result = rule.run(PantherEvent({}, None), {}, {}, batch_mode=True)
        self.assertEqual(str(expected_result), str(result))
Exemplo n.º 23
0
    def test_rule_with_valid_severity_case_insensitive(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\n' \
                    'def alert_context(event):\n\treturn {}\n' \
                    'def title(event):\n\treturn "test_rule_with_valid_severity_case_insensitive"\n' \
                    'def severity(event):\n\treturn "cRiTiCaL"\n'
        rule = Rule({
            'id': 'test_rule_with_valid_severity_case_insensitive',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_result = RuleResult(
            matched=True,
            rule_id='test_rule_with_valid_severity_case_insensitive',
            alert_context='{}',
            title_output='test_rule_with_valid_severity_case_insensitive',
            dedup_output='test_rule_with_valid_severity_case_insensitive',
            severity_output="CRITICAL",
            rule_severity='INFO')
        result = rule.run(PantherEvent({}, None), {}, {})
        self.assertEqual(expected_result, result)
Exemplo n.º 24
0
    def test_alert_context_returns_full_event(self) -> None:
        alert_context_function = 'def alert_context(event):\n\treturn event'
        rule_body = 'def rule(event):\n\treturn True\n{}'.format(
            alert_context_function)
        rule = Rule({
            'id': 'test_alert_context_returns_full_event',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })
        event = {'test': 'event'}

        expected_alert_context = json.dumps(event)
        expected_result = RuleResult(
            rule_id='test_alert_context_returns_full_event',
            matched=True,
            dedup_output=
            'defaultDedupString:test_alert_context_returns_full_event',
            alert_context=expected_alert_context,
            rule_severity='INFO')
        self.assertEqual(expected_result,
                         rule.run(PantherEvent(event, None), {}, {}))
Exemplo n.º 25
0
    def test_alert_context_immutable_event(self) -> None:
        alert_context_function = 'def alert_context(event):\n' \
                                 '\treturn {"headers": event["headers"],\n' \
                                 '\t\t"get_params": event["query_string_args"]}'
        rule_body = 'def rule(event):\n\treturn True\n{}'.format(
            alert_context_function)
        rule = Rule({
            'id': 'test_alert_context_immutable_event',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })
        event = {
            'headers': {
                'User-Agent': 'Chrome'
            },
            'query_string_args': [{
                'a': '1'
            }, {
                'b': '2'
            }]
        }

        expected_alert_context = json.dumps({
            'headers':
            event['headers'],
            'get_params':
            event['query_string_args']
        })
        expected_result = RuleResult(
            rule_id='test_alert_context_immutable_event',
            matched=True,
            dedup_output=
            'defaultDedupString:test_alert_context_immutable_event',
            alert_context=expected_alert_context,
            rule_severity='INFO',
        )
        self.assertEqual(expected_result,
                         rule.run(PantherEvent(event, None), {}, {}))
 def test_assignment_not_allowed_on_getitem_access(self) -> None:
     # No DataModel given
     event = {
         'dst': {
             'ip': '1.1.1.1',
             'port': '2222'
         },
         'extra': [{
             't': 10
         }]
     }
     enriched_event = PantherEvent(event, None)
     with self.assertRaises(TypeError):
         # pylint: disable=E1137
         enriched_event['dst'] = 1  # type: ignore
     self.assertIsInstance(enriched_event['dst'],
                           ImmutableCaseInsensitiveDict)
     with self.assertRaises(TypeError):
         # pylint: disable=E1137
         enriched_event['dst']['ip'] = 1
     self.assertIsInstance(enriched_event['extra'], ImmutableList)
     self.assertIsInstance(enriched_event['extra'][0],
                           ImmutableCaseInsensitiveDict)
Exemplo n.º 27
0
    def test_rule_matches(self) -> None:
        rule_body = 'def rule(event):\n\treturn True'
        rule = Rule({
            'id': 'test_rule_matches',
            'body': rule_body,
            'dedupPeriodMinutes': 100,
            'versionId': 'test',
            'severity': 'INFO'
        })

        self.assertEqual('test_rule_matches', rule.rule_id)
        self.assertEqual(rule_body, rule.rule_body)
        self.assertEqual('test', rule.rule_version)
        self.assertEqual(100, rule.rule_dedup_period_mins)

        expected_rule = RuleResult(
            rule_id='test_rule_matches',
            matched=True,
            dedup_output='defaultDedupString:test_rule_matches',
            rule_severity='INFO',
        )
        self.assertEqual(expected_rule, rule.run(PantherEvent({}, None), {},
                                                 {}))
Exemplo n.º 28
0
    def test_alert_context_invalid_return_value(self) -> None:
        rule_body = 'def rule(event):\n\treturn True\ndef alert_context(event):\n\treturn ""'
        rule = Rule({
            'id': 'test_alert_context_invalid_return_value',
            'body': rule_body,
            'versionId': 'versionId',
            'severity': 'INFO'
        })

        expected_alert_context = json.dumps({
            '_error':
            'Exception(\'rule [test_alert_context_invalid_return_value] function [alert_context] returned [str], expected [Mapping]\')'  # pylint: disable=C0301
        })
        expected_result = RuleResult(
            rule_id='test_alert_context_invalid_return_value',
            matched=True,
            dedup_output=
            'defaultDedupString:test_alert_context_invalid_return_value',
            alert_context=expected_alert_context,
            rule_severity='INFO',
        )
        self.assertEqual(expected_result,
                         rule.run(PantherEvent({}, None), {}, {}))
 def test_nested_list_immutability(self) -> None:
     event = {'headers': [{'User-Agent': 'Chrome', 'Host': 'google.com'}]}
     enriched_event = PantherEvent(event, None)
     self.assertIsInstance(enriched_event['headers'], ImmutableList)
     self.assertIsInstance(enriched_event['headers'][0],
                           ImmutableCaseInsensitiveDict)