Skip to content

Commit d175425

Browse files
authored
Merge pull request Yelp#2741 from Yelp/add_hive_alerter
Re-add TheHive alerter without any libraries
2 parents b45d767 + 6a4ae2d commit d175425

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

docs/source/elastalert.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Currently, we have support built in for these alert types:
4242
- GoogleChat
4343
- Debug
4444
- Stomp
45+
- TheHive
4546

4647
Additional rule types and alerts can be easily imported or written. (See :ref:`Writing rule types <writingrules>` and :ref:`Writing alerts <writingalerts>`)
4748

docs/source/ruletypes.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,6 +2186,51 @@ Required:
21862186

21872187
``linenotify_access_token``: The access token that you got from https://notify-bot.line.me/my/
21882188

2189+
theHive
2190+
~~~~~~
2191+
2192+
theHive alert type will send JSON request to theHive (Security Incident Response Platform) with TheHive4py API. Sent request will be stored like Hive Alert with description and observables.
2193+
2194+
Required:
2195+
2196+
``hive_connection``: The connection details as key:values. Required keys are ``hive_host``, ``hive_port`` and ``hive_apikey``.
2197+
2198+
``hive_alert_config``: Configuration options for the alert.
2199+
2200+
Optional:
2201+
2202+
``hive_proxies``: Proxy configuration.
2203+
2204+
``hive_observable_data_mapping``: If needed, matched data fields can be mapped to TheHive observable types using python string formatting.
2205+
2206+
Example usage::
2207+
2208+
alert: hivealerter
2209+
2210+
hive_connection:
2211+
hive_host: http://localhost
2212+
hive_port: <hive_port>
2213+
hive_apikey: <hive_apikey>
2214+
hive_proxies:
2215+
http: ''
2216+
https: ''
2217+
2218+
hive_alert_config:
2219+
title: 'Title' ## This will default to {rule[index]_rule[name]} if not provided
2220+
type: 'external'
2221+
source: 'elastalert'
2222+
description: '{match[field1]} {rule[name]} Sample description'
2223+
severity: 2
2224+
tags: ['tag1', 'tag2 {rule[name]}']
2225+
tlp: 3
2226+
status: 'New'
2227+
follow: True
2228+
2229+
hive_observable_data_mapping:
2230+
- domain: "{match[field1]}_{rule[name]}"
2231+
- domain: "{match[field]}"
2232+
- ip: "{match[ip_field]}"
2233+
21892234

21902235
Zabbix
21912236
~~~~~~~~~~~

elastalert/alerts.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import json
55
import logging
66
import os
7+
import re
78
import subprocess
89
import sys
910
import time
@@ -2104,3 +2105,72 @@ def alert(self, matches):
21042105

21052106
def get_info(self):
21062107
return {"type": "linenotify", "linenotify_access_token": self.linenotify_access_token}
2108+
2109+
2110+
class HiveAlerter(Alerter):
2111+
"""
2112+
Use matched data to create alerts containing observables in an instance of TheHive
2113+
"""
2114+
2115+
required_options = set(['hive_connection', 'hive_alert_config'])
2116+
2117+
def alert(self, matches):
2118+
2119+
connection_details = self.rule['hive_connection']
2120+
2121+
for match in matches:
2122+
context = {'rule': self.rule, 'match': match}
2123+
2124+
artifacts = []
2125+
for mapping in self.rule.get('hive_observable_data_mapping', []):
2126+
for observable_type, match_data_key in mapping.items():
2127+
try:
2128+
match_data_keys = re.findall(r'\{match\[([^\]]*)\]', match_data_key)
2129+
rule_data_keys = re.findall(r'\{rule\[([^\]]*)\]', match_data_key)
2130+
data_keys = match_data_keys + rule_data_keys
2131+
context_keys = list(context['match'].keys()) + list(context['rule'].keys())
2132+
if all([True if k in context_keys else False for k in data_keys]):
2133+
artifact = {'tlp': 2, 'tags': [], 'message': None, 'dataType': observable_type,
2134+
'data': match_data_key.format(**context)}
2135+
artifacts.append(artifact)
2136+
except KeyError:
2137+
raise KeyError('\nformat string\n{}\nmatch data\n{}'.format(match_data_key, context))
2138+
2139+
alert_config = {
2140+
'artifacts': artifacts,
2141+
'sourceRef': str(uuid.uuid4())[0:6],
2142+
'customFields': {},
2143+
'caseTemplate': None,
2144+
'title': '{rule[index]}_{rule[name]}'.format(**context),
2145+
'date': int(time.time()) * 1000
2146+
}
2147+
alert_config.update(self.rule.get('hive_alert_config', {}))
2148+
2149+
for alert_config_field, alert_config_value in alert_config.items():
2150+
if isinstance(alert_config_value, str):
2151+
alert_config[alert_config_field] = alert_config_value.format(**context)
2152+
elif isinstance(alert_config_value, (list, tuple)):
2153+
formatted_list = []
2154+
for element in alert_config_value:
2155+
try:
2156+
formatted_list.append(element.format(**context))
2157+
except (AttributeError, KeyError, IndexError):
2158+
formatted_list.append(element)
2159+
alert_config[alert_config_field] = formatted_list
2160+
2161+
alert_body = json.dumps(alert_config, indent=4, sort_keys=True)
2162+
req = '{}:{}/api/alert'.format(connection_details['hive_host'], connection_details['hive_port'])
2163+
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(connection_details.get('hive_apikey', ''))}
2164+
proxies = connection_details.get('hive_proxies', {'http': '', 'https': ''})
2165+
verify = connection_details.get('hive_verify', False)
2166+
response = requests.post(req, headers=headers, data=alert_body, proxies=proxies, verify=verify)
2167+
2168+
if response.status_code != 201:
2169+
raise Exception('alert not successfully created in TheHive\n{}'.format(response.text))
2170+
2171+
def get_info(self):
2172+
2173+
return {
2174+
'type': 'hivealerter',
2175+
'hive_host': self.rule.get('hive_connection', {}).get('hive_host', '')
2176+
}

elastalert/loaders.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class RulesLoader(object):
7777
'servicenow': alerts.ServiceNowAlerter,
7878
'alerta': alerts.AlertaAlerter,
7979
'post': alerts.HTTPPostAlerter,
80+
'hivealerter': alerts.HiveAlerter
8081
}
8182

8283
# A partial ordering of alert types. Relative order will be preserved in the resulting alerts list

0 commit comments

Comments
 (0)