示例#1
0
 def test_dynamo_record(self):
     """Alert Class - Dynamo Record"""
     # Make sure there are no empty strings nor sets (not allowed in Dynamo)
     alert = Alert('test_rule', {}, {'aws-sns:test-output'},
                   cluster='',
                   created='',
                   log_source='',
                   log_type='',
                   outputs_sent=set(),
                   rule_description='',
                   source_entity='',
                   source_service='')
     record = alert.dynamo_record()
     assert_not_in('', record.values())
     assert_not_in(set(), record.values())
示例#2
0
class TestAlertProcessor(object):
    """Tests for alert_processor/main.py"""
    # pylint: disable=no-member,no-self-use,protected-access

    @patch('stream_alert.alert_processor.main.load_config',
           Mock(return_value=load_config('tests/unit/conf/', validate=True)))
    @patch.dict(os.environ, MOCK_ENV)
    @patch.object(AlertProcessor, 'BACKOFF_MAX_TRIES', 1)
    @patch('stream_alert.alert_processor.main.AlertTable', MagicMock())
    def setup(self):
        """Alert Processor - Test Setup"""
        # pylint: disable=attribute-defined-outside-init
        self.processor = AlertProcessor()
        self.alert = Alert(
            'hello_world',
            {'abc': 123, NORMALIZATION_KEY: {}},
            {'slack:unit-test-channel'}
        )

    def test_init(self):
        """Alert Processor - Initialization"""
        assert_is_instance(self.processor.config, dict)

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_create_dispatcher_invalid(self, mock_logger):
        """Alert Processor - Create Dispatcher - Invalid Output"""
        assert_is_none(self.processor._create_dispatcher('helloworld'))
        mock_logger.error.called_once_with(ANY, 'helloworld')

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_create_dispatcher_output_doesnt_exist(self, mock_logger):
        """Alert Processor - Create Dispatcher - Output Does Not Exist"""
        assert_is_none(self.processor._create_dispatcher('slack:no-such-channel'))
        mock_logger.error.called_once_with(
            'The output \'%s\' does not exist!', 'slack:no-such-channel')

    @patch.dict(os.environ, MOCK_ENV)
    def test_create_dispatcher(self):
        """Alert Processor - Create Dispatcher - Success"""
        dispatcher = self.processor._create_dispatcher('aws-s3:unit_test_bucket')
        assert_is_instance(dispatcher, OutputDispatcher)

    @patch.object(AlertProcessor, '_create_dispatcher')
    def test_send_alerts_success(self, mock_create_dispatcher):
        """Alert Processor - Send Alerts Success"""
        mock_create_dispatcher.return_value.dispatch.return_value = True
        result = self.processor._send_to_outputs(self.alert)
        mock_create_dispatcher.assert_called_once()
        mock_create_dispatcher.return_value.dispatch.assert_called_once()
        assert_equal({'slack:unit-test-channel': True}, result)
        assert_equal(self.alert.outputs, self.alert.outputs_sent)

    @patch.object(AlertProcessor, '_create_dispatcher')
    def test_send_alerts_failure(self, mock_create_dispatcher):
        """Alert Processor - Send Alerts Failure"""
        mock_create_dispatcher.return_value.dispatch.return_value = False
        result = self.processor._send_to_outputs(self.alert)
        mock_create_dispatcher.assert_called_once()
        mock_create_dispatcher.return_value.dispatch.assert_called_once()
        assert_equal({'slack:unit-test-channel': False}, result)
        assert_equal(set(), self.alert.outputs_sent)

    @patch.object(AlertProcessor, '_create_dispatcher', return_value=None)
    def test_send_alerts_skip_invalid_outputs(self, mock_create_dispatcher):
        """Alert Processor - Send Alerts With Invalid Outputs"""
        result = self.processor._send_to_outputs(self.alert)
        mock_create_dispatcher.assert_called_once()
        assert_equal({'slack:unit-test-channel': False}, result)

    def test_update_alerts_table_none(self):
        """Alert Processor - Update Alerts Table - Empty Results"""
        self.processor.alerts_table.delete_alert = MagicMock()
        self.processor.alerts_table.update_retry_outputs = MagicMock()
        self.processor._update_table(self.alert, {})
        self.processor.alerts_table.delete_alert.assert_not_called()
        self.processor.alerts_table.update_retry_outputs.assert_not_called()

    def test_update_alerts_table_delete(self):
        """Alert Processor - Update Alerts Table - Delete Item"""
        self.processor._update_table(self.alert, {'out1': True, 'out2': True})
        self.processor.alerts_table.delete_alerts.assert_called_once_with(
            [(self.alert.rule_name, self.alert.alert_id)])

    def test_update_alerts_table_update(self):
        """Alert Processor - Update Alerts Table - Update With Failed Outputs"""
        self.processor._update_table(self.alert, {'out1': True, 'out2': False, 'out3': False})
        self.processor.alerts_table.update_sent_outputs.assert_called_once_with(self.alert)

    @patch.object(AlertProcessor, '_send_to_outputs',
                  return_value={'slack:unit-test-channel': True})
    @patch.object(AlertProcessor, '_update_table')
    def test_run_full_event(self, mock_send_alerts, mock_update_table):
        """Alert Processor - Run With the Full Alert Record"""
        result = self.processor.run(self.alert.dynamo_record())
        assert_equal({'slack:unit-test-channel': True}, result)
        mock_send_alerts.assert_called_once()
        mock_update_table.assert_called_once()

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_run_invalid_alert(self, mock_logger):
        """Alert Processor - Run With an Invalid Alert"""
        result = self.processor.run({'Record': 'Nonsense'})
        assert_equal({}, result)
        mock_logger.exception.called_once_with('Invalid alert %s', {'Record': 'Nonsense'})

    @patch.object(AlertProcessor, '_send_to_outputs',
                  return_value={'slack:unit-test-channel': True})
    @patch.object(AlertProcessor, '_update_table')
    def test_run_get_alert_from_dynamo(self, mock_send_alerts, mock_update_table):
        """Alert Processor - Run With Just the Alert Key"""
        self.processor.alerts_table.get_alert_record = MagicMock(
            return_value=self.alert.dynamo_record())
        result = self.processor.run(self.alert.dynamo_key)
        assert_equal({'slack:unit-test-channel': True}, result)

        self.processor.alerts_table.get_alert_record.assert_called_once_with(
            self.alert.rule_name, self.alert.alert_id)
        mock_send_alerts.assert_called_once()
        mock_update_table.assert_called_once()

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_run_alert_does_not_exist(self, mock_logger):
        """Alert Processor - Run - Alert Does Not Exist"""
        self.processor.alerts_table.get_alert_record = MagicMock(return_value=None)
        self.processor.run(self.alert.dynamo_key)
        mock_logger.error.assert_called_once_with(
            '%s does not exist in the alerts table', self.alert.dynamo_key)

    @patch.dict(os.environ, MOCK_ENV)
    @patch.object(AlertProcessor, 'run', return_value={'output': True})
    def test_handler(self, mock_run):
        """Alert Processor - Lambda Handler"""
        event = {'AlertID': 'abc', 'RuleName': 'hello_world'}
        result = handler(event, None)
        assert_equal({'output': True}, result)
        mock_run.assert_called_once_with(event)
示例#3
0
class TestAlertProcessor(object):
    """Tests for alert_processor/main.py"""
    # pylint: disable=no-member,no-self-use,protected-access

    @patch.dict(os.environ, {'ALERTS_TABLE': ALERTS_TABLE})
    @patch.object(AlertProcessor, 'BACKOFF_MAX_TRIES', 1)
    @patch.object(AlertProcessor, 'OUTPUT_CONFIG_PATH', OUTPUT_CONFIG_PATH)
    @patch('stream_alert.alert_processor.main.AlertTable', MagicMock())
    def setup(self):
        """Alert Processor - Test Setup"""
        # pylint: disable=attribute-defined-outside-init
        self.processor = AlertProcessor(_ARN)
        self.alert = Alert(
            'hello_world',
            {'abc': 123, NORMALIZATION_KEY: {}},
            {'slack:unit-test-channel'}
        )

    def test_init(self):
        """Alert Processor - Initialization"""
        assert_is_instance(self.processor.config, dict)
        assert_equal(self.processor.region, REGION)
        assert_equal(self.processor.account_id, ACCOUNT_ID)
        assert_equal(self.processor.prefix, PREFIX)

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_create_dispatcher_invalid(self, mock_logger):
        """Alert Processor - Create Dispatcher - Invalid Output"""
        assert_is_none(self.processor._create_dispatcher('helloworld'))
        mock_logger.error.called_once_with(ANY, 'helloworld')

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_create_dispatcher_output_doesnt_exist(self, mock_logger):
        """Alert Processor - Create Dispatcher - Output Does Not Exist"""
        assert_is_none(self.processor._create_dispatcher('slack:no-such-channel'))
        mock_logger.error.called_once_with(
            'The output \'%s\' does not exist!', 'slack:no-such-channel')

    def test_create_dispatcher(self):
        """Alert Processor - Create Dispatcher - Success"""
        dispatcher = self.processor._create_dispatcher('aws-s3:unit_test_bucket')
        assert_is_instance(dispatcher, OutputDispatcher)

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_send_alert_exception(self, mock_logger):
        """Alert Processor - Send Alert - Exception"""
        dispatcher = MagicMock()
        dispatcher.dispatch.side_effect = AttributeError
        alert = Alert('hello_world', {'abc': 123}, {'output'})
        output = 'slack:unit_test_channel'

        assert_false(AlertProcessor._send_alert(alert, output, dispatcher))
        mock_logger.assert_has_calls([
            call.info('Sending %s to %s', alert, output),
            call.exception('Exception when sending %s to %s. Alert:\n%s', alert, output, ANY)
        ])

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_send_alert(self, mock_logger):
        """Alert Processor - Send Alert - Success"""
        dispatcher = MagicMock()
        dispatcher.dispatch.return_value = True
        output = 'slack:unit_test_channel'

        assert_true(AlertProcessor._send_alert(self.alert, output, dispatcher))
        mock_logger.info.assert_called_once_with('Sending %s to %s', self.alert, output)
        dispatcher.dispatch.assert_called_once_with(self.alert, 'unit_test_channel')

    @patch.object(AlertProcessor, '_create_dispatcher')
    @patch.object(AlertProcessor, '_send_alert', return_value=True)
    def test_send_alerts_success(self, mock_send_alert, mock_create_dispatcher):
        """Alert Processor - Send Alerts Success"""
        result = self.processor._send_to_outputs(self.alert)
        mock_create_dispatcher.assert_called_once()
        mock_send_alert.assert_called_once()
        assert_equal({'slack:unit-test-channel': True}, result)
        assert_equal(self.alert.outputs, self.alert.outputs_sent)

    @patch.object(AlertProcessor, '_create_dispatcher')
    @patch.object(AlertProcessor, '_send_alert', return_value=False)
    def test_send_alerts_failure(self, mock_send_alert, mock_create_dispatcher):
        """Alert Processor - Send Alerts Failure"""
        result = self.processor._send_to_outputs(self.alert)
        mock_create_dispatcher.assert_called_once()
        mock_send_alert.assert_called_once()
        assert_equal({'slack:unit-test-channel': False}, result)
        assert_equal(set(), self.alert.outputs_sent)

    @patch.object(AlertProcessor, '_create_dispatcher', return_value=None)
    def test_send_alerts_skip_invalid_outputs(self, mock_create_dispatcher):
        """Alert Processor - Send Alerts With Invalid Outputs"""
        result = self.processor._send_to_outputs(self.alert)
        mock_create_dispatcher.assert_called_once()
        assert_equal({'slack:unit-test-channel': False}, result)

    def test_update_alerts_table_none(self):
        """Alert Processor - Update Alerts Table - Empty Results"""
        self.processor.alerts_table.delete_alert = MagicMock()
        self.processor.alerts_table.update_retry_outputs = MagicMock()
        self.processor._update_table(self.alert, {})
        self.processor.alerts_table.delete_alert.assert_not_called()
        self.processor.alerts_table.update_retry_outputs.assert_not_called()

    def test_update_alerts_table_delete(self):
        """Alert Processor - Update Alerts Table - Delete Item"""
        self.processor._update_table(self.alert, {'out1': True, 'out2': True})
        self.processor.alerts_table.delete_alerts.assert_called_once_with(
            [(self.alert.rule_name, self.alert.alert_id)])

    def test_update_alerts_table_update(self):
        """Alert Processor - Update Alerts Table - Update With Failed Outputs"""
        self.processor._update_table(self.alert, {'out1': True, 'out2': False, 'out3': False})
        self.processor.alerts_table.update_sent_outputs.assert_called_once_with(self.alert)

    @patch.object(AlertProcessor, '_send_to_outputs',
                  return_value={'slack:unit-test-channel': True})
    @patch.object(AlertProcessor, '_update_table')
    def test_run_full_event(self, mock_send_alerts, mock_update_table):
        """Alert Processor - Run With the Full Alert Record"""
        result = self.processor.run(self.alert.dynamo_record())
        assert_equal({'slack:unit-test-channel': True}, result)
        mock_send_alerts.assert_called_once()
        mock_update_table.assert_called_once()

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_run_invalid_alert(self, mock_logger):
        """Alert Processor - Run With an Invalid Alert"""
        result = self.processor.run({'Record': 'Nonsense'})
        assert_equal({}, result)
        mock_logger.exception.called_once_with('Invalid alert %s', {'Record': 'Nonsense'})

    @patch.object(AlertProcessor, '_send_to_outputs',
                  return_value={'slack:unit-test-channel': True})
    @patch.object(AlertProcessor, '_update_table')
    def test_run_get_alert_from_dynamo(self, mock_send_alerts, mock_update_table):
        """Alert Processor - Run With Just the Alert Key"""
        self.processor.alerts_table.get_alert_record = MagicMock(
            return_value=self.alert.dynamo_record())
        result = self.processor.run(self.alert.dynamo_key)
        assert_equal({'slack:unit-test-channel': True}, result)

        self.processor.alerts_table.get_alert_record.assert_called_once_with(
            self.alert.rule_name, self.alert.alert_id)
        mock_send_alerts.assert_called_once()
        mock_update_table.assert_called_once()

    @patch('stream_alert.alert_processor.main.LOGGER')
    def test_run_alert_does_not_exist(self, mock_logger):
        """Alert Processor - Run - Alert Does Not Exist"""
        self.processor.alerts_table.get_alert_record = MagicMock(return_value=None)
        self.processor.run(self.alert.dynamo_key)
        mock_logger.error.assert_called_once_with(
            '%s does not exist in the alerts table', self.alert.dynamo_key)

    @patch.dict(os.environ, {'ALERTS_TABLE': ALERTS_TABLE})
    @patch.object(AlertProcessor, 'run', return_value={'output': True})
    def test_handler(self, mock_run):
        """Alert Processor - Lambda Handler"""
        context = MagicMock()
        context.invoked_function_arn = _ARN
        event = {'AlertID': 'abc', 'RuleName': 'hello_world'}
        result = handler(event, context)
        assert_equal({'output': True}, result)
        mock_run.assert_called_once_with(event)