From 2e36ac318a7d8782e58827e9afbfb00d593c5011 Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Fri, 16 Mar 2018 09:13:19 -0700 Subject: [PATCH 01/14] [cli] yapf the helpers file --- stream_alert_cli/helpers.py | 134 ++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/stream_alert_cli/helpers.py b/stream_alert_cli/helpers.py index 1a13a9148..89065d8f2 100644 --- a/stream_alert_cli/helpers.py +++ b/stream_alert_cli/helpers.py @@ -27,16 +27,8 @@ import boto3 from botocore.exceptions import ClientError -from moto import ( - mock_cloudwatch, - mock_dynamodb2, - mock_kinesis, - mock_kms, - mock_lambda, - mock_s3, - mock_sns, - mock_sqs -) +from moto import (mock_cloudwatch, mock_dynamodb2, mock_kinesis, mock_kms, mock_lambda, mock_s3, + mock_sns, mock_sqs) from stream_alert_cli.logger import LOGGER_CLI from stream_alert.rule_processor.firehose import StreamAlertFirehose @@ -53,9 +45,7 @@ def run_command(runner_args, **kwargs): quiet (bool): Whether to show command output or hide it """ - default_error_message = "An error occurred while running: {}".format( - ' '.join(runner_args) - ) + default_error_message = "An error occurred while running: {}".format(' '.join(runner_args)) error_message = kwargs.get('error_message', default_error_message) default_cwd = 'terraform' cwd = kwargs.get('cwd', default_cwd) @@ -123,8 +113,10 @@ def tf_runner(action='apply', refresh=True, auto_approve=False, targets=None): if not run_command(['terraform', 'get'], quiet=True): return False - tf_command = ['terraform', action, '-var-file=../conf/lambda.json', - '-refresh={}'.format(str(refresh).lower())] + tf_command = [ + 'terraform', action, '-var-file=../conf/lambda.json', '-refresh={}'.format( + str(refresh).lower()) + ] if action == 'destroy': # Terraform destroy has a '-force' flag instead of '-auto-approve' @@ -253,10 +245,7 @@ def _get_record_template(service): } elif service == 'stream_alert_app': - return { - 'stream_alert_app': '', - 'logs': [''] - } + return {'stream_alert_app': '', 'logs': ['']} else: LOGGER_CLI.error('Unsupported service: %s', service) @@ -340,26 +329,25 @@ def create_lambda_function(function_name, region): if function_name.find(':') != -1: function_name = function_name.split(':')[0] - boto3.client('lambda', region_name=region).create_function( - FunctionName=function_name, - Runtime='python2.7', - Role='test-iam-role', - Handler='function.handler', - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - Code={ - 'ZipFile': _make_lambda_package() - } - ) + boto3.client( + 'lambda', region_name=region).create_function( + FunctionName=function_name, + Runtime='python2.7', + Role='test-iam-role', + Handler='function.handler', + Description='test lambda function', + Timeout=3, + MemorySize=128, + Publish=True, + Code={ + 'ZipFile': _make_lambda_package() + }) def encrypt_with_kms(data, region, alias): """Encrypt the given data with KMS.""" kms_client = boto3.client('kms', region_name=region) - response = kms_client.encrypt(KeyId=alias, - Plaintext=data) + response = kms_client.encrypt(KeyId=alias, Plaintext=data) return response['CiphertextBlob'] @@ -407,8 +395,7 @@ def create_delivery_stream(region, stream_name, prefix=''): 'IntervalInSeconds': 124 }, 'CompressionFormat': 'Snappy', - } - ) + }) @mock_kinesis @@ -462,31 +449,44 @@ def setup_mock_dynamodb_ioc_table(config): ) dynamodb_client.put_item( - Item={ - 'ioc_value': {'S': '1.1.1.2'}, - 'ioc_type': {'S': 'ip'}, - 'sub_type': {'S': 'mal_ip'} + Item={'ioc_value': { + 'S': '1.1.1.2' }, - TableName=table_name - ) + 'ioc_type': { + 'S': 'ip' + }, + 'sub_type': { + 'S': 'mal_ip' + }}, + TableName=table_name) dynamodb_client.put_item( Item={ - 'ioc_value': {'S': '0123456789abcdef0123456789abcdef'}, - 'ioc_type': {'S': 'md5'}, - 'sub_type': {'S': 'mal_md5'} + 'ioc_value': { + 'S': '0123456789abcdef0123456789abcdef' + }, + 'ioc_type': { + 'S': 'md5' + }, + 'sub_type': { + 'S': 'mal_md5' + } }, - TableName=table_name - ) + TableName=table_name) dynamodb_client.put_item( Item={ - 'ioc_value': {'S': 'evil.com'}, - 'ioc_type': {'S': 'domain'}, - 'sub_type': {'S': 'c2_domain'} + 'ioc_value': { + 'S': 'evil.com' + }, + 'ioc_type': { + 'S': 'domain' + }, + 'sub_type': { + 'S': 'c2_domain' + } }, - TableName=table_name - ) + TableName=table_name) def put_mock_s3_object(bucket, key, data, region): @@ -505,12 +505,7 @@ def put_mock_s3_object(bucket, key, data, region): except ClientError: s3_client.create_bucket(Bucket=bucket) - s3_client.put_object( - Body=data, - Bucket=bucket, - Key=key, - ServerSideEncryption='AES256' - ) + s3_client.put_object(Body=data, Bucket=bucket, Key=key, ServerSideEncryption='AES256') def mock_me(context): @@ -520,9 +515,11 @@ def mock_me(context): Args: context (namedtuple): A constructed aws context object """ + def wrap(func): """Wrap the returned function with or without mocks""" if context.mocked: + @mock_cloudwatch @mock_kinesis @mock_kms @@ -535,11 +532,13 @@ def mocked(options, context): override any boto3 calls. Wrapping this function here allows us to mock out all calls that happen below this scope.""" return func(options, context) + return mocked def unmocked(options, context): """This function will remain unmocked and operate normally""" return func(options, context) + return unmocked return wrap @@ -554,9 +553,7 @@ def get_context_from_config(cluster, config): includes cluster info, etc that can be used for constructing an aws context object """ - context = namedtuple('aws_context', ['invoked_function_arn', - 'function_name' - 'mocked']) + context = namedtuple('aws_context', ['invoked_function_arn', 'function_name' 'mocked']) # Return a mocked context if the cluster is not provided # Otherwise construct the context from the config using the cluster @@ -572,8 +569,7 @@ def get_context_from_config(cluster, config): account = config['global']['account']['aws_account_id'] region = config['global']['account']['region'] function_name = '{}_streamalert_alert_processor'.format(prefix) - arn = 'arn:aws:lambda:{}:{}:function:{}:testing'.format( - region, account, function_name) + arn = 'arn:aws:lambda:{}:{}:function:{}:testing'.format(region, account, function_name) context.invoked_function_arn = arn context.function_name = function_name @@ -618,8 +614,8 @@ def user_input(requested_info, mask, input_restrictions): else: valid_response = not any(x in input_restrictions for x in response) if not valid_response: - restrictions = ', '.join('\'{}\''.format(restriction) - for restriction in input_restrictions) + restrictions = ', '.join( + '\'{}\''.format(restriction) for restriction in input_restrictions) LOGGER_CLI.error('The supplied input should not contain any of the following: %s', restrictions) @@ -694,6 +690,8 @@ def get_rule_test_files(test_files_dir): dict: Information about test files on disk, where the key is the base name of the file and the value is the relative path to the file """ - return {os.path.splitext(event_file)[0]: os.path.join(root, event_file) - for root, _, test_event_files in os.walk(test_files_dir) - for event_file in test_event_files} + return { + os.path.splitext(event_file)[0]: os.path.join(root, event_file) + for root, _, test_event_files in os.walk(test_files_dir) + for event_file in test_event_files + } From 3d42c049bb9057aa70bc369b5019f08e3be6e5ff Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Fri, 16 Mar 2018 14:46:36 -0700 Subject: [PATCH 02/14] [rule tests] support override_records in rule tests * Move default value generation into Parser Base to be accessible in all classes/publicly * Remove the need for a top level `records` key in test events * Create a test payload which combines default values of the schema with overridden values --- stream_alert/rule_processor/parsers.py | 35 +++++++------- stream_alert_cli/helpers.py | 42 +++++++++++------ stream_alert_cli/test.py | 65 ++++++++++++++++++++++---- 3 files changed, 101 insertions(+), 41 deletions(-) diff --git a/stream_alert/rule_processor/parsers.py b/stream_alert/rule_processor/parsers.py index 6fe050760..4c0c37b2b 100644 --- a/stream_alert/rule_processor/parsers.py +++ b/stream_alert/rule_processor/parsers.py @@ -110,6 +110,21 @@ def matched_log_pattern(self, record, log_patterns): # if all pattern group results are True return all_patterns_result + @staticmethod + def default_optional_values(key): + """Return a default value for a given schema type""" + if key == 'string': + return str() + elif key == 'integer': + return int() + elif key == 'float': + return float() + elif key == 'boolean': + return bool() + elif key == []: + return list() + elif key == OrderedDict(): + return dict() @parser class JSONParser(ParserBase): @@ -158,8 +173,7 @@ def _key_check(self, schema, json_records): return bool(json_records) - @staticmethod - def _add_optional_keys(json_records, schema, optional_keys): + def _add_optional_keys(self, json_records, schema, optional_keys): """Add optional keys to a parsed JSON record. Args: @@ -170,21 +184,6 @@ def _add_optional_keys(json_records, schema, optional_keys): if not optional_keys: return - def _default_optional_values(key): - """Return a default value for a given schema type""" - if key == 'string': - return str() - elif key == 'integer': - return int() - elif key == 'float': - return float() - elif key == 'boolean': - return bool() - elif key == []: - return list() - elif key == OrderedDict(): - return dict() - for key_name in optional_keys: # Instead of doing a schema.update() here with a default value type, # we should enforce having any optional keys declared within the schema @@ -197,7 +196,7 @@ def _default_optional_values(key): for record in json_records: if key_name not in record: # Set default value - record[key_name] = _default_optional_values(schema[key_name]) + record[key_name] = self.default_optional_values(schema[key_name]) @time_me def _parse_records(self, schema, json_payload): diff --git a/stream_alert_cli/helpers.py b/stream_alert_cli/helpers.py index 89065d8f2..6ff35413b 100644 --- a/stream_alert_cli/helpers.py +++ b/stream_alert_cli/helpers.py @@ -631,11 +631,25 @@ def user_input(requested_info, mask, input_restrictions): def load_test_file(path): """Helper to json load the contents of a file with some error handling + Test files can be either formatted as: + + { + "records": [ + {"data": {}, "description": "", ...} + ] + } + + or + + [ + {"data": {}, "description": "", ...} + ] + Args: path (str): Relative path to file on disk Returns: - dict: Loaded JSON from test event file + list: Loaded JSON from test event file """ message_template = 'Improperly formatted file ({}): {}' with open(path, 'r') as test_event_file: @@ -643,16 +657,19 @@ def load_test_file(path): contents = json.load(test_event_file) except (ValueError, TypeError) as err: message = message_template.format(path, err.message) - return False, message - - # Make sure the test event file is formatted in the way we expect - if not (isinstance(contents, dict) and 'records' in contents): - message = message_template.format(path, 'File must be a dict (JSON ' - 'object) with top level key \'records\'') - - return False, message + return [], message + else: + # Check for legacy format, return a list + if 'records' in contents and isinstance(contents['records'], list): + return contents['records'], None + # Expect that the test event is a JSON list + elif isinstance(contents, list): + return contents, None - return contents, None + message = message_template.format( + path, 'Test file must contain either a list of maps, or a list of ' + 'maps preceeded with a `records` key') + return [], message def get_rules_from_test_events(test_files_dir): @@ -671,7 +688,7 @@ def get_rules_from_test_events(test_files_dir): if not events: continue - for test_event in events['records']: + for test_event in events: if 'trigger_rules' not in test_event: continue @@ -692,6 +709,5 @@ def get_rule_test_files(test_files_dir): """ return { os.path.splitext(event_file)[0]: os.path.join(root, event_file) - for root, _, test_event_files in os.walk(test_files_dir) - for event_file in test_event_files + for root, _, test_event_files in os.walk(test_files_dir) for event_file in test_event_files } diff --git a/stream_alert_cli/test.py b/stream_alert_cli/test.py index 5a6681f4a..91e8247d0 100644 --- a/stream_alert_cli/test.py +++ b/stream_alert_cli/test.py @@ -32,6 +32,7 @@ from stream_alert.rule_processor.handler import StreamAlert # import all rules loaded from the main handler import stream_alert.rule_processor.main # pylint: disable=unused-import +from stream_alert.rule_processor.parsers import get_parser from stream_alert.rule_processor.payload import load_stream_payload from stream_alert.rule_processor.rules_engine import StreamRules from stream_alert_cli import helpers @@ -77,7 +78,6 @@ def __init__(self, context, config, print_output): Warnings and errors captrued during rule processor testing will still be written to stdout regardless of this setting. """ - helpers.setup_mock_dynamodb_ioc_table(config) # Create the RuleProcessor. Passing a mocked context object with fake # values and False for suppressing sending of alerts to alert processor self.processor = StreamAlert(context, False) @@ -87,7 +87,11 @@ def __init__(self, context, config, print_output): self.total_tests = 0 self.all_tests_passed = True self.print_output = print_output + # Configure mocks for Firehose and DDB helpers.setup_mock_firehose_delivery_streams(config) + helpers.setup_mock_dynamodb_ioc_table(config) + # Create a cache map of parsers to parser classes + self.parsers = {} def test_processor(self, rules_filter, files_filter, validate_only): """Perform integration tests for the 'rule' Lambda function @@ -112,14 +116,15 @@ def test_processor(self, rules_filter, files_filter, validate_only): for name in sorted(test_file_info): path = test_file_info[name] + events, error = helpers.load_test_file(path) - if not events: + if error is not None: self.all_tests_passed = False self.status_messages.append(StatusMessage(StatusMessage.WARNING, error)) continue print_header = True - for test_event in events['records']: + for test_event in events: self.total_tests += 1 if self._detect_old_test_event(test_event): self.all_tests_passed = False @@ -134,13 +139,17 @@ def test_processor(self, rules_filter, files_filter, validate_only): self.all_tests_passed = False continue - # Check if there are any rule filters in place + # Check if there are any rule filters in place, and if the current test event + # should be exeecuted per the filter if rules_filter and set(test_event['trigger_rules']).isdisjoint(rules_filter): self.total_tests -= 1 continue self.apply_helpers(test_event) + if 'override_record' in test_event: + self.apply_template(test_event) + formatted_record = helpers.format_lambda_test_record(test_event) # If this test is to validate the schema only, continue the loop and @@ -329,7 +338,6 @@ def _detect_old_test_event(test_event): return False - def check_keys(self, test_event): """Check if the test event contains the required keys @@ -339,7 +347,7 @@ def check_keys(self, test_event): Returns: bool: True if the proper keys are present """ - required_keys = {'data', 'description', 'log', 'service', 'source', 'trigger_rules'} + required_keys = {'description', 'log', 'service', 'source', 'trigger_rules'} record_keys = set(test_event) if not required_keys.issubset(record_keys): @@ -349,9 +357,16 @@ def check_keys(self, test_event): self.status_messages.append(StatusMessage(StatusMessage.FAILURE, message)) return False + input_data_keys = {'data', 'override_record'} + if not record_keys & input_data_keys: + missing_keys = ', '.join('\'{}\''.format(key) for key in input_data_keys) + message = 'Missing one of the following keys in log: {}'.format(missing_keys) + self.status_messages.append(StatusMessage(StatusMessage.FAILURE, message)) + return False + optional_keys = {'compress', 'validate_schema_only'} - key_diff = record_keys.difference(required_keys | optional_keys) + key_diff = record_keys.difference(required_keys | optional_keys | input_data_keys) # Log a warning if there are extra keys declared in the test log if key_diff: @@ -361,11 +376,42 @@ def check_keys(self, test_event): record_keys.difference_update(key_diff) self.status_messages.append(StatusMessage(StatusMessage.WARNING, message)) - return record_keys.issubset(required_keys | optional_keys) + return record_keys.issubset(required_keys | optional_keys | input_data_keys) + + def apply_template(self, test_event): + """Apply default values to the given test event + + Args: + test_event (dict): The loaded test event + """ + event_log = self.cli_config['logs'].get(test_event['log']) + + parser = event_log['parser'] + schema = event_log['schema'] + configuration = event_log.get('configuration', {}) + + # Add envelope keys + if configuration.get('envelope_keys'): + schema.update(configuration['envelope_keys']) + + # Setup the parser to access default optional values + self.parsers[parser] = self.parsers.get('parser', get_parser(parser)) + + # Setup the modified test event + default_test_event = {} + data = test_event['override_record'].copy() + + # Add apply default values based on the declared schema + for key, value in schema.iteritems(): + default_test_event[key] = self.parsers[parser].default_optional_values(value) + + data.update(default_test_event) + test_event['data'] = data @staticmethod def apply_helpers(test_record): - """Detect and apply helper functions to test fixtures + """Detect and apply helper functions to test event data + Helpers are declared in test fixtures via the following keyword: "" @@ -483,7 +529,6 @@ def check_log_declared_in_sources(self, base_message, test_event): return True - def analyze_record_delta(self, file_name, test_event): """Provide some additional context on why this test failed. This will perform some analysis of the test record to determine which keys are From 5246be8514a8b7ecb3c8313fa2fc305b78746f60 Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Fri, 16 Mar 2018 14:46:56 -0700 Subject: [PATCH 03/14] [tests] use override_records on two existing rule tests --- .../cloudwatch_control_message.json | 47 ++++---- .../rules/duo/duo_anonymous_ip_failure.json | 112 ++++++++---------- 2 files changed, 68 insertions(+), 91 deletions(-) diff --git a/tests/integration/rules/cloudwatch/cloudwatch_control_message.json b/tests/integration/rules/cloudwatch/cloudwatch_control_message.json index 79ffd1dc0..17e0e84b4 100644 --- a/tests/integration/rules/cloudwatch/cloudwatch_control_message.json +++ b/tests/integration/rules/cloudwatch/cloudwatch_control_message.json @@ -1,26 +1,21 @@ -{ - "records": [ - { - "data": { - "messageType": "CONTROL_MESSAGE", - "owner": "CloudwatchLogs", - "logGroup": "", - "logStream": "", - "subscriptionFilters": [], - "logEvents": [ - { - "id": "", - "timestamp": 1512601480749, - "message": "CWL CONTROL MESSAGE: Checking health of destination Kinesis stream." - } - ] - }, - "description": "CloudWatch Control Message (validation only)", - "log": "cloudwatch:control_message", - "service": "kinesis", - "source": "prefix_cluster1_stream_alert_kinesis", - "trigger_rules": [], - "validate_schema_only": true - } - ] -} \ No newline at end of file +[ + { + "override_record": { + "messageType": "CONTROL_MESSAGE", + "owner": "CloudwatchLogs", + "logEvents": [ + { + "id": "100", + "timestamp": 1512601480749, + "message": "CWL CONTROL MESSAGE: Checking health of destination Kinesis stream." + } + ] + }, + "description": "CloudWatch Control Message (validation only)", + "log": "cloudwatch:control_message", + "service": "kinesis", + "source": "prefix_cluster1_stream_alert_kinesis", + "trigger_rules": [], + "validate_schema_only": true + } +] \ No newline at end of file diff --git a/tests/integration/rules/duo/duo_anonymous_ip_failure.json b/tests/integration/rules/duo/duo_anonymous_ip_failure.json index fa67d8b1b..13cafb427 100644 --- a/tests/integration/rules/duo/duo_anonymous_ip_failure.json +++ b/tests/integration/rules/duo/duo_anonymous_ip_failure.json @@ -1,68 +1,50 @@ -{ - "records": [ - { - "data": { - "username": "user.name@email.com", - "access_device": { - "flash_version": "27.0.0.0", - "java_version": "uninstalled", - "os_version": "10.12.6", - "browser_version": "60.0.0000.80", - "trusted_endpoint_status": "not trusted", - "os": "Mac OS X", - "browser": "Chrome" - }, - "timestamp": 1505316499, - "new_enrollment": false, - "ip": "12.123.123.12", - "integration": "Test Integration", - "reason": "Anonymous IP", - "location": { - "city": "Place", - "state": "State", - "country": "US" - }, - "factor": "Duo Push", - "device": "555-123-4567", - "result": "FAILURE" +[ + { + "data": { + "username": "user.name@email.com", + "access_device": { + "flash_version": "27.0.0.0", + "java_version": "uninstalled", + "os_version": "10.12.6", + "browser_version": "60.0.0000.80", + "trusted_endpoint_status": "not trusted", + "os": "Mac OS X", + "browser": "Chrome" }, - "description": "Duo authentication log marked as failure as a result of 'Anonymous IP' that will create an alert", - "log": "duo:authentication", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_auth_sm-app-name_app", - "trigger_rules": ["duo_anonymous_ip_failure"] + "timestamp": 1505316499, + "new_enrollment": false, + "ip": "12.123.123.12", + "integration": "Test Integration", + "reason": "Anonymous IP", + "location": { + "city": "Place", + "state": "State", + "country": "US" + }, + "factor": "Duo Push", + "device": "555-123-4567", + "result": "FAILURE" }, - { - "data": { - "username": "user.name@email.com", - "access_device": { - "flash_version": "27.0.0.0", - "java_version": "uninstalled", - "os_version": "10.12.6", - "browser_version": "60.0.0000.80", - "trusted_endpoint_status": "not trusted", - "os": "Mac OS X", - "browser": "Chrome" - }, - "timestamp": 1505316499, - "new_enrollment": false, - "ip": "12.123.123.12", - "integration": "Test Integration", - "reason": "Location restricted", - "location": { - "city": "Place", - "state": "State", - "country": "US" - }, - "factor": "Duo Push", - "device": "555-123-5678", - "result": "FAILURE" + "description": "Duo authentication log marked as failure as a result of 'Anonymous IP' that will create an alert", + "log": "duo:authentication", + "service": "stream_alert_app", + "source": "prefix_cluster_duo_auth_sm-app-name_app", + "trigger_rules": ["duo_anonymous_ip_failure"] + }, + { + "override_record": { + "reason": "Location restricted", + "location": { + "city": "Place", + "state": "State", + "country": "US" }, - "description": "Duo authentication log marked as failure as a result of 'Location restricted' that will not create an alert", - "log": "duo:authentication", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_auth_sm-app-name_app", - "trigger_rules": [] - } - ] -} \ No newline at end of file + "result": "FAILURE" + }, + "description": "Duo authentication log marked as failure as a result of 'Location restricted' that will not create an alert", + "log": "duo:authentication", + "service": "stream_alert_app", + "source": "prefix_cluster_duo_auth_sm-app-name_app", + "trigger_rules": [] + } +] \ No newline at end of file From b5354243a50c45f704919b0d48866daca0836573 Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Fri, 16 Mar 2018 14:47:10 -0700 Subject: [PATCH 04/14] [tests] update unit tests per override_record functionality --- tests/unit/stream_alert_cli/test_helpers.py | 37 +++++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/tests/unit/stream_alert_cli/test_helpers.py b/tests/unit/stream_alert_cli/test_helpers.py index 4988c6b96..03e8243c5 100644 --- a/tests/unit/stream_alert_cli/test_helpers.py +++ b/tests/unit/stream_alert_cli/test_helpers.py @@ -21,9 +21,9 @@ from nose.tools import assert_equal, assert_false, assert_is_none, assert_items_equal -def test_load_test_file(): - """Load Rule Test File - Good File""" - test_data = {'records': []} +def test_load_test_file_list(): + """CLI - Helpers - Load Rule Test File - Good File (list)""" + test_data = [{'data': {'field': 'value'}}] mock = mock_open(read_data=json.dumps(test_data)) with patch('__builtin__.open', mock): event, error = helpers.load_test_file('no/path') @@ -32,29 +32,44 @@ def test_load_test_file(): assert_is_none(error) +def test_load_test_file_map(): + """CLI - Helpers - Load Rule Test File - Good File (map)""" + test_data = {'records': [{'field': 'value'}]} + mock = mock_open(read_data=json.dumps(test_data)) + with patch('__builtin__.open', mock): + event, error = helpers.load_test_file('no/path') + + assert_equal(event, test_data['records']) + assert_is_none(error) + + def test_load_test_file_bad_value(): - """Load Rule Test File - Bad Value""" + """CLI - Helpers - Load Rule Test File - Bad Value""" mock = mock_open(read_data='bad json string') with patch('__builtin__.open', mock): event, error = helpers.load_test_file('no/path') - assert_false(event) + assert_equal(event, []) assert_equal(error, 'Improperly formatted file (no/path): No JSON object could be decoded') def test_load_test_file_bad_format(): - """Load Rule Test File - Bad Format""" - mock = mock_open(read_data=json.dumps({'record': []})) + """CLI - Helpers - Load Rule Test File - Bad Format""" + test_data = {'records': {'field': 'value'}} + mock = mock_open(read_data=json.dumps(test_data)) with patch('__builtin__.open', mock): event, error = helpers.load_test_file('no/path') assert_false(event) - assert_equal(error, 'Improperly formatted file (no/path): File must be a dict (JSON ' - 'object) with top level key \'records\'') + assert_equal( + error, + 'Improperly formatted file (no/path): Test file must contain either ' + 'a list of maps, or a list of ' + 'maps preceeded with a `records` key') def test_get_rule_test_files(): - """Get Rule Test Files - Load Files""" + """CLI - Helpers - Get Rule Test Files - Load Files""" with patch('os.walk') as mock_walk: mock_walk.return_value = [ ('/root_dir', (), ('file.json', 'file2.json',)), @@ -67,7 +82,7 @@ def test_get_rule_test_files(): def test_get_rules_from_test_events(): - """Get Rules From Test Events""" + """CLI - Helpers - Get Rules From Test Events""" with patch('os.walk') as mock_walk: mock_walk.return_value = [('/root_dir', (), ('file.json',))] From a4c3aa3344ce73e5cb6f58bcaa6b7d3841c43622 Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Tue, 20 Mar 2018 10:35:00 -0700 Subject: [PATCH 05/14] [tests] @ryandeivert's feedback --- stream_alert_cli/helpers.py | 4 ++++ stream_alert_cli/test.py | 17 ++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/stream_alert_cli/helpers.py b/stream_alert_cli/helpers.py index 6ff35413b..361c49e88 100644 --- a/stream_alert_cli/helpers.py +++ b/stream_alert_cli/helpers.py @@ -660,7 +660,11 @@ def load_test_file(path): return [], message else: # Check for legacy format, return a list + # TOOD: Remove legacy format support if 'records' in contents and isinstance(contents['records'], list): + LOGGER_CLI.warning('Legacy testing format detected, ' + 'test events should be a JSON list: [%s]', + os.path.basename(path)) return contents['records'], None # Expect that the test event is a JSON list elif isinstance(contents, list): diff --git a/stream_alert_cli/test.py b/stream_alert_cli/test.py index 91e8247d0..1a3b60713 100644 --- a/stream_alert_cli/test.py +++ b/stream_alert_cli/test.py @@ -391,20 +391,19 @@ def apply_template(self, test_event): configuration = event_log.get('configuration', {}) # Add envelope keys - if configuration.get('envelope_keys'): - schema.update(configuration['envelope_keys']) + schema.update(configuration.get('envelope_keys', {})) # Setup the parser to access default optional values - self.parsers[parser] = self.parsers.get('parser', get_parser(parser)) - - # Setup the modified test event - default_test_event = {} - data = test_event['override_record'].copy() + self.parsers[parser] = self.parsers.get(parser, get_parser(parser)) # Add apply default values based on the declared schema - for key, value in schema.iteritems(): - default_test_event[key] = self.parsers[parser].default_optional_values(value) + default_test_event = {key: self.parsers[parser].default_optional_values(value) + for key, value + in schema.iteritems()} + # Fill in the fields left out in the 'override_record' field, + # and update the test event with a full 'data' key + data = test_event['override_record'].copy() data.update(default_test_event) test_event['data'] = data From 7766cd9d3d863da54855696fa80330e906d54687 Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Tue, 20 Mar 2018 10:45:47 -0700 Subject: [PATCH 06/14] [scripts] new test event converter --- tests/scripts/convert_test_events.py | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100755 tests/scripts/convert_test_events.py diff --git a/tests/scripts/convert_test_events.py b/tests/scripts/convert_test_events.py new file mode 100755 index 000000000..399876073 --- /dev/null +++ b/tests/scripts/convert_test_events.py @@ -0,0 +1,34 @@ +#! /usr/bin/env python + +# This script should be used as a one-off to convert all rule test events +# from the legacy format to the new test format + +import json +import os + +TEST_FILES_PATH = 'tests/integration/rules' + + +def convert_test_event(file): + with open(file, 'r+') as test_event: + # Load the file, handle bad JSON + try: + data = json.load(test_event) + except ValueError as err: + print '[ERROR] {}: {}'.format(os.path.basename(file), err) + return + # Convert legacy test events + if isinstance(data, dict) and 'records' in data: + test_event.seek(0) + test_event.truncate() + json.dump( + data['records'], test_event, indent=2, separators=(',', ':'), sort_keys=True) + +def convert(): + for root, dirs, files in os.walk(TEST_FILES_PATH): + for file in files: + convert_test_event(os.path.join(root, file)) + + +if __name__ == "__main__": + convert() From 7c372ea1abab44ca50ebbfa7bacef5c567f8c3b7 Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Tue, 20 Mar 2018 11:05:52 -0700 Subject: [PATCH 07/14] [tests] convert test files to new format --- .../binaryalert/binaryalert_yara_match.json | 94 +-- .../rules/box/box_admin_events.json | 62 +- .../cloudtrail_critical_api_calls.json | 792 +++++++++--------- .../cloudtrail_mfa_policy_abuse_attempt.json | 256 +++--- ...oudtrail_network_acl_ingress_anywhere.json | 253 +++--- .../cloudtrail/cloudtrail_put_bucket_acl.json | 397 +++++---- .../cloudtrail_put_object_acl_public.json | 611 +++++++------- .../cloudtrail_root_account_usage.json | 191 +++-- ...trail_security_group_ingress_anywhere.json | 283 ++++--- ...bypass_code_create_non_auto_generated.json | 64 +- .../duo_bypass_code_create_non_expiring.json | 65 +- .../duo_bypass_code_create_unlimited_use.json | 65 +- tests/integration/rules/duo/duo_fraud.json | 130 +-- ..._dismiss_stale_pull_request_approvals.json | 89 +- .../github_disable_protect_this_branch.json | 89 +- ...disable_required_pull_request_reviews.json | 89 +- ...github_disable_required_status_checks.json | 97 ++- ...ub_disable_two_factor_requirement_org.json | 96 ++- ...b_disable_two_factor_requirement_user.json | 96 ++- .../github_oauth_application_create.json | 88 +- .../github/github_site_admin_action.json | 88 +- .../github_site_admin_user_promotion.json | 88 +- .../rules/gsuite/gsuite_admin.json | 82 +- .../rules/guardduty/guard_duty_all.json | 46 +- .../mitre_attack/right_to_left_character.json | 208 +++-- .../onelogin_events_assumed_role.json | 169 ++-- 26 files changed, 2260 insertions(+), 2328 deletions(-) diff --git a/tests/integration/rules/binaryalert/binaryalert_yara_match.json b/tests/integration/rules/binaryalert/binaryalert_yara_match.json index 21c84b80e..14ed9902b 100644 --- a/tests/integration/rules/binaryalert/binaryalert_yara_match.json +++ b/tests/integration/rules/binaryalert/binaryalert_yara_match.json @@ -1,50 +1,48 @@ -{ - "records": [ - { - "data": { - "FileInfo": { - "MD5": "...", - "S3LastModified": "...", - "S3Location": "...", - "S3Metadata": {}, - "SHA256": "..." - }, - "MatchedRules": { - "Rule1": { - "MatchedStrings": [ - "$eicar_regex" - ], - "Meta": { - "author": "Austin Byers (Airbnb CSIRT)", - "description": "This is a standard AV test, intended to check whether BinaryAlert is working correctly.", - "reference": "http://www.eicar.org/86-0-Intended-use.html" - }, - "RuleFile": "eicar.yar", - "RuleName": "eicar_av_test", - "RuleTags": [] - } - }, - "NumMatchedRules": "1" +[ + { + "data":{ + "FileInfo":{ + "MD5":"...", + "S3LastModified":"...", + "S3Location":"...", + "S3Metadata":{}, + "SHA256":"..." }, - "description": "All YARA matches from BinaryAlert trigger an alert", - "log": "binaryalert", - "service": "sns", - "source": "prefix_cluster_sample_topic", - "trigger_rules": [ - "binaryalert_yara_match" - ] - }, - { - "data": { - "FileInfo": {}, - "MatchedRules": {}, - "NumMatchedRules": "0" + "MatchedRules":{ + "Rule1":{ + "MatchedStrings":[ + "$eicar_regex" + ], + "Meta":{ + "author":"Austin Byers (Airbnb CSIRT)", + "description":"This is a standard AV test, intended to check whether BinaryAlert is working correctly.", + "reference":"http://www.eicar.org/86-0-Intended-use.html" + }, + "RuleFile":"eicar.yar", + "RuleName":"eicar_av_test", + "RuleTags":[] + } }, - "description": "No alerts triggered if no YARA rules were matched", - "log": "binaryalert", - "service": "sns", - "source": "prefix_cluster_sample_topic", - "trigger_rules": [] - } - ] -} + "NumMatchedRules":"1" + }, + "description":"All YARA matches from BinaryAlert trigger an alert", + "log":"binaryalert", + "service":"sns", + "source":"prefix_cluster_sample_topic", + "trigger_rules":[ + "binaryalert_yara_match" + ] + }, + { + "data":{ + "FileInfo":{}, + "MatchedRules":{}, + "NumMatchedRules":"0" + }, + "description":"No alerts triggered if no YARA rules were matched", + "log":"binaryalert", + "service":"sns", + "source":"prefix_cluster_sample_topic", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/box/box_admin_events.json b/tests/integration/rules/box/box_admin_events.json index 16e6dcdc2..123301fcc 100644 --- a/tests/integration/rules/box/box_admin_events.json +++ b/tests/integration/rules/box/box_admin_events.json @@ -1,33 +1,31 @@ -{ - "records": [ - { - "data": { - "additional_details": null, - "created_at": "2017-10-27T12:31:22-07:00", - "created_by": { - "id": "2810219233", - "login": "testemail@email.com", - "name": "User Name", - "type": "user" - }, - "event_id": "0e0b8122-17ed-42ee-8a9d-d9a57bf8dd83", - "event_type": "ADD_LOGIN_ACTIVITY_DEVICE", - "ip_address": "1.1.1.1", - "session_id": null, - "source": { - "id": "2810219233", - "login": "testemail@email.com", - "name": "User Name", - "type": "user" - }, - "type": "event" +[ + { + "data":{ + "additional_details":null, + "created_at":"2017-10-27T12:31:22-07:00", + "created_by":{ + "id":"2810219233", + "login":"testemail@email.com", + "name":"User Name", + "type":"user" }, - "description": "Box admin event log exmaple (validation only)", - "log": "box:admin_events", - "service": "stream_alert_app", - "source": "prefix_cluster_box_admin_events_sm-app-name_app", - "trigger_rules": [], - "validate_schema_only": true - } - ] -} \ No newline at end of file + "event_id":"0e0b8122-17ed-42ee-8a9d-d9a57bf8dd83", + "event_type":"ADD_LOGIN_ACTIVITY_DEVICE", + "ip_address":"1.1.1.1", + "session_id":null, + "source":{ + "id":"2810219233", + "login":"testemail@email.com", + "name":"User Name", + "type":"user" + }, + "type":"event" + }, + "description":"Box admin event log exmaple (validation only)", + "log":"box:admin_events", + "service":"stream_alert_app", + "source":"prefix_cluster_box_admin_events_sm-app-name_app", + "trigger_rules":[], + "validate_schema_only":true + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_critical_api_calls.json b/tests/integration/rules/cloudtrail/cloudtrail_critical_api_calls.json index be618a176..c02cd7612 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_critical_api_calls.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_critical_api_calls.json @@ -1,427 +1,425 @@ -{ - "records": [ - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DeleteSubnet", - "eventSource": "ec2.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "subnetId": "..." +[ + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DeleteSubnet", + "eventSource":"ec2.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "subnetId":"..." + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } }, - "responseElements": { - "_return": true - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Deleting an AWS subnet (DeleteSubnet) will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DeleteVpc", - "eventSource": "ec2.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "vpcId": "..." - }, - "responseElements": { - "_return": true + "description":"Deleting an AWS subnet (DeleteSubnet) will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DeleteVpc", + "eventSource":"ec2.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "vpcId":"..." + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Deleting an AWS VPC (DeleteVpc) will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "UpdateTrail", - "eventSource": "cloudtrail.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "cloudWatchLogsLogGroupArn": "...", - "cloudWatchLogsRoleArn": "...", - "enableLogFileValidation": true, - "isMultiRegionTrail": true, - "kmsKeyId": "", - "name": "..." - }, - "responseElements": { - "cloudWatchLogsLogGroupArn": "...", - "cloudWatchLogsRoleArn": "...", - "includeGlobalServiceEvents": true, - "isMultiRegionTrail": true, - "logFileValidationEnabled": true, - "name": "...", - "s3BucketName": "...", - "trailARN": "..." + "description":"Deleting an AWS VPC (DeleteVpc) will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"UpdateTrail", + "eventSource":"cloudtrail.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "cloudWatchLogsLogGroupArn":"...", + "cloudWatchLogsRoleArn":"...", + "enableLogFileValidation":true, + "isMultiRegionTrail":true, + "kmsKeyId":"", + "name":"..." + }, + "responseElements":{ + "cloudWatchLogsLogGroupArn":"...", + "cloudWatchLogsRoleArn":"...", + "includeGlobalServiceEvents":true, + "isMultiRegionTrail":true, + "logFileValidationEnabled":true, + "name":"...", + "s3BucketName":"...", + "trailARN":"..." + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Updating an AWS CloudTrail trail (UpdateTrail) will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "StopLogging", - "eventSource": "cloudtrail.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "name": "..." + "description":"Updating an AWS CloudTrail trail (UpdateTrail) will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"StopLogging", + "eventSource":"cloudtrail.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "name":"..." + }, + "responseElements":null, + "sourceIPAddress":"...", + "userAgent":"...m", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"false" + } }, - "responseElements": null, - "sourceIPAddress": "...", - "userAgent": "...m", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "false" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Suspending the recording of AWS API calls and log file delivery for a trail will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DeleteDBCluster", - "eventSource": "rds.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "dBClusterIdentifier": "...", - "skipFinalSnapshot": true - }, - "responseElements": { - "allocatedStorage": 1, - "associatedRoles": [], - "availabilityZones": [ - "...", - "...", - "..." - ], - "backupRetentionPeriod": 1, - "clusterCreateTime": "...", - "dBClusterArn": "...", - "dBClusterIdentifier": "...", - "dBClusterMembers": [ - { - "dBClusterParameterGroupStatus": "...", - "dBInstanceIdentifier": "...", - "isClusterWriter": true, - "promotionTier": 1 - }, - { - "dBClusterParameterGroupStatus": "...", - "dBInstanceIdentifier": "...", - "isClusterWriter": false, - "promotionTier": 1 - } - ], - "dBClusterParameterGroup": "...", - "dBSubnetGroup": "...", - "databaseName": "...", - "dbClusterResourceId": "...", - "earliestRestorableTime": "...", - "endpoint": "...", - "engine": "...", - "engineVersion": "...", - "hostedZoneId": "...", - "iAMDatabaseAuthenticationEnabled": false, - "latestRestorableTime": "...", - "masterUsername": "...", - "multiAZ": true, - "port": 3306, - "preferredBackupWindow": "...", - "preferredMaintenanceWindow": "...", - "readReplicaIdentifiers": [], - "readerEndpoint": "...", - "status": "...", - "storageEncrypted": false, - "vpcSecurityGroups": [ - { - "status": "...", - "vpcSecurityGroupId": "..." - } - ] - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "false" - } + "description":"Suspending the recording of AWS API calls and log file delivery for a trail will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DeleteDBCluster", + "eventSource":"rds.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "dBClusterIdentifier":"...", + "skipFinalSnapshot":true + }, + "responseElements":{ + "allocatedStorage":1, + "associatedRoles":[], + "availabilityZones":[ + "...", + "...", + "..." + ], + "backupRetentionPeriod":1, + "clusterCreateTime":"...", + "dBClusterArn":"...", + "dBClusterIdentifier":"...", + "dBClusterMembers":[ + { + "dBClusterParameterGroupStatus":"...", + "dBInstanceIdentifier":"...", + "isClusterWriter":true, + "promotionTier":1 }, - "type": "..." - } + { + "dBClusterParameterGroupStatus":"...", + "dBInstanceIdentifier":"...", + "isClusterWriter":false, + "promotionTier":1 + } + ], + "dBClusterParameterGroup":"...", + "dBSubnetGroup":"...", + "databaseName":"...", + "dbClusterResourceId":"...", + "earliestRestorableTime":"...", + "endpoint":"...", + "engine":"...", + "engineVersion":"...", + "hostedZoneId":"...", + "iAMDatabaseAuthenticationEnabled":false, + "latestRestorableTime":"...", + "masterUsername":"...", + "multiAZ":true, + "port":3306, + "preferredBackupWindow":"...", + "preferredMaintenanceWindow":"...", + "readReplicaIdentifiers":[], + "readerEndpoint":"...", + "status":"...", + "storageEncrypted":false, + "vpcSecurityGroups":[ + { + "status":"...", + "vpcSecurityGroupId":"..." + } + ] + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"false" + } + }, + "type":"..." } - ] - }, - "description": "Deleting a database cluster (DeleteDBCluster) will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "StopConfigurationRecorder", - "eventSource": "config.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "configurationRecorderName": "..." + "description":"Deleting a database cluster (DeleteDBCluster) will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"StopConfigurationRecorder", + "eventSource":"config.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "configurationRecorderName":"..." + }, + "responseElements":null, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"false" + } }, - "responseElements": null, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "false" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Suspending recording of resource changes through AWS Config (StopConfigurationRecorder) will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DeleteFlowLogs", - "eventSource": "ec2.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "flowLogId": [ - "..." - ] + "description":"Suspending recording of resource changes through AWS Config (StopConfigurationRecorder) will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DeleteFlowLogs", + "eventSource":"ec2.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "flowLogId":[ + "..." + ] + }, + "responseElements":{ + "unsuccessful":[] + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "invokedBy":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } }, - "responseElements": { - "unsuccessful": [] - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "invokedBy": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Deleting AWS network flow logs (DeleteFlowLogs) will create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [ - "cloudtrail_critical_api_calls" + } ] }, - { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DescribeFlowLogs", - "eventSource": "ec2.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "flowLogId": [ - "..." - ] + "description":"Deleting AWS network flow logs (DeleteFlowLogs) will create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_critical_api_calls" + ] + }, + { + "data":{ + "Records":[ + { + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DescribeFlowLogs", + "eventSource":"ec2.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "flowLogId":[ + "..." + ] + }, + "responseElements":{ + "unsuccessful":[] + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "invokedBy":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } }, - "responseElements": { - "unsuccessful": [] - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "invokedBy": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } - }, - "type": "..." - } + "type":"..." } - ] - }, - "description": "Describing AWS network flog logs will not create an alert.", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [] - } - ] -} \ No newline at end of file + } + ] + }, + "description":"Describing AWS network flog logs will not create an alert.", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.json b/tests/integration/rules/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.json index 20dae3560..c8404db34 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.json @@ -1,135 +1,137 @@ -{ - "records": [ +[ + { + "data":{ + "Records":[ { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DeactivateMFADevice", - "eventSource": "iam.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "subnetId": "..." - }, - "responseElements": { - "_return": true - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "false" - } - }, - "type": "..." - } - } - ] + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DeactivateMFADevice", + "eventSource":"iam.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "subnetId":"..." + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"false" + } }, - "description": "Attempting to deactivate an MFA device without being MFA'd in", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": ["cloudtrail_mfa_policy_abuse_attempt"] - }, + "type":"..." + } + } + ] + }, + "description":"Attempting to deactivate an MFA device without being MFA'd in", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_mfa_policy_abuse_attempt" + ] + }, + { + "data":{ + "Records":[ { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "DeactivateMFADevice", - "eventSource": "iam.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "subnetId": "..." - }, - "responseElements": { - "_return": true - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } - }, - "type": "..." - } - } - ] + "awsRegion":"us-west-2", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"DeactivateMFADevice", + "eventSource":"iam.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "subnetId":"..." + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } }, - "description": "Benign, user is MFAd", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": [] - }, + "type":"..." + } + } + ] + }, + "description":"Benign, user is MFAd", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[] + }, + { + "data":{ + "Records":[ { - "data": { - "Records": [ - { - "awsRegion": "us-west-2", - "eventID": "123aaac1-123d-456a-1k29a4dd2kea", - "eventName": "CreateVirtualMFADevice", - "eventSource": "iam.amazonaws.com", - "eventTime": "2017-01-01T00:20:50Z", - "eventType": "AwsApiCall", - "eventVersion": "1.05", - "errorCode": "EntityAlreadyExists", - "errorMessage": "An error occurred (EntityAlreadyExists) when calling the CreateVirtualMFADevice operation: MFADevice entity at the same path and name already exists.", - "recipientAccountId": "123456789123", - "requestID": "...", - "requestParameters": { - "subnetId": "..." - }, - "responseElements": { - "_return": true - }, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accessKeyId": "...", - "accountId": "12345", - "arn": "...", - "principalId": "12345", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "false" - } - }, - "type": "..." - } - } - ] + "awsRegion":"us-west-2", + "errorCode":"EntityAlreadyExists", + "errorMessage":"An error occurred (EntityAlreadyExists) when calling the CreateVirtualMFADevice operation: MFADevice entity at the same path and name already exists.", + "eventID":"123aaac1-123d-456a-1k29a4dd2kea", + "eventName":"CreateVirtualMFADevice", + "eventSource":"iam.amazonaws.com", + "eventTime":"2017-01-01T00:20:50Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"123456789123", + "requestID":"...", + "requestParameters":{ + "subnetId":"..." + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"12345", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"false" + } }, - "description": "Attempt to create an MFA device when one already exists, and user is not MFA'd", - "log": "cloudtrail:events", - "service": "s3", - "source": "prefix.cluster.sample.bucket", - "trigger_rules": ["cloudtrail_mfa_policy_abuse_attempt"] + "type":"..." + } } + ] + }, + "description":"Attempt to create an MFA device when one already exists, and user is not MFA'd", + "log":"cloudtrail:events", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "cloudtrail_mfa_policy_abuse_attempt" ] -} + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_network_acl_ingress_anywhere.json b/tests/integration/rules/cloudtrail/cloudtrail_network_acl_ingress_anywhere.json index 2cea8de87..34c805ed7 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_network_acl_ingress_anywhere.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_network_acl_ingress_anywhere.json @@ -1,131 +1,128 @@ -{ - "records": [ - { - "data": { - "account": 12345, - "region": "123456123456", - "detail-type": "...", - "source": "1.1.1.2", - "version": "1.05", - "time": "...", - "id": "12345", - "resources": { - "test": "..." - }, - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "IAMUser", - "principalId": "...", - "arn": "...", - "accountId": "12345", - "accessKeyId": "...", - "userName": "...", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "...", - "creationDate": "..." - } - } - }, - "eventTime": "...", - "eventSource": "...", - "eventName": "CreateNetworkAclEntry", - "awsRegion": "...", - "sourceIPAddress": "...", - "userAgent": "...", - "requestParameters": { - "networkAclId": "...", - "ruleNumber": 100, - "egress": false, - "ruleAction": "allow", - "icmpTypeCode": {}, - "portRange": { - "from": -1, - "to": -1 - }, - "aclProtocol": "-1", - "cidrBlock": "0.0.0.0/0" - }, - "responseElements": { - "_return": true - }, - "requestID": "...", - "eventID": "...", - "eventType": "AwsApiCall", - "recipientAccountId": "12345" - } - }, - "description": "A Network ACL that allows ingress from anywhere should create an alert.", - "log": "cloudwatch:events", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "cloudtrail_network_acl_ingress_anywhere" - ] +[ + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"...", + "eventID":"...", + "eventName":"CreateNetworkAclEntry", + "eventSource":"...", + "eventTime":"...", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "aclProtocol":"-1", + "cidrBlock":"0.0.0.0/0", + "egress":false, + "icmpTypeCode":{}, + "networkAclId":"...", + "portRange":{ + "from":-1, + "to":-1 + }, + "ruleAction":"allow", + "ruleNumber":100 }, - { - "data": { - "account": 12345, - "region": "123456123456", - "detail-type": "...", - "source": "...", - "version": "1.05", - "time": "...", - "id": "12345", - "resources": { - "test": "..." - }, - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "IAMUser", - "principalId": "...", - "arn": "...", - "accountId": "12345", - "accessKeyId": "...", - "userName": "...", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "...", - "creationDate": "..." - } - } - }, - "eventTime": "...", - "eventSource": "...", - "eventName": "CreateNetworkAclEntry", - "awsRegion": "...", - "sourceIPAddress": "...", - "userAgent": "...", - "requestParameters": { - "networkAclId": "...", - "ruleNumber": 100, - "egress": false, - "ruleAction": "deny", - "icmpTypeCode": {}, - "portRange": { - "from": -1, - "to": -1 - }, - "aclProtocol": "-1", - "cidrBlock": "0.0.0.0/0" - }, - "responseElements": { - "_return": true - }, - "requestID": "...", - "eventID": "...", - "eventType": "AwsApiCall", - "recipientAccountId": "12345" - } - }, - "description": "A Network ACL that denies internet ingress should not create an alert.", - "log": "cloudwatch:events", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - ] + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"..." + } + }, + "type":"IAMUser", + "userName":"..." } + }, + "detail-type":"...", + "id":"12345", + "region":"123456123456", + "resources":{ + "test":"..." + }, + "source":"1.1.1.2", + "time":"...", + "version":"1.05" + }, + "description":"A Network ACL that allows ingress from anywhere should create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "cloudtrail_network_acl_ingress_anywhere" ] -} + }, + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"...", + "eventID":"...", + "eventName":"CreateNetworkAclEntry", + "eventSource":"...", + "eventTime":"...", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "aclProtocol":"-1", + "cidrBlock":"0.0.0.0/0", + "egress":false, + "icmpTypeCode":{}, + "networkAclId":"...", + "portRange":{ + "from":-1, + "to":-1 + }, + "ruleAction":"deny", + "ruleNumber":100 + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"..." + } + }, + "type":"IAMUser", + "userName":"..." + } + }, + "detail-type":"...", + "id":"12345", + "region":"123456123456", + "resources":{ + "test":"..." + }, + "source":"...", + "time":"...", + "version":"1.05" + }, + "description":"A Network ACL that denies internet ingress should not create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_put_bucket_acl.json b/tests/integration/rules/cloudtrail/cloudtrail_put_bucket_acl.json index 4c99a20cd..53de8b2d9 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_put_bucket_acl.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_put_bucket_acl.json @@ -1,217 +1,214 @@ -{ - "records": [ - { - "data": { - "account": 12345, - "detail": { - "awsRegion": "us-east-1", - "eventID": "...", - "eventName": "PutBucketAcl", - "eventSource": "s3.amazonaws.com", - "eventTime": "...", - "eventType": "...", - "eventVersion": "...", - "recipientAccountId": "12345", - "requestID": "...", - "requestParameters": { - "AccessControlPolicy": { - "AccessControlList": { - "Grant": [ - { - "Grantee": { - "DisplayName": "...", - "ID": "...", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "CanonicalUser" - }, - "Permission": "FULL_CONTROL" +[ + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"us-east-1", + "eventID":"...", + "eventName":"PutBucketAcl", + "eventSource":"s3.amazonaws.com", + "eventTime":"...", + "eventType":"...", + "eventVersion":"...", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "AccessControlPolicy":{ + "AccessControlList":{ + "Grant":[ + { + "Grantee":{ + "DisplayName":"...", + "ID":"...", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"CanonicalUser" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ" + "Permission":"FULL_CONTROL" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/global/AuthenticatedUsers", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ_ACP" + "Permission":"READ" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/global/AuthenticatedUsers", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/global/AllUsers", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ" + "Permission":"READ_ACP" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/global/AllUsers", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/global/AllUsers", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ_ACP" - } - ] - }, - "Owner": { - "DisplayName": "...", - "ID": "..." - }, - "xmlns": "http://s3.amazonaws.com/doc/2006-03-01/" + "Permission":"READ" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/global/AllUsers", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" + }, + "Permission":"READ_ACP" + } + ] }, - "acl": [ - "" - ], - "bucketName": "..." - }, - "responseElements": null, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accountId": "12345", - "arn": "...", - "invokedBy": "...", - "principalId": "...", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } + "Owner":{ + "DisplayName":"...", + "ID":"..." }, - "type": "IAMUser", - "userName": "..." - } - }, - "detail-type": "...", - "id": "12345", - "region": "us-east-1", - "resources": { - "test": "..." + "xmlns":"http://s3.amazonaws.com/doc/2006-03-01/" + }, + "acl":[ + "" + ], + "bucketName":"..." }, - "source": "...", - "time": "...", - "version": "..." + "responseElements":null, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accountId":"12345", + "arn":"...", + "invokedBy":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } + }, + "type":"IAMUser", + "userName":"..." + } + }, + "detail-type":"...", + "id":"12345", + "region":"us-east-1", + "resources":{ + "test":"..." }, - "description": "Modifying an S3 bucket to have a bucket ACL of AllUsers or AuthenticatedUsers should create an alert.", - "log": "cloudwatch:events", - "service": "kinesis", - "source": "prefix_cluster1_stream_alert_kinesis", - "trigger_rules": [ - "cloudtrail_put_bucket_acl" - ] + "source":"...", + "time":"...", + "version":"..." }, - { - "data": { - "account": 12345, - "detail": { - "awsRegion": "us-east-1", - "eventID": "...", - "eventName": "PutBucketAcl", - "eventSource": "...", - "eventTime": "...", - "eventType": "...", - "eventVersion": "...", - "recipientAccountId": "12345", - "requestID": "...", - "requestParameters": { - "AccessControlPolicy": { - "AccessControlList": { - "Grant": [ - { - "Grantee": { - "DisplayName": "...", - "ID": "...", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "CanonicalUser" - }, - "Permission": "FULL_CONTROL" + "description":"Modifying an S3 bucket to have a bucket ACL of AllUsers or AuthenticatedUsers should create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "cloudtrail_put_bucket_acl" + ] + }, + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"us-east-1", + "eventID":"...", + "eventName":"PutBucketAcl", + "eventSource":"...", + "eventTime":"...", + "eventType":"...", + "eventVersion":"...", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "AccessControlPolicy":{ + "AccessControlList":{ + "Grant":[ + { + "Grantee":{ + "DisplayName":"...", + "ID":"...", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"CanonicalUser" + }, + "Permission":"FULL_CONTROL" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/s3/LogDelivery", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ" + "Permission":"READ" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/s3/LogDelivery", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ_ACP" + "Permission":"READ_ACP" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/s3/LogDelivery", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ" + "Permission":"READ" + }, + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/s3/LogDelivery", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" }, - { - "Grantee": { - "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:type": "Group" - }, - "Permission": "READ_ACP" - } - ] - }, - "Owner": { - "DisplayName": "...", - "ID": "..." - }, - "xmlns": "http://s3.amazonaws.com/doc/2006-03-01/" + "Permission":"READ_ACP" + } + ] }, - "acl": [ - "" - ], - "bucketName": "..." - }, - "responseElements": null, - "sourceIPAddress": "...", - "userAgent": "...", - "userIdentity": { - "accountId": "12345", - "arn": "...", - "invokedBy": "...", - "principalId": "...", - "sessionContext": { - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "true" - } + "Owner":{ + "DisplayName":"...", + "ID":"..." }, - "type": "IAMUser", - "userName": "..." - } - }, - "detail-type": "...", - "id": "12345", - "region": "us-east-1", - "resources": { - "test": "..." + "xmlns":"http://s3.amazonaws.com/doc/2006-03-01/" + }, + "acl":[ + "" + ], + "bucketName":"..." }, - "source": "...", - "time": "...", - "version": "..." + "responseElements":null, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accountId":"12345", + "arn":"...", + "invokedBy":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } + }, + "type":"IAMUser", + "userName":"..." + } + }, + "detail-type":"...", + "id":"12345", + "region":"us-east-1", + "resources":{ + "test":"..." }, - "description": "Modifying an S3 bucket ACL without use of AllUsers or AuthenticatedUsers should not create an alert.", - "log": "cloudwatch:events", - "service": "kinesis", - "source": "prefix_cluster1_stream_alert_kinesis", - "trigger_rules": [ - ] - } - ] -} + "source":"...", + "time":"...", + "version":"..." + }, + "description":"Modifying an S3 bucket ACL without use of AllUsers or AuthenticatedUsers should not create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_put_object_acl_public.json b/tests/integration/rules/cloudtrail/cloudtrail_put_object_acl_public.json index 96432356f..c12e85b94 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_put_object_acl_public.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_put_object_acl_public.json @@ -1,337 +1,334 @@ -{ - "records": [ - { - "data": { - "account": 12345, - "region": "...", - "detail-type": "...", - "source": "...", - "version": "0", - "time": "...", - "id": "123", - "resources": [], - "detail": { - "eventVersion": "...", - "eventID": "...", - "eventTime": "...", - "additionalEventData": { - "x_amz_id_2": "..." - }, - "requestParameters": { - "AccessControlPolicy": { - "Owner": { - "ID": "..." - }, - "xmlns": "http://s3.amazonaws.com/doc/2006-03-01/", - "AccessControlList": { - "Grant": { - "Grantee": { - "xmlns_xsi": "http://www.w3.org/2001/XMLSchema-instance", - "ID": "...", - "xsi_type": "CanonicalUser" - }, - "Permission": "FULL_CONTROL" - } +[ + { + "data":{ + "account":12345, + "detail":{ + "additionalEventData":{ + "x_amz_id_2":"..." + }, + "awsRegion":"...", + "eventID":"...", + "eventName":"PutObjectAcl", + "eventSource":"...", + "eventTime":"...", + "eventType":"AwsApiCall", + "eventVersion":"...", + "readOnly":false, + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "AccessControlPolicy":{ + "AccessControlList":{ + "Grant":{ + "Grantee":{ + "ID":"...", + "xmlns_xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi_type":"CanonicalUser" + }, + "Permission":"FULL_CONTROL" } }, - "bucketName": "an-example-bucket-name", - "key": "...", - "acl": "" - }, - "eventType": "AwsApiCall", - "responseElements": {}, - "awsRegion": "...", - "eventName": "PutObjectAcl", - "readOnly": false, - "userIdentity": { - "principalId": "...", - "accessKeyId": "...", - "sessionContext": { - "sessionIssuer": { - "userName": "roleName", - "type": "Role", - "principalId": "...", - "arn": "...", - "accountId": "12345" - }, - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "..." - } + "Owner":{ + "ID":"..." }, - "type": "...", - "arn": "...", - "accountId": "12345" + "xmlns":"http://s3.amazonaws.com/doc/2006-03-01/" + }, + "acl":"", + "bucketName":"an-example-bucket-name", + "key":"..." + }, + "resources":[ + { + "ARN":"...", + "type":"..." }, - "eventSource": "...", - "requestID": "...", - "userAgent": "...", - "sourceIPAddress": "...", - "resources": [ - { - "type": "...", - "ARN": "..." + { + "ARN":"...", + "accountId":"12345", + "type":"..." + } + ], + "responseElements":{}, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"..." }, - { - "type": "...", - "ARN": "...", - "accountId": "12345" + "sessionIssuer":{ + "accountId":"12345", + "arn":"...", + "principalId":"...", + "type":"Role", + "userName":"roleName" } - ], - "recipientAccountId": "12345" + }, + "type":"..." } }, - "description": "A PutObjectAcl call without use of AllUsers or AuthenticatedUsers should not create an alert.", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "log": "cloudwatch:events", - "trigger_rules": [] + "detail-type":"...", + "id":"123", + "region":"...", + "resources":[], + "source":"...", + "time":"...", + "version":"0" }, - { - "data": { - "account": 12345, - "region": "...", - "detail-type": "...", - "source": "...", - "version": "0", - "time": "...", - "id": "123", - "resources": [], - "detail": { - "eventVersion": "...", - "eventID": "...", - "eventTime": "...", - "additionalEventData": { - "x_amz_id_2": "..." - }, - "requestParameters": { - "AccessControlPolicy": { - "Owner": { - "ID": "..." - }, - "xmlns": "http://s3.amazonaws.com/doc/2006-03-01/", - "AccessControlList": { - "Grant": [ - { - "Grantee": { - "xsi:type": "Group", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "URI": "http://acs.amazonaws.com/groups/global/AllUsers" - }, - "permission": "READ" - } - ] - } + "description":"A PutObjectAcl call without use of AllUsers or AuthenticatedUsers should not create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + }, + { + "data":{ + "account":12345, + "detail":{ + "additionalEventData":{ + "x_amz_id_2":"..." + }, + "awsRegion":"...", + "eventID":"...", + "eventName":"PutObjectAcl", + "eventSource":"...", + "eventTime":"...", + "eventType":"...", + "eventVersion":"...", + "readOnly":false, + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "AccessControlPolicy":{ + "AccessControlList":{ + "Grant":[ + { + "Grantee":{ + "URI":"http://acs.amazonaws.com/groups/global/AllUsers", + "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + "xsi:type":"Group" + }, + "permission":"READ" + } + ] }, - "bucketName": "...", - "key": "...", - "acl": "" - }, - "eventType": "...", - "responseElements": {}, - "awsRegion": "...", - "eventName": "PutObjectAcl", - "readOnly": false, - "userIdentity": { - "principalId": "...:...", - "accessKeyId": "...", - "sessionContext": { - "sessionIssuer": { - "userName": "...", - "type": "...", - "principalId": "...", - "arn": "...", - "accountId": "12345" - }, - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "..." - } + "Owner":{ + "ID":"..." }, - "type": "...", - "arn": "...", - "accountId": "12345" + "xmlns":"http://s3.amazonaws.com/doc/2006-03-01/" }, - "eventSource": "...", - "requestID": "...", - "userAgent": "...", - "sourceIPAddress": "...", - "resources": [ - { - "type": "...", - "ARN": "..." + "acl":"", + "bucketName":"...", + "key":"..." + }, + "resources":[ + { + "ARN":"...", + "type":"..." + }, + { + "ARN":"...", + "accountId":"12345", + "type":"..." + } + ], + "responseElements":{}, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...:...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"..." }, - { - "type": "...", - "ARN": "...", - "accountId": "12345" + "sessionIssuer":{ + "accountId":"12345", + "arn":"...", + "principalId":"...", + "type":"...", + "userName":"..." } - ], - "recipientAccountId": "12345" + }, + "type":"..." } }, - "description": "A PutObjectAcl call from the AWS Console with AllUsers or AuthenticatedUsers should create an alert.", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "log": "cloudwatch:events", - "trigger_rules": [ - "cloudtrail_put_object_acl_public" - ] + "detail-type":"...", + "id":"123", + "region":"...", + "resources":[], + "source":"...", + "time":"...", + "version":"0" }, - { - "data": { - "account": 12345, - "region": "...", - "detail-type": "...", - "source": "...", - "version": "0", - "time": "...", - "id": "123", - "resources": [], - "detail": { - "eventVersion": "...", - "eventId": "...", - "userIdentity": { - "accountId": "12345", - "sessionContext": { - "sessionIssuer": { - "accountId": "12345", - "principalId": "...", - "type": "...", - "arn": "...", - "username": "..." - }, - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "..." - } - }, - "principalId": "...", - "type": "...", - "arn": "...", - "accessKeyId": "..." + "description":"A PutObjectAcl call from the AWS Console with AllUsers or AuthenticatedUsers should create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "cloudtrail_put_object_acl_public" + ] + }, + { + "data":{ + "account":12345, + "detail":{ + "additionalEventData":{ + "x_amz_id_2":"..." + }, + "awsRegion":"...", + "eventId":"...", + "eventName":"PutObjectAcl", + "eventSource":"...", + "eventTime":"...", + "eventType":"...", + "eventVersion":"...", + "readOnly":false, + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "AccessControlList":{ + "x_amz_grant_read":"uri=http://acs.amazonaws.com/groups/global/AllUsers" }, - "sourceIPAddress": "...", - "userAgent": [ - "..." - ], - "resources": [ - { - "type": "...", - "ARN": "..." - }, - { - "accountId": "12345", - "type": "...", - "ARN": "..." - } - ], - "eventTime": "...", - "recipientAccountId": "12345", - "eventSource": "...", - "eventName": "PutObjectAcl", - "additionalEventData": { - "x_amz_id_2": "..." + "acl":"", + "bucketName":"...", + "key":"..." + }, + "resources":[ + { + "ARN":"...", + "type":"..." }, - "readOnly": false, - "requestID": "...", - "requestParameters": { - "AccessControlList": { - "x_amz_grant_read": "uri=http://acs.amazonaws.com/groups/global/AllUsers" + { + "ARN":"...", + "accountId":"12345", + "type":"..." + } + ], + "responseElements":{}, + "sourceIPAddress":"...", + "userAgent":[ + "..." + ], + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"..." }, - "bucketName": "...", - "acl": "", - "key": "..." + "sessionIssuer":{ + "accountId":"12345", + "arn":"...", + "principalId":"...", + "type":"...", + "username":"..." + } }, - "eventType": "...", - "responseElements": {}, - "awsRegion": "..." + "type":"..." } }, - "description": "A PutObjectAcl call via the S3 API with AllUsers or AuthenticatedUsers should create an alert.", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "log": "cloudwatch:events", - "trigger_rules": [ - "cloudtrail_put_object_acl_public" - ] + "detail-type":"...", + "id":"123", + "region":"...", + "resources":[], + "source":"...", + "time":"...", + "version":"0" }, - { - "data": { - "account": 12345, - "region": "...", - "detail-type": "...", - "source": "...", - "version": "0", - "time": "...", - "id": "123", - "resources": [], - "detail": { - "eventVersion": "...", - "eventId": "...", - "userIdentity": { - "accountId": "12345", - "sessionContext": { - "sessionIssuer": { - "accountId": "12345", - "principalId": "...", - "type": "...", - "arn": "...", - "username": "..." - }, - "attributes": { - "creationDate": "...", - "mfaAuthenticated": "..." - } - }, - "principalId": "...", - "type": "...", - "arn": "...", - "accessKeyId": "..." + "description":"A PutObjectAcl call via the S3 API with AllUsers or AuthenticatedUsers should create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "cloudtrail_put_object_acl_public" + ] + }, + { + "data":{ + "account":12345, + "detail":{ + "additionalEventData":{ + "x_amz_id_2":"..." + }, + "awsRegion":"...", + "eventId":"...", + "eventName":"PutObjectAcl", + "eventSource":"...", + "eventTime":"...", + "eventType":"...", + "eventVersion":"...", + "readOnly":false, + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "AccessControlList":{ + "x_amz_grant_read":"uri=http://acs.amazonaws.com/groups/global/AllUsers" }, - "sourceIPAddress": "...", - "userAgent": [ - "..." - ], - "resources": [ - { - "type": "...", - "ARN": "..." - }, - { - "accountId": "12345", - "type": "...", - "ARN": "..." - } - ], - "eventTime": "...", - "recipientAccountId": "12345", - "eventSource": "...", - "eventName": "PutObjectAcl", - "additionalEventData": { - "x_amz_id_2": "..." + "acl":"", + "bucketName":"example-bucket-to-ignore", + "key":"..." + }, + "resources":[ + { + "ARN":"...", + "type":"..." }, - "readOnly": false, - "requestID": "...", - "requestParameters": { - "AccessControlList": { - "x_amz_grant_read": "uri=http://acs.amazonaws.com/groups/global/AllUsers" + { + "ARN":"...", + "accountId":"12345", + "type":"..." + } + ], + "responseElements":{}, + "sourceIPAddress":"...", + "userAgent":[ + "..." + ], + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"..." }, - "bucketName": "example-bucket-to-ignore", - "acl": "", - "key": "..." + "sessionIssuer":{ + "accountId":"12345", + "arn":"...", + "principalId":"...", + "type":"...", + "username":"..." + } }, - "eventType": "...", - "responseElements": {}, - "awsRegion": "..." + "type":"..." } }, - "description": "A PutObjectAcl call via the S3 API with AllUsers or AuthenticatedUsers for a whitelisted bucket should not create an alert.", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "log": "cloudwatch:events", - "trigger_rules": [ - ] - } - ] -} + "detail-type":"...", + "id":"123", + "region":"...", + "resources":[], + "source":"...", + "time":"...", + "version":"0" + }, + "description":"A PutObjectAcl call via the S3 API with AllUsers or AuthenticatedUsers for a whitelisted bucket should not create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_root_account_usage.json b/tests/integration/rules/cloudtrail/cloudtrail_root_account_usage.json index 3b718d434..e95286350 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_root_account_usage.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_root_account_usage.json @@ -1,99 +1,98 @@ -{ - "records": [ - { - "data": { - "account": 12345, - "region": "123456123456", - "detail-type": "...", - "source": "1.1.1.2", - "version": "1.05", - "time": "...", - "id": "12345", - "resources": { - "test": "..." - }, - "detail": { - "eventVersion": "...", - "userIdentity": { - "type": "Root", - "principalId": "12345", - "arn": "arn:aws:iam::12345:root", - "accountId": "12345" - }, - "eventTime": "...", - "eventSource": "...", - "eventName": "ConsoleLogin", - "awsRegion": "...", - "sourceIPAddress": "...", - "userAgent": "...", - "requestParameters": null, - "responseElements": { - "ConsoleLogin": "..." - }, - "additionalEventData": { - "LoginTo": "...", - "MobileVersion": "No", - "MFAUsed": "Yes" - }, - "eventID": "...", - "eventType": "AwsConsoleSignIn", - "recipientAccountId": "12345" - } - }, - "description": "Use of the AWS 'Root' account will create an alert.", - "log": "cloudwatch:events", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": ["cloudtrail_root_account_usage"] +[ + { + "data":{ + "account":12345, + "detail":{ + "additionalEventData":{ + "LoginTo":"...", + "MFAUsed":"Yes", + "MobileVersion":"No" }, - { - "data": { - "account": 12345, - "region": "123456123456", - "detail-type": "...", - "source": "...", - "version": "1.05", - "time": "...", - "id": "12345", - "resources": { - "test": "..." - }, - "detail": { - "eventVersion": "...", - "userIdentity": { - "type": "Root", - "principalId": "...", - "arn": "...", - "accountId": "12345", - "userName": "...", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "true", - "creationDate": "..." - } - }, - "invokedBy": "signin.amazonaws.com" - }, - "eventTime": "...", - "eventSource": "...", - "eventName": "...", - "awsRegion": "...", - "sourceIPAddress": "AWS Internal", - "userAgent": "...", - "requestParameters": { - }, - "responseElements": null, - "requestID": "...", - "eventID": "...", - "eventType": "AwsApiCall", - "recipientAccountId": "12345" - } - }, - "description": "AWS 'Root' account activity initiated automatically by an AWS service on your behalf will not create an alert.", - "log": "cloudwatch:events", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] + "awsRegion":"...", + "eventID":"...", + "eventName":"ConsoleLogin", + "eventSource":"...", + "eventTime":"...", + "eventType":"AwsConsoleSignIn", + "eventVersion":"...", + "recipientAccountId":"12345", + "requestParameters":null, + "responseElements":{ + "ConsoleLogin":"..." + }, + "sourceIPAddress":"...", + "userAgent":"...", + "userIdentity":{ + "accountId":"12345", + "arn":"arn:aws:iam::12345:root", + "principalId":"12345", + "type":"Root" } + }, + "detail-type":"...", + "id":"12345", + "region":"123456123456", + "resources":{ + "test":"..." + }, + "source":"1.1.1.2", + "time":"...", + "version":"1.05" + }, + "description":"Use of the AWS 'Root' account will create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "cloudtrail_root_account_usage" ] -} + }, + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"...", + "eventID":"...", + "eventName":"...", + "eventSource":"...", + "eventTime":"...", + "eventType":"AwsApiCall", + "eventVersion":"...", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{}, + "responseElements":null, + "sourceIPAddress":"AWS Internal", + "userAgent":"...", + "userIdentity":{ + "accountId":"12345", + "arn":"...", + "invokedBy":"signin.amazonaws.com", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"...", + "mfaAuthenticated":"true" + } + }, + "type":"Root", + "userName":"..." + } + }, + "detail-type":"...", + "id":"12345", + "region":"123456123456", + "resources":{ + "test":"..." + }, + "source":"...", + "time":"...", + "version":"1.05" + }, + "description":"AWS 'Root' account activity initiated automatically by an AWS service on your behalf will not create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/cloudtrail/cloudtrail_security_group_ingress_anywhere.json b/tests/integration/rules/cloudtrail/cloudtrail_security_group_ingress_anywhere.json index 74b9715b1..485f68a71 100644 --- a/tests/integration/rules/cloudtrail/cloudtrail_security_group_ingress_anywhere.json +++ b/tests/integration/rules/cloudtrail/cloudtrail_security_group_ingress_anywhere.json @@ -1,153 +1,150 @@ -{ - "records": [ - { - "data": { - "account": 12345, - "region": "...", - "detail-type": "...", - "source": "...", - "version": "1.05", - "time": "...", - "id": "12345", - "resources": { - "test": "..." - }, - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "IAMUser", - "principalId": "...", - "arn": "...", - "accountId": "12345", - "accessKeyId": "...", - "userName": "...", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "false", - "creationDate": "2017-10-11T16:57:31Z" - } +[ + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"...", + "eventID":"...", + "eventName":"AuthorizeSecurityGroupIngress", + "eventSource":"ec2.amazonaws.com", + "eventTime":"2017-10-11T17:02:32Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "groupId":"...", + "ipPermissions":{ + "items":[ + { + "groups":{}, + "ipProtocol":"-1", + "ipRanges":{ + "items":[ + { + "cidrIp":"0.0.0.0/0", + "description":"..." } + ] }, - "eventTime": "2017-10-11T17:02:32Z", - "eventSource": "ec2.amazonaws.com", - "eventName": "AuthorizeSecurityGroupIngress", - "awsRegion": "...", - "sourceIPAddress": "...", - "userAgent": "console.ec2.amazonaws.com", - "requestParameters": { - "groupId": "...", - "ipPermissions": { - "items": [ - { - "ipProtocol": "-1", - "groups": {}, - "ipRanges": { - "items": [ - { - "cidrIp": "0.0.0.0/0", - "description": "..." - } - ] - }, - "ipv6Ranges": { - "items": [ - { - "cidrIpv6": "::/0", - "description": "..." - } - ] - }, - "prefixListIds": {} - } - ] + "ipv6Ranges":{ + "items":[ + { + "cidrIpv6":"::/0", + "description":"..." } + ] }, - "responseElements": { - "_return": true - }, - "requestID": "...", - "eventID": "...", - "eventType": "AwsApiCall", - "recipientAccountId": "12345" + "prefixListIds":{} } - }, - "description": "A Security Group that allows ingress from anywhere on the internet should create an alert.", - "log": "cloudwatch:events", - "trigger_rules": [ - "cloudtrail_security_group_ingress_anywhere" - ], - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis" + ] + } }, - { - "data": { - "account": 12345, - "region": "...", - "detail-type": "...", - "source": "...", - "version": "1.05", - "time": "...", - "id": "12345", - "resources": { - "test": "..." - }, - "detail": { - "eventVersion": "1.05", - "userIdentity": { - "type": "IAMUser", - "principalId": "...", - "arn": "...", - "accountId": "12345", - "accessKeyId": "...", - "userName": "...", - "sessionContext": { - "attributes": { - "mfaAuthenticated": "false", - "creationDate": "2017-10-11T16:57:31Z" - } - } - }, - "eventTime": "2017-10-11T17:02:32Z", - "eventSource": "ec2.amazonaws.com", - "eventName": "AuthorizeSecurityGroupIngress", - "awsRegion": "...", - "sourceIPAddress": "...", - "userAgent": "console.ec2.amazonaws.com", - "requestParameters": { - "groupId": "...", - "ipPermissions": { - "items": [ - { - "ipProtocol": "-1", - "groups": {}, - "ipRanges": { - "items": [ - { - "cidrIp": "10.0.0.0/8", - "description": "..." - } - ] - }, - "prefixListIds": {} - } - ] + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"console.ec2.amazonaws.com", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"2017-10-11T16:57:31Z", + "mfaAuthenticated":"false" + } + }, + "type":"IAMUser", + "userName":"..." + } + }, + "detail-type":"...", + "id":"12345", + "region":"...", + "resources":{ + "test":"..." + }, + "source":"...", + "time":"...", + "version":"1.05" + }, + "description":"A Security Group that allows ingress from anywhere on the internet should create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "cloudtrail_security_group_ingress_anywhere" + ] + }, + { + "data":{ + "account":12345, + "detail":{ + "awsRegion":"...", + "eventID":"...", + "eventName":"AuthorizeSecurityGroupIngress", + "eventSource":"ec2.amazonaws.com", + "eventTime":"2017-10-11T17:02:32Z", + "eventType":"AwsApiCall", + "eventVersion":"1.05", + "recipientAccountId":"12345", + "requestID":"...", + "requestParameters":{ + "groupId":"...", + "ipPermissions":{ + "items":[ + { + "groups":{}, + "ipProtocol":"-1", + "ipRanges":{ + "items":[ + { + "cidrIp":"10.0.0.0/8", + "description":"..." } + ] }, - "responseElements": { - "_return": true - }, - "requestID": "...", - "eventID": "...", - "eventType": "AwsApiCall", - "recipientAccountId": "12345" + "prefixListIds":{} } - }, - "description": "A Security Group that allows ingress from an RFC1918 IP should not create an alert.", - "log": "cloudwatch:events", - "trigger_rules": [ - ], - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis" + ] + } + }, + "responseElements":{ + "_return":true + }, + "sourceIPAddress":"...", + "userAgent":"console.ec2.amazonaws.com", + "userIdentity":{ + "accessKeyId":"...", + "accountId":"12345", + "arn":"...", + "principalId":"...", + "sessionContext":{ + "attributes":{ + "creationDate":"2017-10-11T16:57:31Z", + "mfaAuthenticated":"false" + } + }, + "type":"IAMUser", + "userName":"..." } - ] -} + }, + "detail-type":"...", + "id":"12345", + "region":"...", + "resources":{ + "test":"..." + }, + "source":"...", + "time":"...", + "version":"1.05" + }, + "description":"A Security Group that allows ingress from an RFC1918 IP should not create an alert.", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/duo/duo_bypass_code_create_non_auto_generated.json b/tests/integration/rules/duo/duo_bypass_code_create_non_auto_generated.json index 1fdf99fee..56f1ed3b5 100644 --- a/tests/integration/rules/duo/duo_bypass_code_create_non_auto_generated.json +++ b/tests/integration/rules/duo/duo_bypass_code_create_non_auto_generated.json @@ -1,34 +1,32 @@ -{ - "records": [ - { - "data": { - "action": "bypass_create", - "description": "{\"bypass\": \"\", \"count\": 1, \"auto_generated\": false, \"valid_secs\": 10, \"remaining_uses\": 100, \"user_id\": \"...\"}", - "object": "...", - "timestamp": "1234567890", - "username": "..." - }, - "description": "A DUO bypass code that was hand crafted should create an alert.", - "log": "duo:administrator", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_admin_sm-app-name_app", - "trigger_rules": [ - "duo_bypass_code_create_non_auto_generated" - ] +[ + { + "data":{ + "action":"bypass_create", + "description":"{\"bypass\": \"\", \"count\": 1, \"auto_generated\": false, \"valid_secs\": 10, \"remaining_uses\": 100, \"user_id\": \"...\"}", + "object":"...", + "timestamp":"1234567890", + "username":"..." }, - { - "data": { - "action": "bypass_create", - "description": "{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 10, \"remaining_uses\": 100, \"user_id\": \"...\"}", - "object": "...", - "timestamp": "1234567890", - "username": "..." - }, - "description": "A DUO bypass code that was auto generated should not create an alert.", - "log": "duo:administrator", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_admin_sm-app-name_app", - "trigger_rules": [] - } - ] -} \ No newline at end of file + "description":"A DUO bypass code that was hand crafted should create an alert.", + "log":"duo:administrator", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_admin_sm-app-name_app", + "trigger_rules":[ + "duo_bypass_code_create_non_auto_generated" + ] + }, + { + "data":{ + "action":"bypass_create", + "description":"{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 10, \"remaining_uses\": 100, \"user_id\": \"...\"}", + "object":"...", + "timestamp":"1234567890", + "username":"..." + }, + "description":"A DUO bypass code that was auto generated should not create an alert.", + "log":"duo:administrator", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_admin_sm-app-name_app", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/duo/duo_bypass_code_create_non_expiring.json b/tests/integration/rules/duo/duo_bypass_code_create_non_expiring.json index b55606306..0e0ab2e81 100644 --- a/tests/integration/rules/duo/duo_bypass_code_create_non_expiring.json +++ b/tests/integration/rules/duo/duo_bypass_code_create_non_expiring.json @@ -1,35 +1,32 @@ -{ - "records": [ - { - "data": { - "action": "bypass_create", - "description": "{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": null, \"remaining_uses\": 1, \"user_id\": \"...\"}", - "object": "...", - "timestamp": "1234567890", - "username": "..." - }, - "description": "A DUO bypass code that has no expiration should create an alert.", - "log": "duo:administrator", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_admin_sm-app-name_app", - "trigger_rules": [ - "duo_bypass_code_create_non_expiring" - ] +[ + { + "data":{ + "action":"bypass_create", + "description":"{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": null, \"remaining_uses\": 1, \"user_id\": \"...\"}", + "object":"...", + "timestamp":"1234567890", + "username":"..." }, - { - "data": { - "action": "bypass_create", - "description": "{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 60, \"remaining_uses\": 1, \"user_id\": \"...\"}", - "object": "...", - "timestamp": "1234567890", - "username": "..." - }, - "description": "A DUO bypass code that has an expiration should not create an alert.", - "log": "duo:administrator", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_admin_sm-app-name_app", - "trigger_rules": [ - ] - } - ] -} + "description":"A DUO bypass code that has no expiration should create an alert.", + "log":"duo:administrator", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_admin_sm-app-name_app", + "trigger_rules":[ + "duo_bypass_code_create_non_expiring" + ] + }, + { + "data":{ + "action":"bypass_create", + "description":"{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 60, \"remaining_uses\": 1, \"user_id\": \"...\"}", + "object":"...", + "timestamp":"1234567890", + "username":"..." + }, + "description":"A DUO bypass code that has an expiration should not create an alert.", + "log":"duo:administrator", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_admin_sm-app-name_app", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/duo/duo_bypass_code_create_unlimited_use.json b/tests/integration/rules/duo/duo_bypass_code_create_unlimited_use.json index 595443aec..1359475da 100644 --- a/tests/integration/rules/duo/duo_bypass_code_create_unlimited_use.json +++ b/tests/integration/rules/duo/duo_bypass_code_create_unlimited_use.json @@ -1,35 +1,32 @@ -{ - "records": [ - { - "data": { - "action": "bypass_create", - "description": "{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 10, \"remaining_uses\": null, \"user_id\": \"...\"}", - "object": "...", - "timestamp": "1234567890", - "username": "..." - }, - "description": "A DUO bypass code that has unlimited use should create an alert.", - "log": "duo:administrator", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_admin_sm-app-name_app", - "trigger_rules": [ - "duo_bypass_code_create_unlimited_use" - ] +[ + { + "data":{ + "action":"bypass_create", + "description":"{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 10, \"remaining_uses\": null, \"user_id\": \"...\"}", + "object":"...", + "timestamp":"1234567890", + "username":"..." }, - { - "data": { - "action": "bypass_create", - "description": "{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 60, \"remaining_uses\": 1, \"user_id\": \"...\"}", - "object": "...", - "timestamp": "1234567890", - "username": "..." - }, - "description": "A DUO bypass code that has finite remaining uses should not create an alert.", - "log": "duo:administrator", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_admin_sm-app-name_app", - "trigger_rules": [ - ] - } - ] -} + "description":"A DUO bypass code that has unlimited use should create an alert.", + "log":"duo:administrator", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_admin_sm-app-name_app", + "trigger_rules":[ + "duo_bypass_code_create_unlimited_use" + ] + }, + { + "data":{ + "action":"bypass_create", + "description":"{\"bypass\": \"\", \"count\": 1, \"auto_generated\": true, \"valid_secs\": 60, \"remaining_uses\": 1, \"user_id\": \"...\"}", + "object":"...", + "timestamp":"1234567890", + "username":"..." + }, + "description":"A DUO bypass code that has finite remaining uses should not create an alert.", + "log":"duo:administrator", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_admin_sm-app-name_app", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/duo/duo_fraud.json b/tests/integration/rules/duo/duo_fraud.json index 664ca60d1..8e31a67fc 100644 --- a/tests/integration/rules/duo/duo_fraud.json +++ b/tests/integration/rules/duo/duo_fraud.json @@ -1,68 +1,68 @@ -{ - "records": [ - { - "data": { - "username": "user.name@email.com", - "access_device": { - "flash_version": "27.0.0.0", - "java_version": "uninstalled", - "os_version": "10.12.6", - "browser_version": "60.0.0000.80", - "trusted_endpoint_status": "not trusted", - "os": "Mac OS X", - "browser": "Chrome" - }, - "timestamp": 1505316499, - "new_enrollment": false, - "ip": "12.123.123.12", - "integration": "Test Integration", - "reason": "", - "location": { - "city": "Place", - "state": "State", - "country": "US" - }, - "factor": "Duo Push", - "device": "555-123-4567", - "result": "FRAUD" +[ + { + "data":{ + "access_device":{ + "browser":"Chrome", + "browser_version":"60.0.0000.80", + "flash_version":"27.0.0.0", + "java_version":"uninstalled", + "os":"Mac OS X", + "os_version":"10.12.6", + "trusted_endpoint_status":"not trusted" }, - "description": "Duo authentication log marked as fraud that will create an alert", - "log": "duo:authentication", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_auth_sm-app-name_app", - "trigger_rules": ["duo_fraud"] + "device":"555-123-4567", + "factor":"Duo Push", + "integration":"Test Integration", + "ip":"12.123.123.12", + "location":{ + "city":"Place", + "country":"US", + "state":"State" + }, + "new_enrollment":false, + "reason":"", + "result":"FRAUD", + "timestamp":1505316499, + "username":"user.name@email.com" }, - { - "data": { - "username": "user.name@email.com", - "access_device": { - "flash_version": "27.0.0.0", - "java_version": "uninstalled", - "os_version": "10.12.6", - "browser_version": "60.0.0000.80", - "trusted_endpoint_status": "not trusted", - "os": "Mac OS X", - "browser": "Chrome" - }, - "timestamp": 1505316499, - "new_enrollment": false, - "ip": "12.123.123.12", - "integration": "Test Integration", - "reason": "", - "location": { - "city": "Place", - "state": "State", - "country": "US" - }, - "factor": "Duo Push", - "device": "555-123-5678", - "result": "SUCCESS" + "description":"Duo authentication log marked as fraud that will create an alert", + "log":"duo:authentication", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_auth_sm-app-name_app", + "trigger_rules":[ + "duo_fraud" + ] + }, + { + "data":{ + "access_device":{ + "browser":"Chrome", + "browser_version":"60.0.0000.80", + "flash_version":"27.0.0.0", + "java_version":"uninstalled", + "os":"Mac OS X", + "os_version":"10.12.6", + "trusted_endpoint_status":"not trusted" + }, + "device":"555-123-5678", + "factor":"Duo Push", + "integration":"Test Integration", + "ip":"12.123.123.12", + "location":{ + "city":"Place", + "country":"US", + "state":"State" }, - "description": "Duo authentication log marked as success that will not create an alert", - "log": "duo:authentication", - "service": "stream_alert_app", - "source": "prefix_cluster_duo_auth_sm-app-name_app", - "trigger_rules": [] - } - ] -} \ No newline at end of file + "new_enrollment":false, + "reason":"", + "result":"SUCCESS", + "timestamp":1505316499, + "username":"user.name@email.com" + }, + "description":"Duo authentication log marked as success that will not create an alert", + "log":"duo:authentication", + "service":"stream_alert_app", + "source":"prefix_cluster_duo_auth_sm-app-name_app", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_disable_dismiss_stale_pull_request_approvals.json b/tests/integration/rules/github/github_disable_dismiss_stale_pull_request_approvals.json index 3fb1a0d54..39e54ccd5 100644 --- a/tests/integration/rules/github/github_disable_dismiss_stale_pull_request_approvals.json +++ b/tests/integration/rules/github/github_disable_dismiss_stale_pull_request_approvals.json @@ -1,48 +1,43 @@ -{ - "records": [ - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"protected_branch.dismiss_stale_reviews\",\"data\":{\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit" - }, - "description": "Disabling 'Dismiss stale pull request approvals' should trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_disable_dismiss_stale_pull_request_approvals" - ] +[ + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"protected_branch.dismiss_stale_reviews\",\"data\":{\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." }, - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"something.different\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit", - "pid": "1234" - }, - "description": "An unrelated Github log should not trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - ] - } - ] -} + "description":"Disabling 'Dismiss stale pull request approvals' should trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_disable_dismiss_stale_pull_request_approvals" + ] + }, + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"something.different\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "pid":"1234", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." + }, + "description":"An unrelated Github log should not trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_disable_protect_this_branch.json b/tests/integration/rules/github/github_disable_protect_this_branch.json index 3196804ef..2d1c756e1 100644 --- a/tests/integration/rules/github/github_disable_protect_this_branch.json +++ b/tests/integration/rules/github/github_disable_protect_this_branch.json @@ -1,48 +1,43 @@ -{ - "records": [ - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"protected_branch.destroy\",\"data\":{\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit" - }, - "description": "Disabling Github branch protections should trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_disable_protect_this_branch" - ] +[ + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"protected_branch.destroy\",\"data\":{\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." }, - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"something.different\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit", - "pid": "1234" - }, - "description": "An unrelated Github log should not trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - ] - } - ] -} + "description":"Disabling Github branch protections should trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_disable_protect_this_branch" + ] + }, + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"something.different\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "pid":"1234", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." + }, + "description":"An unrelated Github log should not trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_disable_required_pull_request_reviews.json b/tests/integration/rules/github/github_disable_required_pull_request_reviews.json index 766621003..866d1cfed 100644 --- a/tests/integration/rules/github/github_disable_required_pull_request_reviews.json +++ b/tests/integration/rules/github/github_disable_required_pull_request_reviews.json @@ -1,48 +1,43 @@ -{ - "records": [ - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"protected_branch.dismissal_restricted_users_teams\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit" - }, - "description": "Disabling Required Pull Request reviews should trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_disable_required_pull_request_reviews" - ] +[ + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"protected_branch.dismissal_restricted_users_teams\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." }, - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"something.different\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit", - "pid": "1234" - }, - "description": "An unrelated Github log should not trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - ] - } - ] -} + "description":"Disabling Required Pull Request reviews should trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_disable_required_pull_request_reviews" + ] + }, + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"something.different\",\"data\":{\"authorized_actors_only\":true,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "pid":"1234", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." + }, + "description":"An unrelated Github log should not trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_disable_required_status_checks.json b/tests/integration/rules/github/github_disable_required_status_checks.json index bff9bba75..ef87ea249 100644 --- a/tests/integration/rules/github/github_disable_required_status_checks.json +++ b/tests/integration/rules/github/github_disable_required_status_checks.json @@ -1,51 +1,48 @@ -{ - "records": [ - { - "data": { - "@timestamp": "2017-09-06T03:49:31.600Z", - "@version": 1, - "host": "192.168.1.1", - "logsource": "...", - "message": "<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"protected_branch.update_required_status_checks_enforcement_level\",\"data\":{\"required_status_checks_enforcement_level\":0,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", - "pid": 0, - "port": 123, - "program": "github_audit", - "received_at": "...", - "tags": [ - "..." - ], - "timestamp": "Sep 5 20:49:31" - }, - "description": "Disabling 'required status checks' on Github should create an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_disable_required_status_checks" - ] - }, - { - "data": { - "@timestamp": "2017-09-06T03:49:31.600Z", - "@version": 1, - "host": "192.168.1.1", - "logsource": "...", - "message": "<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"protected_branch.update_required_status_checks_enforcement_level\",\"data\":{\"required_status_checks_enforcement_level\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", - "pid": 0, - "port": 123, - "program": "github_audit", - "received_at": "...", - "tags": [ - "..." - ], - "timestamp": "Sep 5 20:49:31" - }, - "description": "Enabling 'required status checks' on Github should not create an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - ] - } +[ + { + "data":{ + "@timestamp":"2017-09-06T03:49:31.600Z", + "@version":1, + "host":"192.168.1.1", + "logsource":"...", + "message":"<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"protected_branch.update_required_status_checks_enforcement_level\",\"data\":{\"required_status_checks_enforcement_level\":0,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", + "pid":0, + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[ + "..." + ], + "timestamp":"Sep 5 20:49:31" + }, + "description":"Disabling 'required status checks' on Github should create an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_disable_required_status_checks" ] -} + }, + { + "data":{ + "@timestamp":"2017-09-06T03:49:31.600Z", + "@version":1, + "host":"192.168.1.1", + "logsource":"...", + "message":"<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"protected_branch.update_required_status_checks_enforcement_level\",\"data\":{\"required_status_checks_enforcement_level\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", + "pid":0, + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[ + "..." + ], + "timestamp":"Sep 5 20:49:31" + }, + "description":"Enabling 'required status checks' on Github should not create an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_disable_two_factor_requirement_org.json b/tests/integration/rules/github/github_disable_two_factor_requirement_org.json index bcdeb3a25..a4071d6f3 100644 --- a/tests/integration/rules/github/github_disable_two_factor_requirement_org.json +++ b/tests/integration/rules/github/github_disable_two_factor_requirement_org.json @@ -1,50 +1,48 @@ -{ - "records": [ - { - "data": { - "@timestamp": "2017-09-06T03:49:31.600Z", - "@version": 1, - "host": "192.168.1.1", - "logsource": "...", - "message": "<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"orgs/two_factor_enforcements#update\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"org.disable_two_factor_requirement\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", - "pid": 0, - "port": 123, - "program": "github_audit", - "received_at": "...", - "tags": [ - "..." - ], - "timestamp": "Sep 5 20:49:31" - }, - "description": "Disabling the 2FA requirement on a Github org should create an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_disable_two_factor_requirement_org" - ] - }, - { - "data": { - "@timestamp": "2017-09-06T03:49:31.600Z", - "@version": 1, - "host": "192.168.1.1", - "logsource": "...", - "message": "<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"orgs/two_factor_enforcements#update\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"org.enable_two_factor_requirement\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", - "pid": 0, - "port": 123, - "program": "github_audit", - "received_at": "...", - "tags": [ - "..." - ], - "timestamp": "Sep 5 20:49:31" - }, - "description": "Enabling the 2FA requirement on a Github org should not create an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] - } +[ + { + "data":{ + "@timestamp":"2017-09-06T03:49:31.600Z", + "@version":1, + "host":"192.168.1.1", + "logsource":"...", + "message":"<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"orgs/two_factor_enforcements#update\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"org.disable_two_factor_requirement\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", + "pid":0, + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[ + "..." + ], + "timestamp":"Sep 5 20:49:31" + }, + "description":"Disabling the 2FA requirement on a Github org should create an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_disable_two_factor_requirement_org" ] -} + }, + { + "data":{ + "@timestamp":"2017-09-06T03:49:31.600Z", + "@version":1, + "host":"192.168.1.1", + "logsource":"...", + "message":"<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"orgs/two_factor_enforcements#update\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"org.enable_two_factor_requirement\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", + "pid":0, + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[ + "..." + ], + "timestamp":"Sep 5 20:49:31" + }, + "description":"Enabling the 2FA requirement on a Github org should not create an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_disable_two_factor_requirement_user.json b/tests/integration/rules/github/github_disable_two_factor_requirement_user.json index 207fbf4ed..7b6b194ea 100644 --- a/tests/integration/rules/github/github_disable_two_factor_requirement_user.json +++ b/tests/integration/rules/github/github_disable_two_factor_requirement_user.json @@ -1,50 +1,48 @@ -{ - "records": [ - { - "data": { - "@timestamp": "2017-09-06T03:49:31.600Z", - "@version": 1, - "host": "192.168.1.1", - "logsource": "...", - "message": "<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"two_factor_authentication.disabled\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", - "pid": 0, - "port": 123, - "program": "github_audit", - "received_at": "...", - "tags": [ - "..." - ], - "timestamp": "Sep 5 20:49:31" - }, - "description": "Disabling 2FA for a Github user should create an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_disable_two_factor_requirement_user" - ] - }, - { - "data": { - "@timestamp": "2017-09-06T03:49:31.600Z", - "@version": 1, - "host": "192.168.1.1", - "logsource": "...", - "message": "<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"two_factor_authentication.enabled\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", - "pid": 0, - "port": 123, - "program": "github_audit", - "received_at": "...", - "tags": [ - "..." - ], - "timestamp": "Sep 5 20:49:31" - }, - "description": "Enabling 2FA for a Github should not create an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] - } +[ + { + "data":{ + "@timestamp":"2017-09-06T03:49:31.600Z", + "@version":1, + "host":"192.168.1.1", + "logsource":"...", + "message":"<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"two_factor_authentication.disabled\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", + "pid":0, + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[ + "..." + ], + "timestamp":"Sep 5 20:49:31" + }, + "description":"Disabling 2FA for a Github user should create an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_disable_two_factor_requirement_user" ] -} + }, + { + "data":{ + "@timestamp":"2017-09-06T03:49:31.600Z", + "@version":1, + "host":"192.168.1.1", + "logsource":"...", + "message":"<190>Sep 5 20:49:31 ... github_audit: {\"actor_ip\":\"...\",\"from\":\"...\",\"actor\":\"...\",\"actor_id\":123,\"created_at\":123,\"org\":\"foobar\",\"org_id\":123,\"action\":\"two_factor_authentication.enabled\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"github@foo/github_enterprise\",\"newsies_dbconn\":\"github@foo/github_enterprise\",\"method\":\"PUT\",\"request_id\":\"...\",\"server_id\":\"...\",\"url\":\"...\",\"actor_session\":123,\"areas_of_responsibility\":[\"orgs\",\"identity\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"region\":\"CA\",\"region_name\":\"California\",\"city\":\"San Francisco\",\"postal_code\":\"12345\",\"location\":{\"lat\":11.1111,\"lon\":-111.1111}},\"_document_id\":\"123\"}}", + "pid":0, + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[ + "..." + ], + "timestamp":"Sep 5 20:49:31" + }, + "description":"Enabling 2FA for a Github should not create an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_oauth_application_create.json b/tests/integration/rules/github/github_oauth_application_create.json index 1d32d8013..2d3ba34e6 100644 --- a/tests/integration/rules/github/github_oauth_application_create.json +++ b/tests/integration/rules/github/github_oauth_application_create.json @@ -1,47 +1,43 @@ -{ - "records": [ - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"oauth_application.create\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"https://git.server.com/...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit" - }, - "description": "An OAuth application was registered and should trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_oauth_application_create" - ] +[ + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"oauth_application.create\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"https://git.server.com/...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." }, - { - "data": { - "message": "<22>May 22 14:10:28 random", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit", - "pid": "1234" - }, - "description": "An unrelated Github log should not trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] - } - ] -} + "description":"An OAuth application was registered and should trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_oauth_application_create" + ] + }, + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<22>May 22 14:10:28 random", + "pid":"1234", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." + }, + "description":"An unrelated Github log should not trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_site_admin_action.json b/tests/integration/rules/github/github_site_admin_action.json index 10a94bd1b..82e28071f 100644 --- a/tests/integration/rules/github/github_site_admin_action.json +++ b/tests/integration/rules/github/github_site_admin_action.json @@ -1,47 +1,43 @@ -{ - "records": [ - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"staff.fake_user\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"https://git.server.com/...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit" - }, - "description": "A site admin action should trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_site_admin_action" - ] +[ + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"...\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"staff.fake_user\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"https://git.server.com/...\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." }, - { - "data": { - "message": "<22>May 22 14:10:28 random", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit", - "pid": "1234" - }, - "description": "An unrelated Github log should not trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] - } - ] -} + "description":"A site admin action should trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_site_admin_action" + ] + }, + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<22>May 22 14:10:28 random", + "pid":"1234", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." + }, + "description":"An unrelated Github log should not trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/github/github_site_admin_user_promotion.json b/tests/integration/rules/github/github_site_admin_user_promotion.json index 547f1bce4..55f842983 100644 --- a/tests/integration/rules/github/github_site_admin_user_promotion.json +++ b/tests/integration/rules/github/github_site_admin_user_promotion.json @@ -1,47 +1,43 @@ -{ - "records": [ - { - "data": { - "message": "<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"foobar/users#set_site_admin\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"user.promote\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"https://git.server.com/foobar/users/foobar/set_site_admin\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit" - }, - "description": "A Github user promoted to site admin should trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "github_site_admin_user_promotion" - ] +[ + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<190>May 22 12:05:54 foobar github_audit: {\"actor_ip\":\"1.1.1.1\",\"from\":\"foobar/users#set_site_admin\",\"actor\":\"bob\",\"actor_id\":123,\"created_at\":1495479954312,\"org_id\":[1,2013],\"user\":\"sally\",\"user_id\":1234,\"action\":\"user.promote\",\"data\":{\"current_tenant_id\":1,\"tenant_fail_safe\":false,\"dbconn\":\"foo@bar/github_enterprise\",\"newsies_dbconn\":\"foo@bar/github_enterprise\",\"method\":\"POST\",\"request_id\":\"00000000-0000-0000-0000-000000000000\",\"server_id\":\"00000000-0000-0000-0000-000000000000\",\"url\":\"https://git.server.com/foobar/users/foobar/set_site_admin\",\"actor_session\":123,\"areas_of_responsibility\":[\"foo\",\"bar\",\"baz\"],\"actor_location\":{\"country_code\":\"US\",\"country_name\":\"United States\",\"location\":{\"lat\":123.0,\"lon\":-123.0}},\"reason\":\"testing\",\"_document_id\":\"0000000000000000000000\"}}", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." }, - { - "data": { - "message": "<22>May 22 14:10:28 random", - "@version": "1", - "@timestamp": "...", - "host": "10.1.1.1", - "port": 123, - "tags": [ - ], - "received_at": "...", - "timestamp": "...", - "logsource": "...", - "program": "github_audit", - "pid": "1234" - }, - "description": "An unrelated Github log should not trigger an alert.", - "log": "ghe:general", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] - } - ] -} + "description":"A Github user promoted to site admin should trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "github_site_admin_user_promotion" + ] + }, + { + "data":{ + "@timestamp":"...", + "@version":"1", + "host":"10.1.1.1", + "logsource":"...", + "message":"<22>May 22 14:10:28 random", + "pid":"1234", + "port":123, + "program":"github_audit", + "received_at":"...", + "tags":[], + "timestamp":"..." + }, + "description":"An unrelated Github log should not trigger an alert.", + "log":"ghe:general", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + } +] \ No newline at end of file diff --git a/tests/integration/rules/gsuite/gsuite_admin.json b/tests/integration/rules/gsuite/gsuite_admin.json index fa7798540..98d57fb6e 100644 --- a/tests/integration/rules/gsuite/gsuite_admin.json +++ b/tests/integration/rules/gsuite/gsuite_admin.json @@ -1,43 +1,41 @@ -{ - "records": [ - { - "data": { - "actor": { - "callerType": "USER", - "email": "liz@example.com", - "key": "consumer key of requestor in OAuth 2LO requests", - "profileId": "user's unique G Suite profile ID" - }, - "events": [ - { - "name": "CHANGE_GROUP_SETTING", - "parameters": [ - { - "boolValue": "boolean value of parameter", - "intValue": "integer value of parameter", - "name": "SETTING_NAME", - "value": "WHO_CAN_JOIN" - } - ], - "type": "GROUP_SETTINGS" - } - ], - "id": { - "applicationName": "admin", - "customerId": "C03az79cb", - "time": "2011-06-17T15:39:18.460Z", - "uniqueQualifier": "-1234567890987654321" - }, - "ipAddress": "user's IP address", - "kind": "audit#activity", - "ownerDomain": "example.com" +[ + { + "data":{ + "actor":{ + "callerType":"USER", + "email":"liz@example.com", + "key":"consumer key of requestor in OAuth 2LO requests", + "profileId":"user's unique G Suite profile ID" }, - "description": "G Suite Admin Report Log exmaple (validation only)", - "log": "gsuite:reports", - "service": "stream_alert_app", - "source": "prefix_cluster_gsuite_admin_sm-app-name_app", - "trigger_rules": [], - "validate_schema_only": true - } - ] -} \ No newline at end of file + "events":[ + { + "name":"CHANGE_GROUP_SETTING", + "parameters":[ + { + "boolValue":"boolean value of parameter", + "intValue":"integer value of parameter", + "name":"SETTING_NAME", + "value":"WHO_CAN_JOIN" + } + ], + "type":"GROUP_SETTINGS" + } + ], + "id":{ + "applicationName":"admin", + "customerId":"C03az79cb", + "time":"2011-06-17T15:39:18.460Z", + "uniqueQualifier":"-1234567890987654321" + }, + "ipAddress":"user's IP address", + "kind":"audit#activity", + "ownerDomain":"example.com" + }, + "description":"G Suite Admin Report Log exmaple (validation only)", + "log":"gsuite:reports", + "service":"stream_alert_app", + "source":"prefix_cluster_gsuite_admin_sm-app-name_app", + "trigger_rules":[], + "validate_schema_only":true + } +] \ No newline at end of file diff --git a/tests/integration/rules/guardduty/guard_duty_all.json b/tests/integration/rules/guardduty/guard_duty_all.json index 23fd70f78..36438bfd6 100644 --- a/tests/integration/rules/guardduty/guard_duty_all.json +++ b/tests/integration/rules/guardduty/guard_duty_all.json @@ -1,24 +1,22 @@ -{ - "records": [ - { - "data": { - "version": "0", - "id": "00000000-0000-0000-0000-000000000000", - "detail-type": "GuardDuty Finding", - "source": "aws.guardduty", - "account": "111111111111", - "time": "2018-02-13T18:25:01Z", - "region": "us-east-1", - "resources": [], - "detail": {} - }, - "description": "GuardDuty", - "log": "cloudwatch:events", - "service": "kinesis", - "source": "prefix_cluster1_stream_alert_kinesis", - "trigger_rules": [ - "guard_duty_all" - ] - } - ] -} \ No newline at end of file +[ + { + "data":{ + "account":"111111111111", + "detail":{}, + "detail-type":"GuardDuty Finding", + "id":"00000000-0000-0000-0000-000000000000", + "region":"us-east-1", + "resources":[], + "source":"aws.guardduty", + "time":"2018-02-13T18:25:01Z", + "version":"0" + }, + "description":"GuardDuty", + "log":"cloudwatch:events", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "guard_duty_all" + ] + } +] \ No newline at end of file diff --git a/tests/integration/rules/mitre_attack/right_to_left_character.json b/tests/integration/rules/mitre_attack/right_to_left_character.json index 852687065..ed9a15b09 100644 --- a/tests/integration/rules/mitre_attack/right_to_left_character.json +++ b/tests/integration/rules/mitre_attack/right_to_left_character.json @@ -1,110 +1,108 @@ -{ - "records": [ - { - "data": { - "cb_server": "...", - "command_line": "ls -lha", - "computer_name": "...", - "event_type": "...", - "expect_followon_w_md5": false, - "md5": "...", - "parent_create_time": 123456789, - "parent_md5": "...", - "parent_path": "/path/to/parent/command", - "parent_process_guid": "...", - "path": "/path/to/command", - "pid": 1234, - "process_guid": "...", - "sensor_id": 12345, - "timestamp": 123456789, - "type": "...", - "username": "username" - }, - "description": "Running a binary that does not have the right-to-left-override character, should not create an alert.", - "log": "...", - "source": "prefix.cluster.sample.bucket", - "service": "s3", - "trigger_rules": [] +[ + { + "data":{ + "cb_server":"...", + "command_line":"ls -lha", + "computer_name":"...", + "event_type":"...", + "expect_followon_w_md5":false, + "md5":"...", + "parent_create_time":123456789, + "parent_md5":"...", + "parent_path":"/path/to/parent/command", + "parent_process_guid":"...", + "path":"/path/to/command", + "pid":1234, + "process_guid":"...", + "sensor_id":12345, + "timestamp":123456789, + "type":"...", + "username":"username" }, - { - "data": { - "cb_server": "...", - "command_line": "“evil ‮”cod.exe", - "computer_name": "...", - "event_type": "...", - "expect_followon_w_md5": false, - "md5": "...", - "parent_create_time": 123456789, - "parent_md5": "...", - "parent_path": "/bin/bash", - "parent_process_guid": "...", - "path": "/bin/bash", - "pid": 1234, - "process_guid": "...", - "sensor_id": 12345, - "timestamp": 123456789, - "type": "...", - "username": "nobody" - }, - "description": "Running a binary that does have the right-to-left-override character, should create an alert.", - "log": "...", - "source": "prefix.cluster.sample.bucket", - "service": "s3", - "trigger_rules": [ - "right_to_left_character" - ] + "description":"Running a binary that does not have the right-to-left-override character, should not create an alert.", + "log":"...", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[] + }, + { + "data":{ + "cb_server":"...", + "command_line":"\u201cevil \u202e\u201dcod.exe", + "computer_name":"...", + "event_type":"...", + "expect_followon_w_md5":false, + "md5":"...", + "parent_create_time":123456789, + "parent_md5":"...", + "parent_path":"/bin/bash", + "parent_process_guid":"...", + "path":"/bin/bash", + "pid":1234, + "process_guid":"...", + "sensor_id":12345, + "timestamp":123456789, + "type":"...", + "username":"nobody" }, - { - "data": { - "name": "...", - "hostIdentifier": "...", - "calendarTime": "...", - "unixTime": "123456789", - "decorations": { - "envIdentifier": "...", - "roleIdentifier": "..." - }, - "columns": { - "command": "ls -lha", - "history_file": "/path/to/history", - "time": "123456789", - "uid": "1234", - "username": "username" - }, - "action": "added" + "description":"Running a binary that does have the right-to-left-override character, should create an alert.", + "log":"...", + "service":"s3", + "source":"prefix.cluster.sample.bucket", + "trigger_rules":[ + "right_to_left_character" + ] + }, + { + "data":{ + "action":"added", + "calendarTime":"...", + "columns":{ + "command":"ls -lha", + "history_file":"/path/to/history", + "time":"123456789", + "uid":"1234", + "username":"username" + }, + "decorations":{ + "envIdentifier":"...", + "roleIdentifier":"..." }, - "description": "Running a binary that does not have the right-to-left-override character, should not create an alert.", - "log": "...", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [] + "hostIdentifier":"...", + "name":"...", + "unixTime":"123456789" }, - { - "data": { - "name": "...", - "hostIdentifier": "...", - "calendarTime": "...", - "unixTime": "123456789", - "decorations": { - "envIdentifier": "...", - "roleIdentifier": "..." - }, - "columns": { - "command": "“evil ‮”cod.exe", - "history_file": "/path/to/history", - "time": "123456789", - "uid": "1234", - "username": "username" - }, - "action": "added" + "description":"Running a binary that does not have the right-to-left-override character, should not create an alert.", + "log":"...", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[] + }, + { + "data":{ + "action":"added", + "calendarTime":"...", + "columns":{ + "command":"\u201cevil \u202e\u201dcod.exe", + "history_file":"/path/to/history", + "time":"123456789", + "uid":"1234", + "username":"username" }, - "description": "Running a binary that does have the right-to-left-override character, should create an alert.", - "log": "...", - "source": "prefix_cluster1_stream_alert_kinesis", - "service": "kinesis", - "trigger_rules": [ - "right_to_left_character" - ] - } - ] -} + "decorations":{ + "envIdentifier":"...", + "roleIdentifier":"..." + }, + "hostIdentifier":"...", + "name":"...", + "unixTime":"123456789" + }, + "description":"Running a binary that does have the right-to-left-override character, should create an alert.", + "log":"...", + "service":"kinesis", + "source":"prefix_cluster1_stream_alert_kinesis", + "trigger_rules":[ + "right_to_left_character" + ] + } +] \ No newline at end of file diff --git a/tests/integration/rules/onelogin/onelogin_events_assumed_role.json b/tests/integration/rules/onelogin/onelogin_events_assumed_role.json index 422769349..b39b6110e 100644 --- a/tests/integration/rules/onelogin/onelogin_events_assumed_role.json +++ b/tests/integration/rules/onelogin/onelogin_events_assumed_role.json @@ -1,87 +1,84 @@ -{ - "records": [ - { - "data": { - "id": 123, - "created_at": "2017-10-05T18:11:32Z", - "account_id": 1234, - "user_id": 123456789, - "event_type_id": 3, - "notes": "Notes", - "ipaddr": "0.0.0.0", - "actor_user_id": 987, - "assuming_acting_user_id": 654, - "role_id": 456, - "app_id": 123456, - "group_id": 98765, - "otp_device_id": 11111, - "policy_id": 22222, - "actor_system": "System", - "custom_message": "Message", - "role_name": "Role", - "app_name": "App Name", - "group_name": "Group Name", - "actor_user_name": "", - "user_name": "username", - "policy_name": "Policy Name", - "otp_device_name": "OTP Device Name", - "operation_name": "Operation Name", - "directory_sync_run_id": 7777, - "directory_id": 6666, - "proxy_ip": "0.0.0.0", - "resolution": "Resolved", - "client_id": 11223344, - "resource_type_id": 44332211, - "error_description": "ERROR ERROR" - }, - "description": "OneLogin generated event when a user assumed a different role, it should alert", - "log": "onelogin:events", - "trigger_rules": [ - "onelogin_events_assumed_role" - ], - "service": "stream_alert_app", - "source": "prefix_cluster_onelogin-events-app-name_app" - }, - { - "data": { - "id": 123, - "created_at": "2017-10-05T18:11:32Z", - "account_id": 1234, - "user_id": 123456789, - "event_type_id": 10, - "notes": "Notes", - "ipaddr": "0.0.0.0", - "actor_user_id": 987, - "assuming_acting_user_id": 654, - "role_id": 456, - "app_id": 123456, - "group_id": 98765, - "otp_device_id": 11111, - "policy_id": 22222, - "actor_system": "System", - "custom_message": "Message", - "role_name": "Role", - "app_name": "App Name", - "group_name": "Group Name", - "actor_user_name": "", - "user_name": "username", - "policy_name": "Policy Name", - "otp_device_name": "OTP Device Name", - "operation_name": "Operation Name", - "directory_sync_run_id": 7777, - "directory_id": 6666, - "proxy_ip": "0.0.0.0", - "resolution": "Resolved", - "client_id": 11223344, - "resource_type_id": 44332211, - "error_description": "ERROR ERROR" - }, - "description": "OneLogin generated event when a user do any other action, it should not alert", - "log": "onelogin:events", - "trigger_rules": [ - ], - "service": "stream_alert_app", - "source": "prefix_cluster_onelogin-events-app-name_app" - } +[ + { + "data":{ + "account_id":1234, + "actor_system":"System", + "actor_user_id":987, + "actor_user_name":"", + "app_id":123456, + "app_name":"App Name", + "assuming_acting_user_id":654, + "client_id":11223344, + "created_at":"2017-10-05T18:11:32Z", + "custom_message":"Message", + "directory_id":6666, + "directory_sync_run_id":7777, + "error_description":"ERROR ERROR", + "event_type_id":3, + "group_id":98765, + "group_name":"Group Name", + "id":123, + "ipaddr":"0.0.0.0", + "notes":"Notes", + "operation_name":"Operation Name", + "otp_device_id":11111, + "otp_device_name":"OTP Device Name", + "policy_id":22222, + "policy_name":"Policy Name", + "proxy_ip":"0.0.0.0", + "resolution":"Resolved", + "resource_type_id":44332211, + "role_id":456, + "role_name":"Role", + "user_id":123456789, + "user_name":"username" + }, + "description":"OneLogin generated event when a user assumed a different role, it should alert", + "log":"onelogin:events", + "service":"stream_alert_app", + "source":"prefix_cluster_onelogin-events-app-name_app", + "trigger_rules":[ + "onelogin_events_assumed_role" ] -} \ No newline at end of file + }, + { + "data":{ + "account_id":1234, + "actor_system":"System", + "actor_user_id":987, + "actor_user_name":"", + "app_id":123456, + "app_name":"App Name", + "assuming_acting_user_id":654, + "client_id":11223344, + "created_at":"2017-10-05T18:11:32Z", + "custom_message":"Message", + "directory_id":6666, + "directory_sync_run_id":7777, + "error_description":"ERROR ERROR", + "event_type_id":10, + "group_id":98765, + "group_name":"Group Name", + "id":123, + "ipaddr":"0.0.0.0", + "notes":"Notes", + "operation_name":"Operation Name", + "otp_device_id":11111, + "otp_device_name":"OTP Device Name", + "policy_id":22222, + "policy_name":"Policy Name", + "proxy_ip":"0.0.0.0", + "resolution":"Resolved", + "resource_type_id":44332211, + "role_id":456, + "role_name":"Role", + "user_id":123456789, + "user_name":"username" + }, + "description":"OneLogin generated event when a user do any other action, it should not alert", + "log":"onelogin:events", + "service":"stream_alert_app", + "source":"prefix_cluster_onelogin-events-app-name_app", + "trigger_rules":[] + } +] \ No newline at end of file From 65136f20cd78b36c5bb0bcc3d10244dbaf6f6afc Mon Sep 17 00:00:00 2001 From: Jack Naglieri Date: Tue, 20 Mar 2018 13:34:57 -0700 Subject: [PATCH 08/14] [docs] update rule test documentation with override_records --- docs/source/rule-testing.rst | 291 ++++++++++++++++++++++------------- 1 file changed, 180 insertions(+), 111 deletions(-) diff --git a/docs/source/rule-testing.rst b/docs/source/rule-testing.rst index 34b95006d..127efd7d1 100644 --- a/docs/source/rule-testing.rst +++ b/docs/source/rule-testing.rst @@ -1,30 +1,100 @@ Rule Testing ============ -To test the accuracy of new rules, local tests can be written to verify that alerts trigger against valid input. The ``manage.py`` CLI tool comes built-in with a ``lambda test`` command which does exactly this. +To test the accuracy of new rules, local tests can be written to verify that alerts trigger against valid input. + +The ``manage.py`` CLI tool comes built-in with a ``lambda test`` command which does exactly this. Configuration ~~~~~~~~~~~~~ -To test a new rule, first create a new JSON file anywhere within ``tests/integration/rules`` named ``name_of_your_tests.json``. This file should contain this exact structure:: - - { - "records": [ - { - "data": {} or "", - "description": "information about this test", - "log": "log_type_from_logs.json", - "service": "kinesis" or "s3" or "sns" or "stream_alert_app", - "source": "kinesis_stream_name" or "s3_bucket_id" or "sns_topic_name" or "stream_alert_app_function_name", - "trigger_rules": [ - "rule_01", - "rule_02" - ] - } - ] - } - -.. note:: Multiple tests can be included in one file simply by adding them to the "records" array within the `name_of_your_tests.json` file. +To test a new rule, first create a new JSON file anywhere within the 'tests/integration/rules' directory with the ``.json`` extension. + +This file should contain the following structure: + +.. code-block:: json + + [ + { + "data": "Either a string, or JSON object", + "description": "This test should trigger or not trigger an alert", + "log": "The log name declared in logs.json", + "service": "The service sending the log - kinesis, s3, sns, or stream_alert_app", + "source": "The exact resource which sent the log - kinesis stream name, s3 bucket ID, SNS topic name, or stream_alert_app_function name", + "trigger_rules": [ + "list of rule names which should generate alerts", + "another potential rule name could go here" + ] + } + ] + +.. note:: Multiple tests can be included in one file by adding them to the array above. + +When specifying the test data, it can be either of two fields: + +1. ``"data"``: An entire example record, with all necessary fields to properly classify +2. ``"override_record"``: A subset of the example record, where only relevant fields are populated + +The advantage of option #2 is that the overall test event is much smaller. + +The testing framework will auto-populate the records behind the scenes with the remaining fields for that given log type. + +For example: + +.. code-block:: json + + [ + { + "data": { + "account": "123456789102", + "detail": { + "request": { + "eventName": "putObject", + "bucketName": "testBucket" + } + }, + "detail-type": "API Call", + "id": "123456", + "region": "us-west-2", + "resources": [ + "testBucket" + ], + "source": "aws.s3", + "time": "Jan 01 2018 12:00", + "version": "1.05" + }, + "description": "An example test with a full cloudwatch event", + "log": "cloudwatch:events", + "service": "s3", + "source": "test-s3-bucket-name", + "trigger_rules": [ + "my_fake_rule" + ] + } + ] + +Let's say a rule is only checking the value of ``source`` in the test event. In that case, there's no added benefit to fill in all the other data. Here is what the event would look like with ``override_record``: + +.. code-block:: json + + [ + { + "override_record": { + "source": "aws.s3" + }, + "description": "An example test with a partial cloudwatch event", + "log": "cloudwatch:events", + "service": "s3", + "source": "test-s3-bucket-name", + "trigger_rules": [ + "my_fake_rule" + ] + } + ] + +Both test events would have the same result, but with much less effort. + +.. note:: Either "override_record" or "data" is required in the test event Rule Test Reference ------------------- @@ -33,10 +103,11 @@ Rule Test Reference Key Type Required Description ------------------------- -------------------- -------- ----------- ``compress`` ``boolean`` No Whether or not to compress records with ``gzip`` prior to testing. This is useful to simulate services that send gzipped data -``data`` ``{}`` or ``string`` Yes All ``json`` log types should be in JSON object/dict format while others (``csv, kv, syslog``) should be ``string`` +``data`` ``map or string`` Yes The record to test against your rules. All ``json`` log types should be in JSON object/dict format while others (``csv, kv, syslog``) should be ``string`` +``override_record`` ``map`` No A partial record to use in test events, more information below ``description`` ``string`` Yes A short sentence describing the intent of the test ``log`` ``string`` Yes The log type this test record should parse as. The value of this should be taken from the defined logs in ``conf/logs.json`` -``service`` ``string`` Yes The name of the service which sent the log, e.g: `kinesis, s3, sns, or stream_alert_app` +``service`` ``string`` Yes The name of the service which sent the log, e.g: ``kinesis, s3, sns, or stream_alert_app`` ``source`` ``string`` Yes The name of the Kinesis Stream or S3 bucket, SNS topic or StreamAlert App function where the data originated from. This value should match a source provided in ``conf/sources.json`` ``trigger_rules`` ``list`` Yes A list of zero or more rule names that this test record should trigger. An empty list implies this record should not trigger any alerts ``validate_schemas_only`` ``boolean`` No Whether or not the test record should go through the rule processing engine. If set to ``true``, this record will only have validation performed @@ -44,46 +115,75 @@ Key Type Required Description For more examples, see the provided default rule tests in ``tests/integration/rules`` -Helpers -~~~~~~~ +Running Tests +~~~~~~~~~~~~~~ -It's often necessary to stub (dynamically fill in) values in our test data. This could be due to time-based rules which utilize the ``last_hour`` `rule helper `_. In order to test in these scenarios, a testing helper can be used. +Tests are run via the ``manage.py`` script. These tests include the ability to validate rules for +accuracy, or send alerts to outputs to verify proper configuration. -Helpers Functions ------------------ +When adding new rules, it is only necessary to run tests for the **rule processor**. If making code changes to the alert +processor, such as adding a new output integration to send alerts to, tests for the **alert processor** should also be performed. -``last_hour``: Generates a unix epoch time within the last hour (ex: ``1489105783``). +To run integration tests for the **rule processor**: -Usage ------ +.. code-block:: bash -To use these helpers in rule testing, replace a specific log field value with the following:: + $ python manage.py lambda test --processor rule - "" +To run integration tests for the **alert processor**: -For example, to replace a time based field with ``last_hour``: +.. code-block:: bash -.. code-block:: json + $ python manage.py lambda test --processor alert - { - "records": [ - { - "data": { - "host": "app01.prod.mydomain.net", - "time": "" - }, - "description": "example usage of helpers", - "log": "host_time_log", - "service": "kinesis", - "source": "my_demo_kinesis_stream", - "trigger_rules": [ - "last_hour_rule_name" - ] - } - ] - } +To run end-to-end integration tests for **both processors**: + +.. code-block:: bash + + $ python manage.py lambda test --processor all + +Integration tests can be restricted to **specific rules** to reduce time and output: + +.. code-block:: bash + + $ python manage.py lambda test --processor rule --test-rules + +Integration tests can be restricted to **specific file names** to reduce time and output (the .json suffix is optional): + +.. code-block:: bash + + $ python manage.py lambda test --processor rule --test-files + + +Integration tests can send **live test alerts** to configured outputs for rules using a specified cluster. +This can also be combined with an optional list of rules to use for tests (using the ``--rules`` argument): + +.. code-block:: bash + + $ python manage.py live-test --cluster + +Here is a sample command showing how to run tests against two rules included as integration tests in the default StreamAlert configuration: + +.. code-block:: bash + + $ python manage.py lambda test --processor rule --rules cloudtrail_put_bucket_acl cloudtrail_root_account_usage + +This will produce output similar to the following:: + + cloudtrail_put_bucket_acl + [Pass] [trigger=1] rule (kinesis): An AWS S3 bucket with 'AllUsers' permission(s) will create an alert. + [Pass] [trigger=1] rule (kinesis): An AWS S3 bucket with 'AuthenticatedUsers' permission(s) will create an alert. + [Pass] [trigger=0] rule (kinesis): An AWS PutBucketAcl call without 'AuthenticatedUsers' & 'AllUsers' will not create an alert. + + cloudtrail_root_account_usage + [Pass] [trigger=1] rule (kinesis): Use of the AWS 'Root' account will create an alert. + [Pass] [trigger=0] rule (kinesis): AWS 'Root' account activity initiated automatically by an AWS service on your behalf will not create an alert. + + StreamAlertCLI [INFO]: (5/5) Successful Tests + StreamAlertCLI [INFO]: Completed + Validate Log Schemas ~~~~~~~~~~~~~~~~~~~~ @@ -160,72 +260,41 @@ This will produce output similar to the following:: StreamAlertCLI [ERROR]: (1/2) Failures StreamAlertCLI [ERROR]: (1/1) [cloudtrail_put_object_acl] Data is invalid due to missing key(s) in test record: 'eventVersion'. Rule: 'cloudtrail_put_object_acl'. Description: 'CloudTrail - PutObjectAcl - False Positive' + Helpers + ~~~~~~~ -Running Tests -~~~~~~~~~~~~~~ - -Tests can be run via the ``manage.py`` script. These tests include the ability to validate rules for -accuracy and alert outputs for proper configuration. - -When adding new rules, it is only necessary to run tests for the **rule processor**. If making code changes to the alert -processor, such as adding a new output integration to send alerts to, tests for the **alert processor** should also be performed. - -To run integration tests for the **rule processor**: - -.. code-block:: bash + It's occasionally necessary to dynamically fill in values in our test data. This could be due to time-based rules which utilize the ``last_hour`` `rule helper `_. In order to test in these scenarios, a testing helper can be used. - $ python manage.py lambda test --processor rule + Helpers Functions + ----------------- -To run integration tests for the **alert processor**: + ``last_hour``: Generates a unix epoch time within the last hour (ex: ``1489105783``). -.. code-block:: bash + Usage + ----- - $ python manage.py lambda test --processor alert + To use these helpers in rule testing, replace a specific log field value with the following:: -To run end-to-end integration tests for **both processors**: + "" -.. code-block:: bash + For example, to replace a time based field with ``last_hour``: - $ python manage.py lambda test --processor all - -Integration tests can be restricted to **specific rules** to reduce time and output: - -.. code-block:: bash - - $ python manage.py lambda test --processor rule --test-rules - -Integration tests can be restricted to **specific file names** to reduce time and output (the .json suffix is optional): + .. code-block:: json -.. code-block:: bash - - $ python manage.py lambda test --processor rule --test-files - - -Integration tests can send **live test alerts** to configured outputs for rules using a specified cluster. -This can also be combined with an optional list of rules to use for tests (using the ``--rules`` argument): - -.. code-block:: bash - - $ python manage.py live-test --cluster - -Here is a sample command showing how to run tests against two rules included as integration tests in the default StreamAlert configuration: - -.. code-block:: bash - - $ python manage.py lambda test --processor rule --rules cloudtrail_put_bucket_acl cloudtrail_root_account_usage - -This will produce output similar to the following:: - - cloudtrail_put_bucket_acl - [Pass] [trigger=1] rule (kinesis): An AWS S3 bucket with 'AllUsers' permission(s) will create an alert. - [Pass] [trigger=1] rule (kinesis): An AWS S3 bucket with 'AuthenticatedUsers' permission(s) will create an alert. - [Pass] [trigger=0] rule (kinesis): An AWS PutBucketAcl call without 'AuthenticatedUsers' & 'AllUsers' will not create an alert. - - cloudtrail_root_account_usage - [Pass] [trigger=1] rule (kinesis): Use of the AWS 'Root' account will create an alert. - [Pass] [trigger=0] rule (kinesis): AWS 'Root' account activity initiated automatically by an AWS service on your behalf will not create an alert. - - - - StreamAlertCLI [INFO]: (5/5) Successful Tests - StreamAlertCLI [INFO]: Completed + { + "records": [ + { + "data": { + "host": "app01.prod.mydomain.net", + "time": "" + }, + "description": "example usage of helpers", + "log": "host_time_log", + "service": "kinesis", + "source": "my_demo_kinesis_stream", + "trigger_rules": [ + "last_hour_rule_name" + ] + } + ] + } From 0306a71be0d0e01175f0a45c6032627a31e426e6 Mon Sep 17 00:00:00 2001 From: Ryan Deivert Date: Fri, 16 Mar 2018 18:12:32 -0700 Subject: [PATCH 09/14] adding preliminary support for hardcoded required outputs --- stream_alert/rule_processor/rules_engine.py | 20 ++++----- stream_alert/shared/__init__.py | 43 +++++++++++++++++++ .../test_rules_engine.py | 3 +- tests/unit/stream_alert_shared/test_shared.py | 27 ++++++++++++ 4 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 tests/unit/stream_alert_shared/test_shared.py diff --git a/stream_alert/rule_processor/rules_engine.py b/stream_alert/rule_processor/rules_engine.py index ac56cafdd..d96169105 100644 --- a/stream_alert/rule_processor/rules_engine.py +++ b/stream_alert/rule_processor/rules_engine.py @@ -19,7 +19,7 @@ from stream_alert.rule_processor import LOGGER from stream_alert.rule_processor.threat_intel import StreamThreatIntel -from stream_alert.shared import NORMALIZATION_KEY +from stream_alert.shared import get_required_outputs, NORMALIZATION_KEY DEFAULT_RULE_DESCRIPTION = 'No rule description provided' @@ -53,6 +53,7 @@ class StreamRules(object): def __init__(self, config): """Initialize a StreamRules instance to cache a StreamThreatIntel instance.""" self._threat_intel = StreamThreatIntel.load_from_config(config) + self._required_outputs = set(get_required_outputs(config['global']['account']['prefix'])) @classmethod def get_rules(cls): @@ -85,12 +86,6 @@ def decorator(rule): rule_name) return - if not outputs: - LOGGER.error( - 'Invalid rule [%s] - rule must have \'outputs\' declared', - rule_name) - return - if rule_name in cls.__rules: raise ValueError('rule [{}] already defined'.format(rule_name)) cls.__rules[rule_name] = RuleAttributes(rule_name, @@ -397,6 +392,8 @@ def process(self, input_payload): return alerts, normalized_records + + def threat_intel_match(self, payload_with_normalized_records): """Apply Threat Intelligence on normalized records @@ -421,8 +418,7 @@ def threat_intel_match(self, payload_with_normalized_records): self.rule_analysis(ioc_record.pre_parsed_record, rule, ioc_record, alerts) return alerts - @staticmethod - def rule_analysis(record, rule, payload, alerts): + def rule_analysis(self, record, rule, payload, alerts): """Class method to analyze rule against a record Args: @@ -443,6 +439,10 @@ def rule_analysis(record, rule, payload, alerts): LOGGER.info('Rule [%s] triggered alert [%s] on log type [%s] from entity \'%s\' ' 'in service \'%s\'', rule.rule_name, alert_id, payload.log_source, payload.entity, payload.service()) + + # Combine the required alert outputs with the ones for this rule + all_outputs = list(self._required_outputs.union(set(rule.outputs))) + alert = { 'id': alert_id, 'record': record, @@ -450,7 +450,7 @@ def rule_analysis(record, rule, payload, alerts): 'rule_description': rule.rule_function.__doc__ or DEFAULT_RULE_DESCRIPTION, 'log_source': str(payload.log_source), 'log_type': payload.type, - 'outputs': rule.outputs, + 'outputs': all_outputs, 'source_service': payload.service(), 'source_entity': payload.entity, 'context': rule.context} diff --git a/stream_alert/shared/__init__.py b/stream_alert/shared/__init__.py index 4593666f5..f7fc1ccc5 100644 --- a/stream_alert/shared/__init__.py +++ b/stream_alert/shared/__init__.py @@ -1,6 +1,7 @@ """Define some shared resources.""" import logging import os +import string ALERT_PROCESSOR_NAME = 'alert_processor' @@ -8,6 +9,48 @@ RULE_PROCESSOR_NAME = 'rule_processor' NORMALIZATION_KEY = 'streamalert:normalization' +REQUIRED_OUTPUTS = { + 'aws-firehose': { + 'alerts': '{prefix}_streamalert_alert_delivery', + } +} + +def get_required_outputs(prefix=""): + """Iterates through the required outputs and adds the prefix to them + + Args: + prefix (str): Prefix for this StreamAlert deployment + + Returns: + set: Set of required outputs to be applied to an alert + """ + def _check_fmt(output): + fmt = list(string.Formatter().parse(output)) + # Make sure there are only 2 parts to the format string. ie: + # [('', 'prefix', '', None), ('_streamalert_alert_delivery', None, None, None)] + if len(fmt) != 2: + return False + + # Do not try to format if the 'prefix' is not the only formatting option + if fmt[0][1] != 'prefix': + return False + + return True + + outputs = dict() + for service, value in REQUIRED_OUTPUTS.iteritems(): + if not isinstance(value, dict): + continue + + for output, resource in value.iteritems(): + if not _check_fmt(resource): + continue + + outputs['{}:{}'.format(service, output)] = resource.format(prefix=prefix) + + return outputs + + # Create a package level logger to import LEVEL = os.environ.get('LOGGER_LEVEL', 'INFO').upper() diff --git a/tests/unit/stream_alert_rule_processor/test_rules_engine.py b/tests/unit/stream_alert_rule_processor/test_rules_engine.py index 7ed4246c7..8009b03f5 100644 --- a/tests/unit/stream_alert_rule_processor/test_rules_engine.py +++ b/tests/unit/stream_alert_rule_processor/test_rules_engine.py @@ -21,6 +21,7 @@ from nose.tools import ( assert_equal, assert_false, + assert_in, assert_is_instance, assert_items_equal, assert_true, @@ -200,7 +201,7 @@ def test_nest(rec): # pylint: disable=unused-variable if NORMALIZATION_KEY in alert['record'].keys(): alert['record'].remove(NORMALIZATION_KEY) assert_items_equal(alert['record'].keys(), kinesis_data.keys()) - assert_items_equal(alert['outputs'], rule_outputs_map[alert['rule_name']]) + assert_in(rule_outputs_map[alert['rule_name']][0], alert['outputs']) def test_process_subkeys_nested_records(self): """Rules Engine - Required Subkeys with Nested Records""" diff --git a/tests/unit/stream_alert_shared/test_shared.py b/tests/unit/stream_alert_shared/test_shared.py new file mode 100644 index 000000000..cecc81e0a --- /dev/null +++ b/tests/unit/stream_alert_shared/test_shared.py @@ -0,0 +1,27 @@ +""" +Copyright 2017-present, Airbnb Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +from nose.tools import assert_equal + +from stream_alert import shared + + +def test_get_required_outputs(): + """Shared - Get Required Outputs""" + + outputs = shared.get_required_outputs("test") + + assert_equal(len(outputs), 1) + assert_equal(outputs, {'aws-firehose:alerts': 'test_streamalert_alert_delivery'}) From 8d8493804dfb4b61b0aea8cdd1ff978d672f3f7e Mon Sep 17 00:00:00 2001 From: Ryan Deivert Date: Mon, 19 Mar 2018 16:57:23 -0700 Subject: [PATCH 10/14] simplifying a bit and migrating code to resources file in shared module --- stream_alert/alert_processor/main.py | 16 ++-- stream_alert/rule_processor/rules_engine.py | 10 ++- stream_alert/shared/__init__.py | 42 ---------- stream_alert/shared/resources.py | 65 +++++++++++++++ .../stream_alert_alert_processor/__init__.py | 2 +- .../stream_alert_alert_processor/test_main.py | 15 ++-- .../test_outputs/__init__.py | 2 +- .../stream_alert_shared/test_resources.py | 79 +++++++++++++++++++ tests/unit/stream_alert_shared/test_shared.py | 27 ------- 9 files changed, 170 insertions(+), 88 deletions(-) create mode 100644 stream_alert/shared/resources.py create mode 100644 tests/unit/stream_alert_shared/test_resources.py delete mode 100644 tests/unit/stream_alert_shared/test_shared.py diff --git a/stream_alert/alert_processor/main.py b/stream_alert/alert_processor/main.py index 3c8b29c44..51505bbb0 100644 --- a/stream_alert/alert_processor/main.py +++ b/stream_alert/alert_processor/main.py @@ -20,7 +20,7 @@ from stream_alert.alert_processor import LOGGER from stream_alert.alert_processor.helpers import validate_alert from stream_alert.alert_processor.outputs.output_base import StreamAlertOutput -from stream_alert.shared import NORMALIZATION_KEY +from stream_alert.shared import NORMALIZATION_KEY, resources def handler(event, context): @@ -41,14 +41,15 @@ def handler(event, context): """ # A failure to load the config will log the error in load_output_config # and return here - config = _load_output_config() - if not config: - return - split_arn = context.invoked_function_arn.split(':') region = split_arn[3] account_id = split_arn[4] function_name = context.function_name + prefix = function_name.split('_')[0] + + config = _load_output_config(prefix) + if not config: + return # Return the current list of statuses back to the caller return list(run(event, region, account_id, function_name, config)) @@ -151,7 +152,7 @@ def _sort_dict(unordered_dict): return result -def _load_output_config(config_path='conf/outputs.json'): +def _load_output_config(prefix, config_path='conf/outputs.json'): """Load the outputs configuration file from disk Returns: @@ -164,4 +165,7 @@ def _load_output_config(config_path='conf/outputs.json'): LOGGER.error('The \'%s\' file could not be loaded into json', config_path) return + # Merge in the default outputs info + config = resources.merge_required_outputs(config, prefix) + return config diff --git a/stream_alert/rule_processor/rules_engine.py b/stream_alert/rule_processor/rules_engine.py index d96169105..80a6c3bc0 100644 --- a/stream_alert/rule_processor/rules_engine.py +++ b/stream_alert/rule_processor/rules_engine.py @@ -19,7 +19,7 @@ from stream_alert.rule_processor import LOGGER from stream_alert.rule_processor.threat_intel import StreamThreatIntel -from stream_alert.shared import get_required_outputs, NORMALIZATION_KEY +from stream_alert.shared import resources, NORMALIZATION_KEY DEFAULT_RULE_DESCRIPTION = 'No rule description provided' @@ -53,7 +53,7 @@ class StreamRules(object): def __init__(self, config): """Initialize a StreamRules instance to cache a StreamThreatIntel instance.""" self._threat_intel = StreamThreatIntel.load_from_config(config) - self._required_outputs = set(get_required_outputs(config['global']['account']['prefix'])) + self._required_outputs = set(resources.get_required_outputs()) @classmethod def get_rules(cls): @@ -441,7 +441,9 @@ def rule_analysis(self, record, rule, payload, alerts): payload.entity, payload.service()) # Combine the required alert outputs with the ones for this rule - all_outputs = list(self._required_outputs.union(set(rule.outputs))) + all_outputs = self._required_outputs + if rule.outputs: + all_outputs = all_outputs.union(set(rule.outputs)) alert = { 'id': alert_id, @@ -450,7 +452,7 @@ def rule_analysis(self, record, rule, payload, alerts): 'rule_description': rule.rule_function.__doc__ or DEFAULT_RULE_DESCRIPTION, 'log_source': str(payload.log_source), 'log_type': payload.type, - 'outputs': all_outputs, + 'outputs': list(all_outputs), 'source_service': payload.service(), 'source_entity': payload.entity, 'context': rule.context} diff --git a/stream_alert/shared/__init__.py b/stream_alert/shared/__init__.py index f7fc1ccc5..3036b23f2 100644 --- a/stream_alert/shared/__init__.py +++ b/stream_alert/shared/__init__.py @@ -9,48 +9,6 @@ RULE_PROCESSOR_NAME = 'rule_processor' NORMALIZATION_KEY = 'streamalert:normalization' -REQUIRED_OUTPUTS = { - 'aws-firehose': { - 'alerts': '{prefix}_streamalert_alert_delivery', - } -} - -def get_required_outputs(prefix=""): - """Iterates through the required outputs and adds the prefix to them - - Args: - prefix (str): Prefix for this StreamAlert deployment - - Returns: - set: Set of required outputs to be applied to an alert - """ - def _check_fmt(output): - fmt = list(string.Formatter().parse(output)) - # Make sure there are only 2 parts to the format string. ie: - # [('', 'prefix', '', None), ('_streamalert_alert_delivery', None, None, None)] - if len(fmt) != 2: - return False - - # Do not try to format if the 'prefix' is not the only formatting option - if fmt[0][1] != 'prefix': - return False - - return True - - outputs = dict() - for service, value in REQUIRED_OUTPUTS.iteritems(): - if not isinstance(value, dict): - continue - - for output, resource in value.iteritems(): - if not _check_fmt(resource): - continue - - outputs['{}:{}'.format(service, output)] = resource.format(prefix=prefix) - - return outputs - - # Create a package level logger to import LEVEL = os.environ.get('LOGGER_LEVEL', 'INFO').upper() diff --git a/stream_alert/shared/resources.py b/stream_alert/shared/resources.py new file mode 100644 index 000000000..fd461f28b --- /dev/null +++ b/stream_alert/shared/resources.py @@ -0,0 +1,65 @@ +""" +Copyright 2017-present, Airbnb Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +REQUIRED_OUTPUTS = { + 'aws-firehose': { + 'alerts': '{prefix}_streamalert_alert_delivery', + } +} + +def get_required_outputs(): + """Iterates through the required outputs and collapses to the right format + + Returns: + set: Set of required output services and names in the form service:name + """ + outputs = set() + for service, value in REQUIRED_OUTPUTS.iteritems(): + if not isinstance(value, dict): + continue + outputs.update({'{}:{}'.format(service, output) for output in value.keys()}) + return outputs + + +def merge_required_outputs(user_config, prefix): + """Iterates through the required outputs and merges them with the user outputs + + Args: + user_config (dict): Loaded user outputs dictionary from conf/outputs.json + prefix (str): Prefix for this StreamAlert deployment to be injected into + resource names + + Returns: + dict: Entire formatted outputs dictionary, including required items and + user defined outputs + """ + for service, value in REQUIRED_OUTPUTS.iteritems(): + if not isinstance(value, dict): + continue + + # Format the resource with the prefix value + for output, resource in value.iteritems(): + value[output] = resource.format(prefix=prefix) + + # Add the outputs for this service if none are defined + if service not in user_config: + user_config[service] = value + continue + + # Merge the outputs with existing ones for this service + user_config[service].update(value) + + return user_config diff --git a/tests/unit/stream_alert_alert_processor/__init__.py b/tests/unit/stream_alert_alert_processor/__init__.py index a3a48aab0..7aac9e028 100644 --- a/tests/unit/stream_alert_alert_processor/__init__.py +++ b/tests/unit/stream_alert_alert_processor/__init__.py @@ -18,5 +18,5 @@ REGION = 'us-east-1' ACCOUNT_ID = '123456789012' FUNCTION_NAME = 'corp-prefix_prod_streamalert_alert_processor' -CONFIG = _load_output_config('tests/unit/conf/outputs.json') +CONFIG = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') KMS_ALIAS = 'alias/stream_alert_secrets_test' diff --git a/tests/unit/stream_alert_alert_processor/test_main.py b/tests/unit/stream_alert_alert_processor/test_main.py index ed1b4deb0..7ec7ac24f 100644 --- a/tests/unit/stream_alert_alert_processor/test_main.py +++ b/tests/unit/stream_alert_alert_processor/test_main.py @@ -39,7 +39,8 @@ def test_handler_run(run_mock): # This test will load the actual config, so we should compare the # function call against the same config here. - run_mock.assert_called_with(None, REGION, '5'*12, FUNCTION_NAME, _load_output_config()) + run_mock.assert_called_with( + None, REGION, '5'*12, FUNCTION_NAME, _load_output_config('corp-prefix')) @patch('logging.Logger.error') @@ -47,7 +48,7 @@ def test_bad_config(log_mock): """Load output config - bad config""" mock = mock_open(read_data='non-json string that will log an error') with patch('__builtin__.open', mock): - handler(None, None) + handler(None, get_mock_context()) log_mock.assert_called_with( 'The \'%s\' file could not be loaded into json', @@ -65,7 +66,7 @@ def test_handler_return(): def test_load_output_config(): """Load outputs configuration file""" - config = _load_output_config('tests/unit/conf/outputs.json') + config = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') assert_equal(set(config.keys()), { 'aws-firehose', 'aws-s3', 'aws-lambda', 'aws-sns', 'aws-sqs', @@ -103,7 +104,7 @@ def test_sort_dict_recursive(): @patch('stream_alert.alert_processor.outputs.output_base.OutputDispatcher._load_creds') def test_running_success(creds_mock, config_mock, get_mock): """Alert Processor run handler - success""" - config_mock.return_value = _load_output_config('tests/unit/conf/outputs.json') + config_mock.return_value = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') creds_mock.return_value = {'url': 'http://mock.url'} get_mock.return_value.status_code = 200 @@ -120,7 +121,7 @@ def test_running_success(creds_mock, config_mock, get_mock): @patch('stream_alert.alert_processor.main._load_output_config') def test_running_bad_output(config_mock, log_mock): """Alert Processor run handler - bad output""" - config_mock.return_value = _load_output_config('tests/unit/conf/outputs.json') + config_mock.return_value = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') alert = get_alert() alert['outputs'] = ['slack'] @@ -144,7 +145,7 @@ def test_running_bad_output(config_mock, log_mock): @patch('stream_alert.alert_processor.outputs.output_base.StreamAlertOutput.get_dispatcher') def test_running_no_dispatcher(dispatch_mock, config_mock): """Alert Processor - Run Handler With No Dispatcher""" - config_mock.return_value = _load_output_config('tests/unit/conf/outputs.json') + config_mock.return_value = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') dispatch_mock.return_value = None alert = get_alert() @@ -167,7 +168,7 @@ def test_running_exception_occurred(creds_mock, dispatch_mock, config_mock, get_ err = TypeError('bad error') creds_mock.return_value = {'url': 'mock.url'} dispatch_mock.return_value.dispatch.side_effect = err - config_mock.return_value = _load_output_config('tests/unit/conf/outputs.json') + config_mock.return_value = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') get_mock.return_value.status_code = 200 alert = _sort_dict(get_alert()) diff --git a/tests/unit/stream_alert_alert_processor/test_outputs/__init__.py b/tests/unit/stream_alert_alert_processor/test_outputs/__init__.py index 48564fa87..2ef79f2ee 100644 --- a/tests/unit/stream_alert_alert_processor/test_outputs/__init__.py +++ b/tests/unit/stream_alert_alert_processor/test_outputs/__init__.py @@ -17,5 +17,5 @@ REGION = 'us-east-1' FUNCTION_NAME = 'corp-prefix_prod_streamalert_alert_processor' -CONFIG = _load_output_config('tests/unit/conf/outputs.json') +CONFIG = _load_output_config('corp-prefix', 'tests/unit/conf/outputs.json') KMS_ALIAS = 'alias/stream_alert_secrets_test' diff --git a/tests/unit/stream_alert_shared/test_resources.py b/tests/unit/stream_alert_shared/test_resources.py new file mode 100644 index 000000000..c7595175a --- /dev/null +++ b/tests/unit/stream_alert_shared/test_resources.py @@ -0,0 +1,79 @@ +""" +Copyright 2017-present, Airbnb Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +from nose.tools import assert_equal, assert_items_equal + +from stream_alert.shared import resources + + +def test_get_required_outputs(): + """Shared - Get Required Outputs""" + outputs = resources.get_required_outputs() + assert_equal(len(outputs), 1) + assert_equal(outputs, {'aws-firehose:alerts'}) + + +def test_merge_required_outputs_dne(): + """Shared - Merge Required Outputs, Does Not Exist""" + # A simple user config that will be merged with required outputs + users_config = { + 'aws-s3': { + 'bucket': 'my.s3.bucket' + }, + 'aws-sns': { + 'topic': 'my-sns-topic' + }, + 'slack': [ + 'slack_output' + ] + } + + outputs = resources.merge_required_outputs(users_config, "test") + + assert_equal(len(outputs), 4) + + expected_fh = { + 'alerts': 'test_streamalert_alert_delivery' + } + + assert_items_equal(outputs['aws-firehose'], expected_fh) + + +def test_merge_required_outputs_exists(): + """Shared - Merge Required Outputs, Has Existing""" + # A simple user config with an exist aws-firehose output + # that will be merged with required outputs + users_config = { + 'aws-firehose': { + 'notalerts': 'resource_name' + }, + 'aws-sns': { + 'topic': 'my-sns-topic' + }, + 'slack': [ + 'slack_output' + ] + } + + outputs = resources.merge_required_outputs(users_config, "test") + + assert_equal(len(outputs), 3) + + expected_fh = { + 'notalerts': 'resource_name', + 'alerts': 'test_streamalert_alert_delivery' + } + + assert_items_equal(outputs['aws-firehose'], expected_fh) diff --git a/tests/unit/stream_alert_shared/test_shared.py b/tests/unit/stream_alert_shared/test_shared.py deleted file mode 100644 index cecc81e0a..000000000 --- a/tests/unit/stream_alert_shared/test_shared.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -Copyright 2017-present, Airbnb Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from nose.tools import assert_equal - -from stream_alert import shared - - -def test_get_required_outputs(): - """Shared - Get Required Outputs""" - - outputs = shared.get_required_outputs("test") - - assert_equal(len(outputs), 1) - assert_equal(outputs, {'aws-firehose:alerts': 'test_streamalert_alert_delivery'}) From 8a5a616bd6d24ddba54935e8eff908d40f024af9 Mon Sep 17 00:00:00 2001 From: Ryan Deivert Date: Tue, 20 Mar 2018 13:30:17 -0700 Subject: [PATCH 11/14] [test] suppressing noisy logging while running tests --- stream_alert_cli/logger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stream_alert_cli/logger.py b/stream_alert_cli/logger.py index f8f1841f7..7a45140e6 100644 --- a/stream_alert_cli/logger.py +++ b/stream_alert_cli/logger.py @@ -35,7 +35,8 @@ def filter(self, record): 'Completed download in*', '*triggered alert*', '*Firehose*', - 'Got * normalized records' + 'Got * normalized records', + 'Rule * triggered alert * on log type *' ) message = record.getMessage() From 8d6213715862a1410b87215a8a8eec3d865d5a3c Mon Sep 17 00:00:00 2001 From: Ryan Deivert Date: Tue, 20 Mar 2018 13:49:44 -0700 Subject: [PATCH 12/14] updating test suite to support required outputs --- stream_alert_cli/test.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/stream_alert_cli/test.py b/stream_alert_cli/test.py index 1a3b60713..e8935059b 100644 --- a/stream_alert_cli/test.py +++ b/stream_alert_cli/test.py @@ -35,6 +35,7 @@ from stream_alert.rule_processor.parsers import get_parser from stream_alert.rule_processor.payload import load_stream_payload from stream_alert.rule_processor.rules_engine import StreamRules +from stream_alert.shared import resources from stream_alert_cli import helpers from stream_alert_cli.logger import ( get_log_memory_handler, @@ -652,7 +653,10 @@ def __init__(self, config, context): self.context = context self.kms_alias = 'alias/stream_alert_secrets_test' self.secrets_bucket = 'test.streamalert.secrets' - self.outputs_config = load_outputs_config() + self.outputs_config = resources.merge_required_outputs( + load_outputs_config(), + 'test-prefix' + ) self.region = config['global']['account']['region'] self._cleanup_old_secrets() self.region = config['global']['account']['region'] @@ -786,7 +790,10 @@ def setup_outputs(self, alert): # Patch requests.get and requests.post self._setup_requests_mocks() - for output in alert.get('outputs', []): + alert_outputs = resources.get_required_outputs() + alert_outputs.update(set(alert.get('outputs', []))) + + for output in alert_outputs: try: service, descriptor = output.split(':') except ValueError: From 1188ea42640b8dd199905efed302b7823102f022 Mon Sep 17 00:00:00 2001 From: Ryan Deivert Date: Tue, 20 Mar 2018 13:30:53 -0700 Subject: [PATCH 13/14] updates to rules to remove output that is required by default --- rules/community/binaryalert/binaryalert_yara_match.py | 4 +--- rules/community/cloudtrail/cloudtrail_critical_api_calls.py | 4 +--- .../cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py | 4 +--- .../cloudtrail/cloudtrail_network_acl_ingress_anywhere.py | 1 - rules/community/cloudtrail/cloudtrail_put_bucket_acl.py | 1 - .../community/cloudtrail/cloudtrail_put_object_acl_public.py | 1 - rules/community/cloudtrail/cloudtrail_root_account_usage.py | 1 - .../cloudtrail/cloudtrail_security_group_ingress_anywhere.py | 1 - .../duo_bypass_code_create_non_auto_generated.py | 2 +- .../duo_administrator/duo_bypass_code_create_non_expiring.py | 2 +- .../duo_administrator/duo_bypass_code_create_unlimited_use.py | 2 +- .../community/duo_authentication/duo_anonymous_ip_failure.py | 2 +- rules/community/duo_authentication/duo_fraud.py | 2 +- .../github_disable_dismiss_stale_pull_request_approvals.py | 2 +- rules/community/github/github_disable_protect_this_branch.py | 2 +- .../github/github_disable_required_pull_request_reviews.py | 2 +- .../community/github/github_disable_required_status_checks.py | 2 +- .../github/github_disable_two_factor_requirement_org.py | 2 +- .../github/github_disable_two_factor_requirement_user.py | 2 +- rules/community/github/github_oauth_application_create.py | 2 +- rules/community/github/github_site_admin_action.py | 2 +- rules/community/github/github_site_admin_user_promotion.py | 2 +- rules/community/guardduty/guard_duty_all.py | 2 +- .../right_to_left_character.py | 4 +--- rules/community/onelogin/onelogin_events_assumed_role.py | 2 +- 25 files changed, 20 insertions(+), 33 deletions(-) diff --git a/rules/community/binaryalert/binaryalert_yara_match.py b/rules/community/binaryalert/binaryalert_yara_match.py index eee1071eb..3fe9374aa 100644 --- a/rules/community/binaryalert/binaryalert_yara_match.py +++ b/rules/community/binaryalert/binaryalert_yara_match.py @@ -4,9 +4,7 @@ rule = StreamRules.rule -@rule( - logs=['binaryalert'], - outputs=['aws-firehose:alerts']) +@rule(logs=['binaryalert']) def binaryalert_yara_match(rec): """ author: Austin Byers (Airbnb CSIRT) diff --git a/rules/community/cloudtrail/cloudtrail_critical_api_calls.py b/rules/community/cloudtrail/cloudtrail_critical_api_calls.py index bb9cb871d..3cc101f3d 100644 --- a/rules/community/cloudtrail/cloudtrail_critical_api_calls.py +++ b/rules/community/cloudtrail/cloudtrail_critical_api_calls.py @@ -6,9 +6,7 @@ disable = StreamRules.disable() -@rule( - logs=['cloudtrail:events'], - outputs=['aws-firehose:alerts']) +@rule(logs=['cloudtrail:events']) def cloudtrail_critical_api_calls(rec): """ author: airbnb_csirt diff --git a/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py b/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py index 18489c747..9ea6ad62b 100644 --- a/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py +++ b/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py @@ -6,9 +6,7 @@ disable = StreamRules.disable() -@rule( - logs=['cloudtrail:events'], - outputs=['aws-firehose:alerts']) +@rule(logs=['cloudtrail:events']) def cloudtrail_mfa_policy_abuse_attempt(rec): """ author: Scott Piper of Summit Route in collaboration with Duo Security diff --git a/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py b/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py index 664b22b02..6213b6ec5 100644 --- a/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py +++ b/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py @@ -6,7 +6,6 @@ @rule( logs=['cloudwatch:events'], - outputs=['aws-firehose:alerts'], req_subkeys={ 'detail': ['eventName', 'requestParameters'] }) diff --git a/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py b/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py index 3a9d0d117..6f56fdc4d 100644 --- a/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py +++ b/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py @@ -6,7 +6,6 @@ @rule( logs=['cloudwatch:events'], - outputs=['aws-firehose:alerts'], req_subkeys={ 'detail': ['requestParameters', 'eventName'] }) diff --git a/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py b/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py index 153d80bf6..d644930fc 100644 --- a/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py +++ b/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py @@ -7,7 +7,6 @@ @rule( logs=['cloudwatch:events'], - outputs=['aws-firehose:alerts'], req_subkeys={ 'detail': ['requestParameters'] }) diff --git a/rules/community/cloudtrail/cloudtrail_root_account_usage.py b/rules/community/cloudtrail/cloudtrail_root_account_usage.py index 04ed46c5a..754c03ecb 100644 --- a/rules/community/cloudtrail/cloudtrail_root_account_usage.py +++ b/rules/community/cloudtrail/cloudtrail_root_account_usage.py @@ -6,7 +6,6 @@ @rule( logs=['cloudwatch:events'], - outputs=['aws-firehose:alerts'], req_subkeys={ 'detail': ['userIdentity', 'eventType'] }) diff --git a/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py b/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py index 37d3c8b98..0c9c8c4a6 100644 --- a/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py +++ b/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py @@ -7,7 +7,6 @@ @rule( logs=['cloudwatch:events'], - outputs=['aws-firehose:alerts'], req_subkeys={ 'detail': ['eventName', 'requestParameters'] }) diff --git a/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py b/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py index d3ad2408d..6f673add2 100644 --- a/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py +++ b/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py @@ -5,7 +5,7 @@ rule = StreamRules.rule -@rule(logs=['duo:administrator'], outputs=['aws-firehose:alerts']) +@rule(logs=['duo:administrator']) def duo_bypass_code_create_non_auto_generated(rec): """ author: @mimeframe diff --git a/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py b/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py index 41110c517..542955a6e 100644 --- a/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py +++ b/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py @@ -5,7 +5,7 @@ rule = StreamRules.rule -@rule(logs=['duo:administrator'], outputs=['aws-firehose:alerts']) +@rule(logs=['duo:administrator']) def duo_bypass_code_create_non_expiring(rec): """ author: @mimeframe diff --git a/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py b/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py index 7caf9e9d2..159308a01 100644 --- a/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py +++ b/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py @@ -5,7 +5,7 @@ rule = StreamRules.rule -@rule(logs=['duo:administrator'], outputs=['aws-firehose:alerts']) +@rule(logs=['duo:administrator']) def duo_bypass_code_create_unlimited_use(rec): """ author: @mimeframe diff --git a/rules/community/duo_authentication/duo_anonymous_ip_failure.py b/rules/community/duo_authentication/duo_anonymous_ip_failure.py index eb0136205..c82744f10 100644 --- a/rules/community/duo_authentication/duo_anonymous_ip_failure.py +++ b/rules/community/duo_authentication/duo_anonymous_ip_failure.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['duo:authentication'], outputs=['aws-firehose:alerts']) +@rule(logs=['duo:authentication']) def duo_anonymous_ip_failure(rec): """ author: airbnb_csirt diff --git a/rules/community/duo_authentication/duo_fraud.py b/rules/community/duo_authentication/duo_fraud.py index 2348b37e7..6ff9c543c 100644 --- a/rules/community/duo_authentication/duo_fraud.py +++ b/rules/community/duo_authentication/duo_fraud.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['duo:authentication'], outputs=['aws-firehose:alerts']) +@rule(logs=['duo:authentication']) def duo_fraud(rec): """ author: airbnb_csirt diff --git a/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py b/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py index cb6d33aa9..cb79d0226 100644 --- a/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py +++ b/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_disable_dismiss_stale_pull_request_approvals(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_protect_this_branch.py b/rules/community/github/github_disable_protect_this_branch.py index eaf86b4b6..6c90f3c76 100644 --- a/rules/community/github/github_disable_protect_this_branch.py +++ b/rules/community/github/github_disable_protect_this_branch.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_disable_protect_this_branch(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_required_pull_request_reviews.py b/rules/community/github/github_disable_required_pull_request_reviews.py index eca9f1a20..18b55b88e 100644 --- a/rules/community/github/github_disable_required_pull_request_reviews.py +++ b/rules/community/github/github_disable_required_pull_request_reviews.py @@ -5,7 +5,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_disable_required_pull_request_reviews(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_required_status_checks.py b/rules/community/github/github_disable_required_status_checks.py index b28f6833c..c876ad10e 100644 --- a/rules/community/github/github_disable_required_status_checks.py +++ b/rules/community/github/github_disable_required_status_checks.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_disable_required_status_checks(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_two_factor_requirement_org.py b/rules/community/github/github_disable_two_factor_requirement_org.py index 8473aa933..28ce8547c 100644 --- a/rules/community/github/github_disable_two_factor_requirement_org.py +++ b/rules/community/github/github_disable_two_factor_requirement_org.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_disable_two_factor_requirement_org(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_two_factor_requirement_user.py b/rules/community/github/github_disable_two_factor_requirement_user.py index ae9de9ada..77e27008e 100644 --- a/rules/community/github/github_disable_two_factor_requirement_user.py +++ b/rules/community/github/github_disable_two_factor_requirement_user.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_disable_two_factor_requirement_user(rec): """ author: @mimeframe diff --git a/rules/community/github/github_oauth_application_create.py b/rules/community/github/github_oauth_application_create.py index cd91a355d..76fab7722 100644 --- a/rules/community/github/github_oauth_application_create.py +++ b/rules/community/github/github_oauth_application_create.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_oauth_application_create(rec): """ author: @mimeframe diff --git a/rules/community/github/github_site_admin_action.py b/rules/community/github/github_site_admin_action.py index 7679d0417..ce7ed71e2 100644 --- a/rules/community/github/github_site_admin_action.py +++ b/rules/community/github/github_site_admin_action.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_site_admin_action(rec): """ author: @mimeframe diff --git a/rules/community/github/github_site_admin_user_promotion.py b/rules/community/github/github_site_admin_user_promotion.py index 50477e88a..7baccb322 100644 --- a/rules/community/github/github_site_admin_user_promotion.py +++ b/rules/community/github/github_site_admin_user_promotion.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) +@rule(logs=['ghe:general']) def github_site_admin_user_promotion(rec): """ author: @fusionrace, @mimeframe diff --git a/rules/community/guardduty/guard_duty_all.py b/rules/community/guardduty/guard_duty_all.py index 3e334aa3e..8238eb7e8 100644 --- a/rules/community/guardduty/guard_duty_all.py +++ b/rules/community/guardduty/guard_duty_all.py @@ -5,7 +5,7 @@ disable = StreamRules.disable() -@rule(logs=['cloudwatch:events'], matchers=['guard_duty'], outputs=['aws-firehose:alerts']) +@rule(logs=['cloudwatch:events'], matchers=['guard_duty']) def guard_duty_all(*_): """ author: spiper diff --git a/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py b/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py index d2f9d6334..96a842b12 100644 --- a/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py +++ b/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py @@ -5,9 +5,7 @@ rule = StreamRules.rule -@rule( - datatypes=['command', 'filePath', 'processPath', 'fileName'], - outputs=['aws-firehose:alerts']) +@rule(datatypes=['command', 'filePath', 'processPath', 'fileName']) def right_to_left_character(rec): """ author: @javutin diff --git a/rules/community/onelogin/onelogin_events_assumed_role.py b/rules/community/onelogin/onelogin_events_assumed_role.py index 8ae6cc9f3..d39c93b3c 100644 --- a/rules/community/onelogin/onelogin_events_assumed_role.py +++ b/rules/community/onelogin/onelogin_events_assumed_role.py @@ -4,7 +4,7 @@ rule = StreamRules.rule -@rule(logs=['onelogin:events'], outputs=['aws-firehose:alerts']) +@rule(logs=['onelogin:events']) def onelogin_events_assumed_role(rec): """ author: @javutin From da8192af83114b48a871d0739dccf3fbafea71f2 Mon Sep 17 00:00:00 2001 From: Ryan Deivert Date: Tue, 20 Mar 2018 15:40:56 -0700 Subject: [PATCH 14/14] addressing pr feedback --- stream_alert/rule_processor/rules_engine.py | 10 ++++------ stream_alert/shared/__init__.py | 1 - stream_alert/shared/resources.py | 21 ++++++++------------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/stream_alert/rule_processor/rules_engine.py b/stream_alert/rule_processor/rules_engine.py index 80a6c3bc0..80f54089d 100644 --- a/stream_alert/rule_processor/rules_engine.py +++ b/stream_alert/rule_processor/rules_engine.py @@ -19,7 +19,7 @@ from stream_alert.rule_processor import LOGGER from stream_alert.rule_processor.threat_intel import StreamThreatIntel -from stream_alert.shared import resources, NORMALIZATION_KEY +from stream_alert.shared import NORMALIZATION_KEY, resources DEFAULT_RULE_DESCRIPTION = 'No rule description provided' @@ -53,7 +53,7 @@ class StreamRules(object): def __init__(self, config): """Initialize a StreamRules instance to cache a StreamThreatIntel instance.""" self._threat_intel = StreamThreatIntel.load_from_config(config) - self._required_outputs = set(resources.get_required_outputs()) + self._required_outputs_set = resources.get_required_outputs() @classmethod def get_rules(cls): @@ -441,9 +441,7 @@ def rule_analysis(self, record, rule, payload, alerts): payload.entity, payload.service()) # Combine the required alert outputs with the ones for this rule - all_outputs = self._required_outputs - if rule.outputs: - all_outputs = all_outputs.union(set(rule.outputs)) + all_outputs = self._required_outputs_set.union(set(rule.outputs or [])) alert = { 'id': alert_id, @@ -452,7 +450,7 @@ def rule_analysis(self, record, rule, payload, alerts): 'rule_description': rule.rule_function.__doc__ or DEFAULT_RULE_DESCRIPTION, 'log_source': str(payload.log_source), 'log_type': payload.type, - 'outputs': list(all_outputs), + 'outputs': list(all_outputs), # TODO: @austinbyers - change this to a set 'source_service': payload.service(), 'source_entity': payload.entity, 'context': rule.context} diff --git a/stream_alert/shared/__init__.py b/stream_alert/shared/__init__.py index 3036b23f2..4593666f5 100644 --- a/stream_alert/shared/__init__.py +++ b/stream_alert/shared/__init__.py @@ -1,7 +1,6 @@ """Define some shared resources.""" import logging import os -import string ALERT_PROCESSOR_NAME = 'alert_processor' diff --git a/stream_alert/shared/resources.py b/stream_alert/shared/resources.py index fd461f28b..189c2730a 100644 --- a/stream_alert/shared/resources.py +++ b/stream_alert/shared/resources.py @@ -26,12 +26,9 @@ def get_required_outputs(): Returns: set: Set of required output services and names in the form service:name """ - outputs = set() - for service, value in REQUIRED_OUTPUTS.iteritems(): - if not isinstance(value, dict): - continue - outputs.update({'{}:{}'.format(service, output) for output in value.keys()}) - return outputs + return {'{}:{}'.format(service, output) + for service, value in REQUIRED_OUTPUTS.iteritems() + for output in value.keys()} def merge_required_outputs(user_config, prefix): @@ -46,20 +43,18 @@ def merge_required_outputs(user_config, prefix): dict: Entire formatted outputs dictionary, including required items and user defined outputs """ + config = user_config.copy() for service, value in REQUIRED_OUTPUTS.iteritems(): - if not isinstance(value, dict): - continue - # Format the resource with the prefix value for output, resource in value.iteritems(): value[output] = resource.format(prefix=prefix) # Add the outputs for this service if none are defined - if service not in user_config: - user_config[service] = value + if service not in config: + config[service] = value continue # Merge the outputs with existing ones for this service - user_config[service].update(value) + config[service].update(value) - return user_config + return config