示例#1
0
    def _dispatch(self, alert, descriptor):
        """Send alert to Komand

        Publishing:
            By default this output sends the current publication to Komand.
            There is no "magic" field to "override" it: Simply publish what you want to send!

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        creds = self._load_creds(descriptor)
        if not creds:
            return False

        headers = {'Authorization': creds['komand_auth_token']}

        LOGGER.debug('sending alert to Komand')

        publication = compose_alert(alert, self, descriptor)
        resp = self._post_request(creds['url'], {'data': publication}, headers,
                                  False)

        return self._check_http_response(resp)
示例#2
0
def test_pretty_print_arrays():
    """Publishers - PagerDuty - PrettyPrintArrays"""
    alert = get_alert(
        context={'populate_fields': ['publishers', 'cb_server', 'staged']})
    alert.created = datetime(2019, 1, 1)
    alert.publishers = {
        'pagerduty': [
            'streamalert.shared.publisher.DefaultPublisher',
            'publishers.community.generic.populate_fields',
            'publishers.community.pagerduty.pagerduty_layout.PrettyPrintArrays'
        ]
    }
    output = MagicMock(spec=OutputDispatcher)
    output.__service__ = 'pagerduty'
    descriptor = 'unit_test_channel'

    publication = compose_alert(alert, output, descriptor)

    expectation = {
        'publishers': [{
            'pagerduty':
            ('streamalert.shared.publisher.DefaultPublisher\n\n----------\n\n'
             'publishers.community.generic.populate_fields\n\n----------\n\n'
             'publishers.community.pagerduty.pagerduty_layout.PrettyPrintArrays'
             )
        }],
        'staged':
        'False',
        'cb_server':
        'cbserver'
    }
    assert_equal(publication, expectation)
示例#3
0
def test_as_custom_details_ignores_custom_fields():
    """Publishers - PagerDuty - as_custom_details - Ignore Magic Keys"""
    alert = get_alert(context={'context': 'value'})
    alert.created = datetime(2019, 1, 1)
    alert.publishers = {
        'pagerduty': [
            'streamalert.shared.publisher.DefaultPublisher',
            'publishers.community.pagerduty.pagerduty_layout.ShortenTitle',
            'publishers.community.pagerduty.pagerduty_layout.as_custom_details',
        ]
    }
    output = MagicMock(spec=OutputDispatcher)
    output.__service__ = 'pagerduty'
    descriptor = 'unit_test_channel'

    publication = compose_alert(alert, output, descriptor)

    # We don't care about the entire payload; let's check a few top-level keys we know
    # are supposed to be here..
    assert_true(publication['source_entity'])
    assert_true(publication['outputs'])
    assert_true(publication['log_source'])

    # Check that the title keys exists
    assert_true(publication['@pagerduty.description'])

    # now check that the details key exists
    assert_true(publication['@pagerduty.details'])

    # And check that it has no magic keys
    assert_false('@pagerduty.description' in publication['@pagerduty.details'])
    assert_false('@pagerduty-v2.summary' in publication['@pagerduty.details'])
示例#4
0
def test_attach_image():
    """Publishers - PagerDuty - AttachImage"""
    alert = get_alert()
    alert.created = datetime(2019, 1, 1)
    alert.publishers = {
        'pagerduty':
        ['publishers.community.pagerduty.pagerduty_layout.AttachImage']
    }

    output = MagicMock(spec=OutputDispatcher)
    output.__service__ = 'pagerduty'
    descriptor = 'unit_test_channel'

    publication = compose_alert(alert, output, descriptor)

    expectation = {
        '@pagerduty-v2.images': [{
            'src': 'https://streamalert.io/en/stable/_images/sa-banner.png',
            'alt': 'StreamAlert Docs',
            'href': 'https://streamalert.io/en/stable/'
        }],
        '@pagerduty.contexts': [{
            'src': 'https://streamalert.io/en/stable/_images/sa-banner.png',
            'type': 'image'
        }]
    }
    assert_equal(publication, expectation)
示例#5
0
    def test_override_default_body_html(self):
        """SESOutput - Override body html"""
        rule_name = 'test_override_body_html'

        alert = get_random_alert(10, rule_name, omit_rule_desc=True)
        output = MagicMock(spec=SESOutput)
        alert_publication = compose_alert(alert, output, self.DESCRIPTOR)

        alert_publication['@aws-ses.body'] = {
            'html': '<head><body><p>i am a test</p></body></head>'
        }
        msg = SESOutput._build_email(alert, alert_publication, self.CREDS)

        # Tests
        payloads = msg.get_payload()
        for payload in payloads:
            if payload.is_multipart():
                # should only be one payload on this multipart
                html = payload.get_payload()[0].get_payload()
                assert_equal(html,
                             '<head><body><p>i am a test</p></body></head>')
                break
        else:
            # Raise an error if no payload of type MIMEText is found
            raise AssertionError
示例#6
0
 def test_default_publisher(self):
     """AlertPublisher - DefaultPublisher - Positive Case"""
     publication = compose_alert(self._alert, self._output, 'test')
     expectation = {
         'publishers': ['streamalert.shared.publisher.DefaultPublisher'],
         'source_entity': 'corp-prefix.prod.cb.region',
         'outputs': ['slack:unit_test_channel'],
         'cluster': '',
         'rule_description': 'Info about this rule and what actions to take',
         'log_type': 'json',
         'rule_name': 'cb_binarystore_file_added',
         'source_service': 's3',
         'created': '2019-01-01T00:00:00.000000Z',
         'log_source': 'carbonblack:binarystore.file.added',
         'id': '79192344-4a6d-4850-8d06-9c3fef1060a4',
         'record': {
             'compressed_size': '9982',
             'node_id': '1',
             'cb_server': 'cbserver',
             'timestamp': '1496947381.18',
             'md5': '0F9AA55DA3BDE84B35656AD8911A22E1',
             'type': 'binarystore.file.added',
             'file_path': '/tmp/5DA/AD8/0F9AA55DA3BDE84B35656AD8911A22E1.zip',
             'size': '21504'
         },
         'context': {'context': 'value'},
         'staged': False
     }
     assert_equal(publication, expectation)
示例#7
0
    def _dispatch(self, alert, descriptor):
        """Send alert to an SNS topic

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        # SNS topics can only be accessed via their ARN
        topic_name = self.config[self.__service__][descriptor]
        topic_arn = 'arn:aws:sns:{}:{}:{}'.format(self.region, self.account_id,
                                                  topic_name)
        topic = boto3.resource('sns', region_name=self.region).Topic(topic_arn)

        publication = compose_alert(alert, self, descriptor)

        # Presentation defaults
        default_subject = '{} triggered alert {}'.format(
            alert.rule_name, alert.alert_id)
        default_message = json.dumps(publication, indent=2, sort_keys=True)

        # Published presentation fields
        # Subject must be < 100 characters long;
        subject = elide_string_middle(
            publication.get('@aws-sns.topic', default_subject), 99)
        message = publication.get('@aws-sns.message', default_message)

        topic.publish(Message=message, Subject=subject)

        return True
示例#8
0
    def test_remove_fields(self):
        """AlertPublisher - enumerate_fields - enforce alphabetical order"""
        publication = compose_alert(self._alert, self._output, 'test')

        expectation = {
            'staged': False,
            'source_entity': 'corp-prefix.prod.cb.region',
            'rule_name': 'cb_binarystore_file_added',
            'created': '2019-01-01T00:00:00.000000Z',
            'log_source': 'carbonblack:binarystore.file.added',
            'source_service': 's3',
            'id': '79192344-4a6d-4850-8d06-9c3fef1060a4',
            'rule_description': 'Info about this rule and what actions to take',
            'record': {
                'compressed_size': '9982',
                'timestamp': '1496947381.18',
                'node_id': '1',
                'cb_server': 'cbserver',
                'size': '21504',
                'file_path': '/tmp/5DA/AD8/0F9AA55DA3BDE84B35656AD8911A22E1.zip',
                'md5': '0F9AA55DA3BDE84B35656AD8911A22E1'
            }
        }

        assert_equal(publication, expectation)
示例#9
0
    def test_enumerate_fields(self):
        """AlertPublisher - enumerate_fields"""
        publication = compose_alert(self._alert, self._output, 'test')

        expectation = {
            'cluster': '',
            'context.context1': 'value',
            'context.attribs[0].type': 'Name',
            'context.attribs[0].value': 'Bob',
            'context.attribs[1].type': 'Age',
            'context.attribs[1].value': '42',
            'context.attribs[2].value': 'Software engineer',
            'context.attribs[2].type': 'Profession',
            'created': '2019-01-01T00:00:00.000000Z',
            'id': '79192344-4a6d-4850-8d06-9c3fef1060a4',
            'log_source': 'carbonblack:binarystore.file.added',
            'log_type': 'json',
            'outputs[0]': 'slack:unit_test_channel',
            'publishers[0]': 'streamalert.shared.publisher.DefaultPublisher',
            'publishers[1]': 'publishers.community.generic.enumerate_fields',
            'record.timestamp': '1496947381.18',
            'record.compressed_size': '9982',
            'record.cb_server': 'cbserver',
            'record.file_path': '/tmp/5DA/AD8/0F9AA55DA3BDE84B35656AD8911A22E1.zip',
            'record.md5': '0F9AA55DA3BDE84B35656AD8911A22E1',
            'record.node_id': '1',
            'record.size': '21504',
            'record.type': 'binarystore.file.added',
            'rule_description': 'Info about this rule and what actions to take',
            'rule_name': 'cb_binarystore_file_added',
            'source_entity': 'corp-prefix.prod.cb.region',
            'source_service': 's3',
            'staged': False,
        }
        assert_equal(publication, expectation)
示例#10
0
    def _run_publishers(alert):
        """Runs publishers for all currently configured outputs on the given alert

        Args:
            - alert (Alert): The alert

        Returns:
            dict: A dict keyed by output:descriptor strings, mapped to nested dicts.
                  The nested dicts have 2 keys:
                  - publication (dict): The dict publication
                  - success (bool): True if the publishing finished, False if it errored.
        """
        configured_outputs = alert.outputs

        results = {}
        for configured_output in configured_outputs:
            [output_name, descriptor] = configured_output.split(':')

            try:
                output = MagicMock(spec=OutputDispatcher,
                                   __service__=output_name)
                results[configured_output] = {
                    'publication': compose_alert(alert, output, descriptor),
                    'success': True,
                }
            except (RuntimeError, TypeError, NameError) as err:
                results[configured_output] = {
                    'success': False,
                    'error': err,
                }
        return results
示例#11
0
    def _dispatch(self, alert, descriptor):
        """Send a new Incident to Demisto

        Publishing:
            Demisto offers a suite of default incident values. You can override any of the
            following:

            - @demisto.incident_type (str):

            - @demisto.severity (str):
                    Controls the severity of the incident. Any of the following:
                    'info', 'informational', 'low', 'med', 'medium', 'high', 'critical', 'unknown'

            - @demisto.owner (str):
                    Controls which name shows up under the owner. This can be any name, even of
                    users that are not registered on Demisto. Incidents can be filtered by name.

            - @demisto.details (str):
                    A string that briefly describes the nature of the incident and how to respond.

            - @demisto.incident_name (str):
                    Incident name shows up as the title of the Incident.

            - @demisto.label_data (dict):
                    By default, this output sends the entire publication into the Demisto labels
                    section, where the label names are the keys of the publication and the label
                    values are the values of the publication.

                    For deeply nested dictionary publications, the label names become the full path
                    of all nest dictionary keys, concatenated with periods (".").

                    By providing this override field, you can send a different dict of data to
                    Demisto, other than the entire publication. Just like in the default case,
                    if this provided dict is deeply nested, the keys will be flattened.


        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        creds = self._load_creds(descriptor)
        if not creds:
            return False

        request = DemistoRequestAssembler.assemble(
            alert, compose_alert(alert, self, descriptor))
        integration = DemistoApiIntegration(creds, self)

        LOGGER.debug('Sending alert to Demisto: %s', creds['url'])

        try:
            integration.send(request)
            return True
        except OutputRequestFailure as e:
            LOGGER.exception('Failed to create Demisto incident: %s.', e)
            return False
示例#12
0
    def _dispatch(self, alert, descriptor):
        """Send alert to a Lambda function

        The alert gets dumped to a JSON string to be sent to the Lambda function

        Publishing:
            By default this output sends the JSON-serialized alert record as the payload to the
            lambda function. You can override this:

            - @aws-lambda.alert_data (dict):
                    Overrides the alert record. Will instead send this dict, JSON-serialized, to
                    Lambda as the payload.

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        creds = self._load_creds(descriptor)
        if not creds:
            LOGGER.error("No credentials found for descriptor: %s", descriptor)
            return False

        # Create the publication
        publication = compose_alert(alert, self, descriptor)

        # Defaults
        default_alert_data = alert.record

        # Override with publisher
        alert_data = publication.get('@aws-lambda.alert_data',
                                     default_alert_data)
        alert_string = json.dumps(alert_data, separators=(',', ':'))

        client = self._build_client(creds)

        function_name = creds['lambda_function_arn']
        qualifier = creds.get('function_qualifier', False)

        LOGGER.debug('Sending alert to Lambda function %s', function_name)
        invocation_opts = {
            'FunctionName': function_name,
            'InvocationType': 'Event',
            'Payload': alert_string,
        }

        # Use the qualifier if it's available. Passing an empty qualifier in
        # with `Qualifier=''` or `Qualifier=None` does not work
        if qualifier:
            invocation_opts['Qualifier'] = qualifier

        client.invoke(**invocation_opts)

        return True
示例#13
0
    def _dispatch(self, alert, descriptor):
        """Send alert to a Kinesis Firehose Delivery Stream

        Publishing:
            By default this output sends the current publication in JSON to Kinesis.
            There is no "magic" field to "override" it: Simply publish what you want to send!

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        @backoff.on_exception(backoff.fibo,
                              ClientError,
                              max_tries=self.MAX_BACKOFF_ATTEMPTS,
                              jitter=backoff.full_jitter,
                              on_backoff=backoff_handler(),
                              on_success=success_handler(),
                              on_giveup=giveup_handler())
        def _firehose_request_wrapper(json_alert, delivery_stream):
            """Make the PutRecord request to Kinesis Firehose with backoff

            Args:
                json_alert (str): The JSON dumped alert body
                delivery_stream (str): The Firehose Delivery Stream to send to

            Returns:
                dict: Firehose response in the format below
                    {'RecordId': 'string'}
            """
            self.__aws_client__.put_record(DeliveryStreamName=delivery_stream,
                                           Record={'Data': json_alert})

        if self.__aws_client__ is None:
            self.__aws_client__ = boto3.client('firehose',
                                               region_name=self.region)

        publication = compose_alert(alert, self, descriptor)

        json_alert = json.dumps(publication, separators=(',', ':')) + '\n'
        if len(json_alert) > self.MAX_RECORD_SIZE:
            LOGGER.error('Alert too large to send to Firehose: \n%s...',
                         json_alert[0:1000])
            return False

        delivery_stream = self.config[self.__service__][descriptor]
        LOGGER.info('Sending %s to aws-firehose:%s', alert, delivery_stream)

        _firehose_request_wrapper(json_alert, delivery_stream)
        LOGGER.info('%s successfully sent to aws-firehose:%s', alert,
                    delivery_stream)

        return True
示例#14
0
    def _dispatch(self, alert, descriptor):
        """Send alert to Cloudwatch Logger for Lambda

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor
        """
        publication = compose_alert(alert, self, descriptor)
        LOGGER.info('New Alert:\n%s', json.dumps(publication, indent=2))

        return True
示例#15
0
 def test_max_attachments(self, log_mock):
     """SlackOutput - Max Attachment Reached"""
     alert = get_alert()
     alert.record = {'info': 'test' * 20000}
     output = MagicMock(spec=SlackOutput)
     alert_publication = compose_alert(alert, output, 'asdf')
     SlackOutput._format_default_attachments(alert, alert_publication,
                                             'foo')
     log_mock.assert_called_with(
         '%s: %d-part message truncated to %d parts', alert_publication, 21,
         20)
示例#16
0
    def test_publish(self):
        """AlertPublisher - StringifyArrays - publish"""
        publication = compose_alert(self._alert, self._output, 'test')

        expectation = {
            'not_array': ['a', {'b': 'c'}, 'd'],
            'array': 'a\nb\nc',
            'nest': {'deep_array': 'a\nb\nc'}
        }

        assert_equal(publication['context'], expectation)
示例#17
0
    def test_build_email_from_email(self):
        """SESOutput - Test sender"""
        rule_name = 'test_sender'

        alert = get_random_alert(10, rule_name, omit_rule_desc=True)
        output = MagicMock(spec=SESOutput)
        alert_publication = compose_alert(alert, output, self.DESCRIPTOR)

        msg = SESOutput._build_email(alert, alert_publication, self.CREDS)

        # verify to_emails is set
        assert_equal(msg['From'], self.CREDS['from_email'])
示例#18
0
    def test_remove_fields(self):
        """AlertPublisher - populate_fields"""
        publication = compose_alert(self._alert, self._output, 'test')

        expectation = {
            'compressed_size': ['9982'],
            'oof': [],
            'id': ['79192344-4a6d-4850-8d06-9c3fef1060a4'],
            'multi_field': [1, 2]
        }

        assert_equal(publication, expectation)
示例#19
0
    def test_format_message_default_rule_description(self):
        """SlackOutput - Format Message, Default Rule Description"""
        rule_name = 'test_empty_rule_description'
        alert = get_random_alert(10, rule_name, True)
        output = MagicMock(spec=SlackOutput)
        alert_publication = compose_alert(alert, output, 'asdf')
        loaded_message = SlackOutput._format_message(alert, alert_publication)

        # tests
        default_rule_description = '*Rule Description:*\nNo rule description provided\n'
        assert_equal(loaded_message['attachments'][0]['pretext'],
                     default_rule_description)
示例#20
0
    def test_format_message_additional_sections(self, _, alert_section_mock,
                                                record_section_mock,
                                                add_sections_mock):
        """TeamsOutput - Format Message - Additional card sections"""
        rule_name = 'test_rule_default'
        alert = get_random_alert(25, rule_name)
        output = MagicMock(spec=TeamsOutput)
        alert_publication = compose_alert(alert, output, 'asdf')
        alert_publication['@teams.with_record'] = True

        # Setup test_card_section
        test_card_section = Mock()

        alert_section_mock.return_value = 'Alert_Section'
        record_section_mock.return_value = 'Record_Section'

        add_sections_mock.side_effect = (lambda teams_card, _: teams_card)

        # Pass card section in via Publisher
        alert_publication['@teams.additional_card_sections'] = [
            test_card_section
        ]

        loaded_message = self._dispatcher._format_message(
            alert, alert_publication, self.CREDS['url'])

        # Tests

        # Verify title
        loaded_message.title.assert_called()
        loaded_message.title.assert_called_with(
            'StreamAlert Rule Triggered: {}'.format(alert.rule_name))

        # Verify text/description
        loaded_message.text.assert_called()
        loaded_message.text.assert_called_with(alert.rule_description)

        # Verify card color
        loaded_message.color.assert_called()
        loaded_message.color.assert_called_with('E81123')

        # Verify Sections
        alert_section_mock.assert_called()
        alert_section_mock.assert_called_with(alert)
        record_section_mock.assert_called()
        record_section_mock.assert_called_with(alert.record)
        add_sections_mock.assert_called()
        assert_equal(add_sections_mock.call_count, 1)
        loaded_message.addSection.assert_called()
        loaded_message.addSection.assert_has_calls(
            [call('Alert_Section'),
             call('Record_Section')], any_order=False)
示例#21
0
    def test_format_message_custom_attachment(self):
        """SlackOutput - Format Message, Custom Attachment"""
        rule_name = 'test_empty_rule_description'
        alert = get_random_alert(10, rule_name, True)
        output = MagicMock(spec=SlackOutput)
        alert_publication = compose_alert(alert, output, 'asdf')
        alert_publication['@slack.attachments'] = [{'text': 'aasdfkjadfj'}]

        loaded_message = SlackOutput._format_message(alert, alert_publication)

        # tests
        assert_equal(len(loaded_message['attachments']), 1)
        assert_equal(loaded_message['attachments'][0]['text'], 'aasdfkjadfj')
示例#22
0
    def test_build_email_to_emails_single(self):
        """SESOutput - Single recipient"""
        rule_name = 'test_single_recipient'

        alert = get_random_alert(10, rule_name, omit_rule_desc=True)

        output = MagicMock(spec=SESOutput)
        alert_publication = compose_alert(alert, output, self.DESCRIPTOR)

        msg = SESOutput._build_email(alert, alert_publication, self.CREDS)

        # verify to_emails is set
        assert_equal(msg['To'], '*****@*****.**')
示例#23
0
    def test_subject_override(self):
        """SESOutput - Change default Subject"""
        rule_name = 'test_subject_override'

        alert = get_random_alert(10, rule_name, omit_rule_desc=True)
        output = MagicMock(spec=SESOutput)
        alert_publication = compose_alert(alert, output, 'asdf')
        alert_publication['@aws-ses.subject'] = 'this is a test'

        msg = SESOutput._build_email(alert, alert_publication, self.CREDS)

        # check subject override worked
        assert_equal(msg['Subject'], 'this is a test')
示例#24
0
    def test_format_message_single(self):
        """SlackOutput - Format Single Message - Slack"""
        rule_name = 'test_rule_single'
        alert = get_random_alert(25, rule_name)
        output = MagicMock(spec=SlackOutput)
        alert_publication = compose_alert(alert, output, 'asdf')
        loaded_message = SlackOutput._format_message(alert, alert_publication)

        # tests
        assert_set_equal(set(loaded_message.keys()),
                         {'text', 'mrkdwn', 'attachments'})
        assert_equal(loaded_message['text'],
                     '*StreamAlert Rule Triggered: test_rule_single*')
        assert_equal(len(loaded_message['attachments']), 1)
示例#25
0
    def _dispatch(self, alert, descriptor):
        """Send alert to an SES Output

        Publishing:
            By default the aws-ses output sends an email comprising some default intro text
            and an attachment containing:
            * alert.record (record.json)

            - @aws-ses.subject (str):
                Replaces the default subject
            - @aws-ses.attach_record (bool):
                True (default): Attach the alert.record to the email
                False: Don't attach the alert.record to the email
            - @aws-ses.attachments (dict):
                A dict of attachments to include in the message.
            - @aws-ses.body (str):
                Replaces the default intro text

                @see cls._construct_body() for some insight into how you can customize the body

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        creds = self._load_creds(descriptor)
        if not creds:
            return False

        publication = compose_alert(alert, self, descriptor)

        msg = self._build_email(alert, publication, creds)

        ses = boto3.client('ses', region_name=self.region)

        try:
            response = ses.send_raw_email(
                Source=msg['From'],
                Destinations=msg['To'].split(','),
                RawMessage={'Data': msg.as_string()},
            )
        except ClientError as e:
            LOGGER.error(e.response['Error']['Message'])
            return False
        else:
            LOGGER.info('Email sent! Message ID: %s', response['MessageId'])
            return True
示例#26
0
def test_as_custom_details_default():
    """Publishers - PagerDuty - as_custom_details - Default"""
    alert = get_alert(context={'context': 'value'})
    alert.created = datetime(2019, 1, 1)
    alert.publishers = {
        'pagerduty': [
            'streamalert.shared.publisher.DefaultPublisher',
            'publishers.community.pagerduty.pagerduty_layout.as_custom_fields'
        ]
    }
    output = MagicMock(spec=OutputDispatcher)
    output.__service__ = 'pagerduty'
    descriptor = 'unit_test_channel'

    publication = compose_alert(alert, output, descriptor)

    expectation = {
        'publishers': {
            'pagerduty': [
                'streamalert.shared.publisher.DefaultPublisher',
                'publishers.community.pagerduty.pagerduty_layout.as_custom_fields'
            ]
        },
        'source_entity': 'corp-prefix.prod.cb.region',
        'outputs': ['slack:unit_test_channel'],
        'cluster': '',
        'rule_description': 'Info about this rule and what actions to take',
        'log_type': 'json',
        'rule_name': 'cb_binarystore_file_added',
        'source_service': 's3',
        'created': '2019-01-01T00:00:00.000000Z',
        'log_source': 'carbonblack:binarystore.file.added',
        'id': '79192344-4a6d-4850-8d06-9c3fef1060a4',
        'record': {
            'compressed_size': '9982',
            'node_id': '1',
            'cb_server': 'cbserver',
            'timestamp': '1496947381.18',
            'md5': '0F9AA55DA3BDE84B35656AD8911A22E1',
            'type': 'binarystore.file.added',
            'file_path': '/tmp/5DA/AD8/0F9AA55DA3BDE84B35656AD8911A22E1.zip',
            'size': '21504'
        },
        'context': {
            'context': 'value'
        },
        'staged': False
    }
    assert_equal(publication, expectation)
示例#27
0
    def test_format_message_custom_text(self):
        """SlackOutput - Format Single Message - Custom Text"""
        rule_name = 'test_rule_single'
        alert = get_random_alert(25, rule_name)
        output = MagicMock(spec=SlackOutput)
        alert_publication = compose_alert(alert, output, 'asdf')
        alert_publication['@slack.text'] = 'Lorem ipsum foobar'

        loaded_message = SlackOutput._format_message(alert, alert_publication)

        # tests
        assert_set_equal(set(loaded_message.keys()),
                         {'text', 'mrkdwn', 'attachments'})
        assert_equal(loaded_message['text'], 'Lorem ipsum foobar')
        assert_equal(len(loaded_message['attachments']), 1)
示例#28
0
 def test_default_publisher(self):
     """AlertPublisher - add_record - Positive Case"""
     publication = compose_alert(self._alert, self._output, 'test')
     expectation = {
         'record': {
             'compressed_size': '9982',
             'node_id': '1',
             'cb_server': 'cbserver',
             'timestamp': '1496947381.18',
             'md5': '0F9AA55DA3BDE84B35656AD8911A22E1',
             'type': 'binarystore.file.added',
             'file_path': '/tmp/5DA/AD8/0F9AA55DA3BDE84B35656AD8911A22E1.zip',
             'size': '21504'
         },
     }
     assert_equal(publication, expectation)
示例#29
0
    def test_build_email_to_emails_multiple(self):
        """SESOutput - Multiple recipients"""
        rule_name = 'test_multiple_recipients'

        alert = get_random_alert(10, rule_name, omit_rule_desc=True)
        output = MagicMock(spec=SESOutput)
        alert_publication = compose_alert(alert, output, self.DESCRIPTOR)

        creds = {
            'to_emails': '[email protected],[email protected]',
            'from_email': '*****@*****.**'
        }
        msg = SESOutput._build_email(alert, alert_publication, creds)

        # verify to_emails is set
        assert_equal(msg["To"], creds["to_emails"])
示例#30
0
    def _dispatch(self, alert, descriptor):
        """Send alert to Phantom

        Publishing:
            By default this output sends the current publication in as JSON to Phantom.
            There is no "magic" field to "override" it: Simply publish what you want to send!

        Args:
            alert (Alert): Alert instance which triggered a rule
            descriptor (str): Output descriptor

        Returns:
            bool: True if alert was sent successfully, False otherwise
        """
        creds = self._load_creds(descriptor)
        if not creds:
            return False

        publication = compose_alert(alert, self, descriptor)
        record = alert.record

        headers = {"ph-auth-token": creds['ph_auth_token']}
        container_id = self._setup_container(alert.rule_name,
                                             alert.rule_description,
                                             creds['url'], headers)

        LOGGER.debug('sending alert to Phantom container with id %s',
                     container_id)

        if not container_id:
            return False

        artifact = {
            'cef': record,
            'container_id': container_id,
            'data': publication,
            'name': 'Phantom Artifact',
            'label': 'Alert'
        }
        artifact_url = os.path.join(creds['url'], self.ARTIFACT_ENDPOINT)
        try:
            self._post_request_retry(artifact_url, artifact, headers, False)
        except OutputRequestFailure:
            return False

        return True