Exemple #1
0
def test_alert_text_global_substitution(ea):
    rule = ea.rules[0].copy()
    rule['owner'] = 'the owner from rule'
    rule['priority'] = 'priority from rule'
    rule['abc'] = 'abc from rule'
    rule['alert_text'] = 'Priority: {0}; Owner: {1}; Abc: {2}'
    rule['alert_text_args'] = ['priority', 'owner', 'abc']

    match = {
        '@timestamp': '2016-01-01',
        'field': 'field_value',
        'abc': 'abc from match',
    }

    alert_text = unicode(BasicMatchString(rule, match))
    assert 'Priority: priority from rule' in alert_text
    assert 'Owner: the owner from rule' in alert_text

    # When the key exists in both places, it will come from the match
    assert 'Abc: abc from match' in alert_text
Exemple #2
0
def test_opsgenie_priority_none():
    rule = {
        'name': 'Opsgenie Details',
        'type': mock_rule(),
        'opsgenie_account': 'genies',
        'opsgenie_key': 'ogkey',
        'opsgenie_details': {
            'Message': {'field': 'message'},
            'Missing': {'field': 'missing'}
        },
        'opsgenie_priority': 'abc'
    }
    match = {
        'message': 'Testing',
        '@timestamp': '2014-10-31T00:00:00'
    }
    alert = OpsGenieAlerter(rule)

    with mock.patch('requests.post') as mock_post_request:
        alert.alert([match])

    mock_post_request.assert_called_once_with(
        'https://api.opsgenie.com/v2/alerts',
        headers={
            'Content-Type': 'application/json',
            'Authorization': 'GenieKey ogkey'
        },
        json=mock.ANY,
        proxies=None
    )

    expected_json = {
        'description': BasicMatchString(rule, match).__str__(),
        'details': {'Message': 'Testing'},
        'message': 'ElastAlert: Opsgenie Details',
        'source': 'ElastAlert',
        'tags': ['ElastAlert', 'Opsgenie Details'],
        'user': '******'
    }
    actual_json = mock_post_request.call_args_list[0][1]['json']
    assert expected_json == actual_json
def test_rocket_chat_uses_custom_timeout():
    rule = {
        'name': 'Test Rule',
        'type': 'any',
        'rocket_chat_webhook_url': 'http://please.dontgohere.rocketchat',
        'alert_subject': 'Cool subject',
        'alert': [],
        'rocket_chat_timeout': 20
    }
    rules_loader = FileRulesLoader({})
    rules_loader.load_modules(rule)
    alert = RocketChatAlerter(rule)
    match = {'@timestamp': '2016-01-01T00:00:00', 'somefield': 'foobarbaz'}
    with mock.patch('requests.post') as mock_post_request:
        alert.alert([match])

    expected_data = {
        'username':
        '******',
        'channel':
        '',
        'emoji':
        ':ghost:',
        'attachments': [{
            'color': 'danger',
            'title': 'Cool subject',
            'text': BasicMatchString(rule, match).__str__(),
            'fields': []
        }],
        'text':
        ''
    }
    mock_post_request.assert_called_once_with(
        rule['rocket_chat_webhook_url'],
        data=mock.ANY,
        headers={'content-type': 'application/json'},
        proxies=None,
        verify=True,
        timeout=20)
    assert expected_data == json.loads(
        mock_post_request.call_args_list[0][1]['data'])
Exemple #4
0
def test_opsgenie_details_with_environment_variable_replacement(environ):
    environ.update({
        'TEST_VAR': 'Bar'
    })
    rule = {
        'name': 'Opsgenie Details',
        'type': mock_rule(),
        'opsgenie_account': 'genies',
        'opsgenie_key': 'ogkey',
        'opsgenie_details': {'Foo': '$TEST_VAR'}
    }
    match = {
        '@timestamp': '2014-10-31T00:00:00'
    }
    alert = OpsGenieAlerter(rule)

    with mock.patch('requests.post') as mock_post_request:
        alert.alert([match])

    mock_post_request.assert_called_once_with(
        'https://api.opsgenie.com/v2/alerts',
        headers={
            'Content-Type': 'application/json',
            'Authorization': 'GenieKey ogkey'
        },
        json=mock.ANY,
        proxies=None
    )

    expected_json = {
        'description': BasicMatchString(rule, match).__str__(),
        'details': {'Foo': 'Bar'},
        'message': 'ElastAlert: Opsgenie Details',
        'priority': None,
        'source': 'ElastAlert',
        'tags': ['ElastAlert', 'Opsgenie Details'],
        'user': '******'
    }
    actual_json = mock_post_request.call_args_list[0][1]['json']
    assert expected_json == actual_json
Exemple #5
0
def test_slack_uses_custom_slack_channel():
    rule = {
        'name': 'Test Rule',
        'type': 'any',
        'slack_webhook_url': ['http://please.dontgohere.slack'],
        'slack_channel_override': '#test-alert',
        'alert': []
    }
    load_modules(rule)
    alert = SlackAlerter(rule)
    match = {'@timestamp': '2016-01-01T00:00:00', 'somefield': 'foobarbaz'}
    with mock.patch('requests.post') as mock_post_request:
        alert.alert([match])

    expected_data = {
        'username':
        '******',
        'channel':
        '#test-alert',
        'icon_emoji':
        ':ghost:',
        'attachments': [{
            'color': 'danger',
            'title': rule['name'],
            'text': BasicMatchString(rule, match).__str__(),
            'mrkdwn_in': ['text', 'pretext'],
            'fields': []
        }],
        'text':
        '',
        'parse':
        'none'
    }
    mock_post_request.assert_called_once_with(
        rule['slack_webhook_url'][0],
        data=mock.ANY,
        headers={'content-type': 'application/json'},
        proxies=None)
    assert expected_data == json.loads(
        mock_post_request.call_args_list[0][1]['data'])
Exemple #6
0
def test_basic_match_string(ea):
    ea.rules[0]['top_count_keys'] = ['username']
    match = {'@timestamp': '1918-01-17', 'field': 'value', 'top_events_username': {'bob': 10, 'mallory': 5}}
    alert_text = unicode(BasicMatchString(ea.rules[0], match))
    assert 'anytest' in alert_text
    assert 'some stuff happened' in alert_text
    assert 'username' in alert_text
    assert 'bob: 10' in alert_text
    assert 'field: value' in alert_text

    # Non serializable objects don't cause errors
    match['non-serializable'] = {open: 10}
    alert_text = unicode(BasicMatchString(ea.rules[0], match))

    # unicode objects dont cause errors
    match['snowman'] = u'☃'
    alert_text = unicode(BasicMatchString(ea.rules[0], match))

    # Pretty printed objects
    match.pop('non-serializable')
    match['object'] = {'this': {'that': [1, 2, "3"]}}
    alert_text = unicode(BasicMatchString(ea.rules[0], match))
    assert '"this": {\n        "that": [\n            1, \n            2, \n            "3"\n        ]\n    }' in alert_text

    ea.rules[0]['alert_text'] = 'custom text'
    alert_text = unicode(BasicMatchString(ea.rules[0], match))
    assert 'custom text' in alert_text
    assert 'anytest' not in alert_text

    ea.rules[0]['alert_text_type'] = 'alert_text_only'
    alert_text = unicode(BasicMatchString(ea.rules[0], match))
    assert 'custom text' in alert_text
    assert 'some stuff happened' not in alert_text
    assert 'username' not in alert_text
    assert 'field: value' not in alert_text

    ea.rules[0]['alert_text_type'] = 'exclude_fields'
    alert_text = unicode(BasicMatchString(ea.rules[0], match))
    assert 'custom text' in alert_text
    assert 'some stuff happened' in alert_text
    assert 'username' in alert_text
    assert 'field: value' not in alert_text
Exemple #7
0
    def alert(self, matches):
        for match in matches:
            # Parse everything into description.
            description = str(BasicMatchString(self.rule, match))

        # Set proper headers
        headers = {
            "Content-Type": "application/json",
            "Accept": "application/json;charset=utf-8"
        }
        proxies = {
            'https': self.servicenow_proxy
        } if self.servicenow_proxy else None
        payload = {
            "description": description,
            "short_description": self.rule['short_description'],
            "comments": self.rule['comments'],
            "assignment_group": self.rule['assignment_group'],
            "category": self.rule['category'],
            "subcategory": self.rule['subcategory'],
            "cmdb_ci": self.rule['cmdb_ci'],
            "caller_id": self.rule["caller_id"]
        }
        if self.impact != None:
            payload["impact"] = self.impact
        if self.urgency != None:
            payload["urgency"] = self.urgency
        try:
            response = requests.post(self.servicenow_rest_url,
                                     auth=(self.rule['username'],
                                           self.rule['password']),
                                     headers=headers,
                                     data=json.dumps(payload,
                                                     cls=DateTimeEncoder),
                                     proxies=proxies)
            response.raise_for_status()
        except RequestException as e:
            raise EAException("Error posting to ServiceNow: %s" % e)
        elastalert_logger.info("Alert sent to ServiceNow")
Exemple #8
0
def test_alert_text_kw_global_substitution(ea):
    rule = ea.rules[0].copy()
    rule['foo_rule'] = 'foo from rule'
    rule['owner'] = 'the owner from rule'
    rule['abc'] = 'abc from rule'
    rule['alert_text'] = 'Owner: {owner}; Foo: {foo}; Abc: {abc}'
    rule['alert_text_kw'] = {
        'owner': 'owner',
        'foo_rule': 'foo',
        'abc': 'abc',
    }

    match = {
        '@timestamp': '2016-01-01',
        'field': 'field_value',
        'abc': 'abc from match',
    }

    alert_text = unicode(BasicMatchString(rule, match))
    assert 'Owner: the owner from rule' in alert_text
    assert 'Foo: foo from rule' in alert_text

    # When the key exists in both places, it will come from the match
    assert 'Abc: abc from match' in alert_text
Exemple #9
0
 def alert(self, matches):
     body = ''
     for match in matches:
         body += str(BasicMatchString(self.rule, match))
         if len(matches) > 1:
             body += '\n----------------------------------------\n'
     if len(body) > 999:
         body = body[
             0:
             900] + '\n *message was cropped according to line notify embed description limits!*'
     # post to Line Notify
     headers = {
         "Content-Type": "application/x-www-form-urlencoded",
         "Authorization": "Bearer {}".format(self.linenotify_access_token)
     }
     payload = {"message": body}
     try:
         response = requests.post("https://notify-api.line.me/api/notify",
                                  data=payload,
                                  headers=headers)
         response.raise_for_status()
     except RequestException as e:
         raise EAException("Error posting to Line Notify: %s" % e)
     elastalert_logger.info("Alert sent to Line Notify")
Exemple #10
0
def test_alert_text_jinja(ea):
    rule = ea.rules[0].copy()
    rule['foo_rule'] = 'foo from rule'
    rule['owner'] = 'the owner from rule'
    rule['abc'] = 'abc from rule'
    rule['alert_text'] = 'Owner: {{owner}}; Foo: {{_data["foo_rule"]}}; Abc: {{abc}}; Xyz: {{_data["xyz"]}}'
    rule['alert_text_type'] = "alert_text_jinja"
    rule['jinja_root_name'] = "_data"
    rule['jinja_template'] = Template(str(rule['alert_text']))

    match = {
        '@timestamp': '2016-01-01',
        'field': 'field_value',
        'abc': 'abc from match',
        'xyz': 'from match'
    }

    alert_text = str(BasicMatchString(rule, match))
    assert 'Owner: the owner from rule' in alert_text
    assert 'Foo: foo from rule' in alert_text
    assert 'Xyz: from match' in alert_text

    # When the key exists in both places, it will come from the match
    assert 'Abc: abc from match' in alert_text
Exemple #11
0
def test_slack_alert_fields():
    rule = {
        'name': 'Test Rule',
        'type': 'any',
        'slack_webhook_url': 'http://please.dontgohere.slack',
        'slack_username_override': 'elastalert',
        'slack_alert_fields': [
            {
                'title': 'Host',
                'value': 'somefield',
                'short': True
            },
            {
                'title': 'Sensors',
                'value': '@timestamp',
                'short': True
            }
        ],
        'alert_subject': 'Cool subject',
        'alert': []
    }
    rules_loader = FileRulesLoader({})
    rules_loader.load_modules(rule)
    alert = SlackAlerter(rule)
    match = {
        '@timestamp': '2016-01-01T00:00:00',
        'somefield': 'foobarbaz'
    }
    with mock.patch('requests.post') as mock_post_request:
        alert.alert([match])

    expected_data = {
        'username': '******',
        'channel': '',
        'icon_emoji': ':ghost:',
        'attachments': [
            {
                'color': 'danger',
                'title': rule['alert_subject'],
                'text': BasicMatchString(rule, match).__str__(),
                'mrkdwn_in': ['text', 'pretext'],
                'fields':
                [
                    {
                        'short': True,
                        'title': 'Host',
                        'value': 'foobarbaz'
                    },
                    {
                        'short': True,
                        'title': 'Sensors',
                        'value': '2016-01-01T00:00:00'
                    }
                ],
            }
        ],
        'text': '',
        'parse': 'none'
    }
    mock_post_request.assert_called_once_with(
        rule['slack_webhook_url'],
        data=mock.ANY,
        headers={'content-type': 'application/json'},
        proxies=None,
        verify=True,
        timeout=10
    )
    assert expected_data == json.loads(mock_post_request.call_args_list[0][1]['data'])
Exemple #12
0
    def alert(self, matches):
        body = ''
        for match in matches:
            body += str(BasicMatchString(self.rule, match))
            # Separate text of aggregated alerts with dashes
            if len(matches) > 1:
                body += '\n----------------------------------------\n'

        if self.custom_message is None:
            self.message = self.create_title(matches)
        else:
            self.message = self.custom_message.format(**matches[0])
        self.recipients = self._parse_responders(self.recipients,
                                                 self.recipients_args, matches,
                                                 self.default_reciepients)
        self.teams = self._parse_responders(self.teams, self.teams_args,
                                            matches, self.default_teams)
        post = {}
        post['message'] = self.message
        if self.account:
            post['user'] = self.account
        if self.recipients:
            post['responders'] = [{
                'username': r,
                'type': 'user'
            } for r in self.recipients]
        if self.teams:
            post['teams'] = [{'name': r, 'type': 'team'} for r in self.teams]
        if self.description:
            post['description'] = self.description.format(**matches[0])
        else:
            post['description'] = body
        if self.entity:
            post['entity'] = self.entity.format(**matches[0])
        if self.source:
            post['source'] = self.source.format(**matches[0])

        post['tags'] = []
        for i, tag in enumerate(self.tags):
            post['tags'].append(tag.format(**matches[0]))

        priority = self.priority
        if priority:
            priority = priority.format(**matches[0])
        if priority and priority not in ('P1', 'P2', 'P3', 'P4', 'P5'):
            elastalert_logger.warning(
                "Priority level does not appear to be specified correctly. \
                        Please make sure to set it to a value between P1 and P5"
            )
        else:
            post['priority'] = priority

        if self.alias is not None:
            post['alias'] = self.alias.format(**matches[0])

        details = self.get_details(matches)
        if details:
            post['details'] = details

        elastalert_logger.debug(json.dumps(post))

        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'GenieKey {}'.format(self.api_key),
        }
        # set https proxy, if it was provided
        proxies = {
            'https': self.opsgenie_proxy
        } if self.opsgenie_proxy else None

        try:
            r = requests.post(self.to_addr,
                              json=post,
                              headers=headers,
                              proxies=proxies)

            elastalert_logger.debug('request response: {0}'.format(r))
            if r.status_code != 202:
                elastalert_logger.info("Error response from {0} \n "
                                       "API Response: {1}".format(
                                           self.to_addr, r))
                r.raise_for_status()
            elastalert_logger.info("Alert sent to OpsGenie")
        except Exception as err:
            raise EAException("Error sending alert: {0}".format(err))
Exemple #13
0
    def alert(self, matches):
        alerts = []

        qk = self.rule.get('query_key', None)

        fullmessage = {}
        for match in matches:
            if qk is not None:
                resmatch = lookup_es_key(match, qk)
            else:
                resmatch = None

            if resmatch is not None:
                elastalert_logger.info(
                    'Alert for %s, %s at %s:' %
                    (self.rule['name'], resmatch,
                     lookup_es_key(match, self.rule['timestamp_field'])))
                alerts.append(
                    'Alert for %s, %s at %s:' %
                    (self.rule['name'], resmatch,
                     lookup_es_key(match, self.rule['timestamp_field'])))
                fullmessage['match'] = resmatch
            else:
                elastalert_logger.info(
                    'Rule %s generated an alert at %s:' %
                    (self.rule['name'],
                     lookup_es_key(match, self.rule['timestamp_field'])))
                alerts.append(
                    'Rule %s generated an alert at %s:' %
                    (self.rule['name'],
                     lookup_es_key(match, self.rule['timestamp_field'])))
                fullmessage['match'] = lookup_es_key(
                    match, self.rule['timestamp_field'])
            elastalert_logger.info(str(BasicMatchString(self.rule, match)))

        fullmessage['alerts'] = alerts
        fullmessage['rule'] = self.rule['name']
        fullmessage['rule_file'] = self.rule['rule_file']

        fullmessage['matching'] = str(BasicMatchString(self.rule, match))
        fullmessage['alertDate'] = datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S")
        fullmessage['body'] = self.create_alert_body(matches)

        fullmessage['matches'] = matches

        self.stomp_hostname = self.rule.get('stomp_hostname', 'localhost')
        self.stomp_hostport = self.rule.get('stomp_hostport', '61613')
        self.stomp_login = self.rule.get('stomp_login', 'admin')
        self.stomp_password = self.rule.get('stomp_password', 'admin')
        self.stomp_destination = self.rule.get('stomp_destination',
                                               '/queue/ALERT')
        self.stomp_ssl = self.rule.get('stomp_ssl', False)

        try:
            conn = stomp.Connection(
                [(self.stomp_hostname, self.stomp_hostport)],
                use_ssl=self.stomp_ssl)

            conn.connect(self.stomp_login, self.stomp_password)
            # Ensures that the CONNECTED frame is received otherwise, the disconnect call will fail.
            time.sleep(1)
            conn.send(self.stomp_destination, json.dumps(fullmessage))
            conn.disconnect()
        except Exception as e:
            raise EAException("Error posting to Stomp: %s" % e)
        elastalert_logger.info("Alert sent to Stomp")