diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aec1a97..d84f3e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - TBD ## New features -- TBD +- [Iris] Alerter added - [#1301](https://github.com/jertel/elastalert2/pull/1301) - @malinkinsa ## Other changes - Refactored FlatlineRule to make it more extensible - [#1291](https://github.com/jertel/elastalert2/pull/1291) - @rundef diff --git a/docs/source/elastalert.rst b/docs/source/elastalert.rst index e989092f..ad80dcfe 100755 --- a/docs/source/elastalert.rst +++ b/docs/source/elastalert.rst @@ -44,6 +44,7 @@ Currently, we have support built in for these alert types: - Graylog GELF - HTTP POST - HTTP POST 2 +- Iris - Jira - Lark - Line Notify diff --git a/docs/source/ruletypes.rst b/docs/source/ruletypes.rst index 21ee81ca..75dd377c 100644 --- a/docs/source/ruletypes.rst +++ b/docs/source/ruletypes.rst @@ -2579,6 +2579,85 @@ Example usage with json string formatting:: "X-custom-{{key}}": "{{type}}" } +IRIS +~~~~~~~~~ +The Iris alerter can be used to create a new alert or case in `Iris IRP System `_. The alerter supports adding tags, IOCs, and context from the alert matches and rule data. + +The alerter requires the following option: + +``iris_host``: Address of the Iris host. Exclude https:// For example: ``iris.example.com``. + +``iris_api_token``: The API key of the user you created, which will be used to initiate alerts and cases on behalf of this user. + +``iris_customer_id``: The user ID associated with the API key mentioned above. You can find it on the same page where the API key is located. + +Optional: + +``iris_ca_cert``: Path to custom CA certificate. + +``iris_ignore_ssl_errors``: Ignore ssl error. The default value is: ``False``. + +``iris_description``: Description of the alert or case. + +``iris_overwrite_timestamp``: Should the timestamp be overridden when creating an alert. By default, the alert's creation time will be the trigger time. If you want to use the event's timestamp as the ticket creation time, set this value to ``True``. Default value is ``False``. + +``iris_type``: The type of object being created. It can be either ``alert`` or ``case``. The default value is ``alert``. + +``iris_case_template_id``: Case template ID, if you want to apply a pre-prepared template. + +``iris_alert_note``: Note for the alert. + +``iris_alert_tags``: List of tags. + +``iris_alert_status_id``: The alert status of the alert, default value is ``2``. This parameter requires an integer input. + + Possible values: + + - ``1`` - Unspecified + - ``2`` - New + - ``3`` - Assigned + - ``4`` - In progress + - ``5`` - Pending + - ``6`` - Closed + - ``7`` - Merged. + +``iris_alert_source_link``: Your custom link, if needed. + +``iris_alert_severity_id``: The severity level of the alert, default value is ``1``. This parameter requires an integer input. + + Possible values: + + - ``1`` - Unspecified + - ``2`` - Informational + - ``3`` - Low + - ``4`` - Medium + - ``5`` - High + - ``6`` - Critical. + +``iris_alert_context``: Include information from the match into the alert context. Working as key-value, where the key is your custom name and value - data from elasticsearch message. + +``iris_iocs``: Description of the IOC to be added. + +Example usage ``iris_iocs``: + +.. code-block:: yaml + + iris_iocs: + - ioc_value: ip + ioc_description: Suspicious IP address + ioc_tlp_id: 2 + ioc_type_id: 76 + ioc_tags: ipv4, ip, suspicious + - ioc_value: username + ioc_description: Suspicious username + ioc_tlp_id: 1 + ioc_type_id: 3 + ioc_tags: username + +A few words about ``ioc_tlp_id`` and ``ioc_type_id``. ``ioc_tlp_id`` can be of three types: ``1 - red``, ``2 - amber``, ``3 - green``. There are numerous values for ``ioc_type_id``, and you can also add your custom ones. To find the ID for the type you are interested in, refer to your Iris instance's API at 'https://example.com/manage/ioc-types/list'. + +You can find complete examples of rules in the repository under the 'examples' folder. + Jira ~~~~ diff --git a/elastalert/alerters/iris.py b/elastalert/alerters/iris.py new file mode 100644 index 00000000..dd23ca63 --- /dev/null +++ b/elastalert/alerters/iris.py @@ -0,0 +1,187 @@ +import requests +import uuid + +from datetime import datetime +from requests import RequestException + +from elastalert.alerts import Alerter +from elastalert.util import EAException, elastalert_logger, lookup_es_key + + +class IrisAlerter(Alerter): + required_options = set(['iris_host', 'iris_api_token', 'iris_customer_id']) + + def __init__(self, rule): + super(IrisAlerter, self).__init__(rule) + self.url = f"https://{self.rule.get('iris_host')}" + self.api_token = self.rule.get('iris_api_token') + self.customer_id = self.rule.get('iris_customer_id') + self.ca_cert = self.rule.get('iris_ca_cert', False) + self.ignore_ssl_errors = self.rule.get('iris_ignore_ssl_errors', False) + self.description = self.rule.get('iris_description', None) + self.overwrite_timestamp = self.rule.get('iris_overwrite_timestamp', False) + self.type = self.rule.get('iris_type', 'alert') + self.case_template_id = self.rule.get('iris_case_template_id', None) + self.headers = { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {self.rule.get("iris_api_token")}' + } + self.alert_note = self.rule.get('iris_alert_note', None) + self.alert_tags = self.rule.get('iris_alert_tags', None) + self.alert_status_id = self.rule.get('iris_alert_status_id', 2) + self.alert_source_link = self.rule.get('iris_alert_source_link', None) + self.alert_severity_id = self.rule.get('iris_alert_severity_id', 1) + self.alert_context = self.rule.get('iris_alert_context', None) + self.iocs = self.rule.get('iris_iocs', None) + + def make_alert_context_records(self, matches): + alert_context = {} + + for key, value in self.alert_context.items(): + alert_context.update( + { + key: matches[0].get(value) + } + ) + + return alert_context + + def make_iocs_records(self, matches): + iocs = [] + for record in self.iocs: + record['ioc_value'] = lookup_es_key(matches[0], record['ioc_value']) + iocs.append(record) + return iocs + + def make_alert(self, matches): + if self.overwrite_timestamp: + event_timestamp = matches[0].get('@timestamp') + else: + event_timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + + alert_data = { + "alert_title": self.rule.get('name'), + "alert_description": self.description, + "alert_source": "ElastAlert2", + "alert_severity_id": self.alert_severity_id, + "alert_status_id": self.alert_status_id, + "alert_source_event_time": event_timestamp, + "alert_note": self.alert_note, + "alert_tags": self.alert_tags, + "alert_customer_id": self.customer_id, + } + + if self.alert_source_link: + alert_data.update( + {"alert_source_link": self.alert_source_link} + ) + + if self.iocs: + iocs = self.make_iocs_records(matches) + alert_data.update( + {"alert_iocs": iocs} + ) + + if self.alert_context: + alert_context = self.make_alert_context_records(matches) + alert_data.update( + {"alert_context": alert_context} + ) + + return alert_data + + def make_case(self, matches): + iocs = [] + case_data = { + "case_soc_id": f"SOC_{str(uuid.uuid4())[0:6]}", + "case_customer": self.customer_id, + "case_name": self.rule.get('name'), + "case_description": self.description + } + + if self.iocs: + iocs = self.make_iocs_records(matches) + + if self.case_template_id: + case_data.update( + {"case_template_id": self.case_template_id} + ) + + return case_data, iocs + + def alert(self, matches): + if self.ca_cert: + verify = self.ca_cert + else: + verify = False + + if self.ignore_ssl_errors: + requests.packages.urllib3.disable_warnings() + + if 'alert' in self.type: + alert_data = self.make_alert(matches) + + try: + alert_response = requests.post( + url=f'{self.url}/alerts/add', + headers=self.headers, + json=alert_data, + verify=verify, + ) + + if alert_response.status_code != 200: + raise EAException(f"Cannot create a new alert: {alert_response.status_code}") + + except RequestException as e: + raise EAException(f"Error posting alert to Iris: {e}") + elastalert_logger.info('Alert sent to Iris') + + elif 'case' in self.type: + case_data, iocs = self.make_case(matches) + + try: + case_response = requests.post( + url=f'{self.url}/manage/cases/add', + headers=self.headers, + json=case_data, + verify=verify, + ) + + + if case_response.status_code == 200: + case_response_data = case_response.json() + case_id = case_response_data.get('data', '').get('case_id') + for ioc in iocs: + ioc.update( + { + "cid": case_id + } + ) + + try: + response_ioc = requests.post( + url=f'{self.url}/case/ioc/add', + headers=self.headers, + json=ioc, + verify=verify, + ) + + if response_ioc.status_code != 200: + raise EAException(f"Unable to add a new IOC to the case {case_id}") + + except RequestException as e: + raise EAException(f"Error when adding IOC to the case {case_id}: {e}") + elastalert_logger.info('IOCs successfully added to the case') + + else: + raise EAException(f'Cannot create a new case: {case_response.status_code}') + + except RequestException as e: + raise EAException(f"Error posting the case to Iris: {e}") + elastalert_logger.info('Case successfully created in Iris') + + def get_info(self): + return { + 'type': 'IrisAlerter', + 'iris_api_endpoint': self.url + } diff --git a/elastalert/loaders.py b/elastalert/loaders.py index 4e0524a5..aa175dca 100644 --- a/elastalert/loaders.py +++ b/elastalert/loaders.py @@ -25,6 +25,7 @@ import elastalert.alerters.googlechat import elastalert.alerters.httppost import elastalert.alerters.httppost2 +import elastalert.alerters.iris import elastalert.alerters.lark import elastalert.alerters.line import elastalert.alerters.pagertree @@ -132,7 +133,8 @@ class RulesLoader(object): 'datadog': elastalert.alerters.datadog.DatadogAlerter, 'ses': elastalert.alerters.ses.SesAlerter, 'rocketchat': elastalert.alerters.rocketchat.RocketChatAlerter, - 'gelf': elastalert.alerters.gelf.GelfAlerter + 'gelf': elastalert.alerters.gelf.GelfAlerter, + 'iris': elastalert.alerters.iris.IrisAlerter, } # A partial ordering of alert types. Relative order will be preserved in the resulting alerts list diff --git a/elastalert/schema.yaml b/elastalert/schema.yaml index 5a479dc9..7f081310 100644 --- a/elastalert/schema.yaml +++ b/elastalert/schema.yaml @@ -72,6 +72,20 @@ definitions: filter: &filter {} + irisIocField: &irisIocField + type: object + additionalProperties: false + properties: + ioc_value: {type: string} + ioc_description: {type: string} + ioc_tlp_id: {type: integer, enum: [1, 2, 3]} + ioc_type_id: {type: integer} + ioc_tags: {type: string} + + arrayOfIrisIocFields: &arrayOfIrisIocFields + type: array + items: *irisIocField + required: [type, index, alert] type: object @@ -514,6 +528,35 @@ properties: http_post2_ignore_ssl_errors: {type: boolean} http_post2_timeout: {type: integer} + ### IRIS + iris_url: {type: string} + iris_api_token: {type: string} + iris_type: {type: string, enum: ['alert', 'case']} + iris_customer_id: {type: integer} + iris_ignore_ssl_errors: {type: boolean} + iris_ca_cert: {type: string} + iris_overwrite_timestamp: {type: boolean} + iris_case_template_id: {type: integer} + iris_description: {type: string} + iris_alert_note: {type: string} + iris_alert_tags: {type: string} + iris_alert_status_id: {type: integer, enum: [1, 2, 3, 4, 5, 6, 7]} + iris_alert_source_link: {type: string} + iris_alert_severity_id: {type: integer, enum: [1, 2, 3, 4, 5, 6]} + iris_iocs: *arrayOfIrisIocFields + iris_alert_context: + type: object + minProperties: 1 + patternProperties: + "^.+$": + oneOf: + - type: string + - type: object + additionalProperties: false + required: [ field ] + properties: + field: { type: string, minLength: 1 } + ### Jira jira_server: {type: string} jira_project: {type: string} diff --git a/examples/rules/example_iris_alert_any.yaml b/examples/rules/example_iris_alert_any.yaml new file mode 100644 index 00000000..3d712572 --- /dev/null +++ b/examples/rules/example_iris_alert_any.yaml @@ -0,0 +1,36 @@ +name: "Example Iris alert" +type: any +index: index_* +use_strftime_index: true + +filter: +- query: + query_string: + query: "session_status: opened" + +realert: + minutes: 0 + +alert: +- iris + +iris_host: 127.0.0.1 +iris_api_token: token123456789 +iris_customer_id: 1 +iris_description: 'Test alert from ElastAlert2' +iris_alert_note: 'Alert triggered by opened session' +iris_alert_tags: 'test, login, ssh' +iris_alert_context: + username: username + ip: src_ip +iris_iocs: + - ioc_value: src_ip + ioc_description: source ip address + ioc_tlp_id: 1 + ioc_type_id: 42 + ioc_tags: ipv4 + - ioc_value: username + ioc_description: who was connecting + ioc_tlp_id: 3 + ioc_type_id: 3 + ioc_tags: username diff --git a/tests/alerters/iris_test.py b/tests/alerters/iris_test.py new file mode 100644 index 00000000..79884538 --- /dev/null +++ b/tests/alerters/iris_test.py @@ -0,0 +1,437 @@ +import logging + +from unittest import mock +from unittest.mock import patch +from datetime import datetime + +from elastalert.alerters.iris import IrisAlerter +from elastalert.loaders import FileRulesLoader + + +def test_iris_make_alert_context_records(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Context', + 'type': 'any', + 'iris_type': 'alert', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_customer_id': 1, + 'iris_alert_context': {'username': 'username', 'ip': 'src_ip', 'login_status': 'event_status'}, + 'alert': [] + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = { + 'username': 'evil_user', + 'ip': '172.20.1.1', + 'login_status': 'success' + } + + actual_data = alert.make_alert_context_records([match]) + + assert expected_data == actual_data + + +def test_iris_make_iocs_records(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Context', + 'type': 'any', + 'iris_type': 'alert', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_customer_id': 1, + 'iris_iocs': [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': 'src_ip' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'username' + } + ], + 'alert': [] + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': '172.20.1.1' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'evil_user' + } + ] + + actual_data = alert.make_iocs_records([match]) + assert expected_data == actual_data + + +def test_iris_make_alert_minimal(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Minimal Alert Body', + 'type': 'any', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_customer_id': 1, + 'alert': [], + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = { + "alert_title": 'Test Minimal Alert Body', + "alert_description": None, + "alert_source": "ElastAlert2", + "alert_severity_id": 1, + "alert_status_id": 2, + "alert_source_event_time": datetime.now().strftime("%Y-%m-%dT%H:%M:%S"), + "alert_note": None, + "alert_tags": None, + "alert_customer_id": 1 + } + + actual_data = alert.make_alert([match]) + assert expected_data == actual_data + + +def test_iris_make_alert_maximal(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Maximal Alert Body', + 'type': 'any', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_customer_id': 1, + 'iris_description': 'test description in alert', + 'iris_alert_note': 'test note', + 'iris_alert_tags': 'test, alert', + 'iris_overwrite_timestamp': True, + 'iris_alert_source_link': 'https://example.com', + 'iris_iocs': [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': 'src_ip' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'username' + } + ], + 'iris_alert_context': {'username': 'username', 'ip': 'src_ip', 'login_status': 'event_status'}, + 'alert': [], + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = { + "alert_title": 'Test Maximal Alert Body', + "alert_description": 'test description in alert', + "alert_source": "ElastAlert2", + "alert_severity_id": 1, + "alert_status_id": 2, + "alert_source_event_time": '2023-10-21 20:00:00.000', + "alert_note": 'test note', + "alert_tags": 'test, alert', + "alert_customer_id": 1, + "alert_source_link": 'https://example.com', + "alert_iocs": [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': '172.20.1.1' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'evil_user' + } + ], + "alert_context": { + 'username': 'evil_user', + 'ip': '172.20.1.1', + 'login_status': 'success' + }, + } + + actual_data = alert.make_alert([match]) + assert expected_data == actual_data + + +def test_iris_make_case_minimal(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Minimal Case', + 'type': 'any', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_type': 'case', + 'iris_customer_id': 1, + 'alert': [], + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = { + "case_soc_id": "SOC_123456", + "case_customer": 1, + "case_name": "Test Minimal Case", + "case_description": None + } + + with patch('uuid.uuid4', return_value='123456'): + actual_data, actual_data_iocs = alert.make_case([match]) + + assert expected_data == actual_data + + +def test_iris_make_case_maximal(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Maximal Case', + 'type': 'any', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_type': 'case', + 'iris_customer_id': 1, + 'iris_case_template_id': 55, + 'iris_iocs': [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': 'src_ip' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'username' + } + ], + 'alert': [], + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = { + "case_soc_id": "SOC_123456", + "case_customer": 1, + "case_name": "Test Maximal Case", + "case_description": None, + "case_template_id": 55, + } + + expected_data_iocs = [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': '172.20.1.1' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'evil_user' + } + ] + + with patch('uuid.uuid4', return_value='123456'): + actual_data, actual_data_iocs = alert.make_case([match]) + + assert expected_data == actual_data + assert expected_data_iocs == actual_data_iocs + + +def test_iris_alert_alert(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Main', + 'type': 'any', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_customer_id': 1, + 'iris_description': 'test description in alert', + 'iris_alert_note': 'test note', + 'iris_alert_tags': 'test, alert', + 'iris_overwrite_timestamp': True, + 'iris_alert_source_link': 'https://example.com', + 'iris_iocs': [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': 'src_ip' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'username' + } + ], + 'iris_alert_context': {'username': 'username', 'ip': 'src_ip', 'login_status': 'event_status'}, + 'alert': [], + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + match = { + '@timestamp': '2023-10-21 20:00:00.000', 'username': 'evil_user', 'src_ip': '172.20.1.1', 'dst_ip': '10.0.0.1', + 'event_type': 'login', 'event_status': 'success' + } + + expected_data = { + "alert_title": 'Test Main', + "alert_description": 'test description in alert', + "alert_source": "ElastAlert2", + "alert_severity_id": 1, + "alert_status_id": 2, + "alert_source_event_time": '2023-10-21 20:00:00.000', + "alert_note": 'test note', + "alert_tags": 'test, alert', + "alert_customer_id": 1, + "alert_source_link": 'https://example.com', + "alert_iocs": [ + { + 'ioc_description': 'source address', + 'ioc_tags': 'ip, ipv4', + 'ioc_tlp_id': 1, + 'ioc_type_id': 76, + 'ioc_value': '172.20.1.1' + }, + { + 'ioc_description': 'target username', + 'ioc_tags': 'login, username', + 'ioc_tlp_id': 3, + 'ioc_type_id': 3, + 'ioc_value': 'evil_user' + } + ], + "alert_context": { + 'username': 'evil_user', + 'ip': '172.20.1.1', + 'login_status': 'success' + }, + } + mock_response = mock.Mock() + mock_response.status_code = 200 + with mock.patch('requests.post', return_value=mock_response) as mock_post_request: + alert.alert([match]) + + mock_post_request.assert_called_once_with( + url=f'https://{rule["iris_host"]}/alerts/add', + headers={ + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {rule["iris_api_token"]}' + }, + json=mock.ANY, + verify=False, + ) + + assert expected_data == mock_post_request.call_args_list[0][1]['json'] + assert ('elastalert', logging.INFO, 'Alert sent to Iris') == caplog.record_tuples[0] + + +def test_iris_get_info(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test Info', + 'type': 'any', + 'iris_host': '127.0.0.1', + 'iris_api_token': 'token 12345', + 'iris_customer_id': 1, + 'alert': [], + } + + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = IrisAlerter(rule) + + expected_data = { + 'type': 'IrisAlerter', + 'iris_api_endpoint': 'https://127.0.0.1' + } + + actual_data = alert.get_info() + assert expected_data == actual_data