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_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_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_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_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_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)
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), {}, {}))
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_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), {}, {}))
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), {}, {}))
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')
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)
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)
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)
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), {}, {}))
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), {}, {}))
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))
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)
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_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), {}, {}))
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), {}, {}))
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), {}, {}))