elastalert2 2.30.0__tar.gz → 2.31.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. {elastalert2-2.30.0/elastalert2.egg-info → elastalert2-2.31.0}/PKG-INFO +1 -1
  2. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/__init__.py +10 -1
  3. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/elastalert.py +3 -2
  4. elastalert2-2.31.0/elastalert/esql.py +76 -0
  5. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/kibana_discover.py +31 -1
  6. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/test_rule.py +1 -1
  7. {elastalert2-2.30.0 → elastalert2-2.31.0/elastalert2.egg-info}/PKG-INFO +1 -1
  8. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert2.egg-info/SOURCES.txt +1 -0
  9. {elastalert2-2.30.0 → elastalert2-2.31.0}/setup.py +1 -1
  10. {elastalert2-2.30.0 → elastalert2-2.31.0}/LICENSE +0 -0
  11. {elastalert2-2.30.0 → elastalert2-2.31.0}/README.md +0 -0
  12. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/__init__.py +0 -0
  13. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/alerta.py +0 -0
  14. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/alertmanager.py +0 -0
  15. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/chatwork.py +0 -0
  16. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/command.py +0 -0
  17. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/datadog.py +0 -0
  18. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/debug.py +0 -0
  19. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/dingtalk.py +0 -0
  20. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/discord.py +0 -0
  21. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/email.py +0 -0
  22. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/exotel.py +0 -0
  23. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/flashduty.py +0 -0
  24. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/gelf.py +0 -0
  25. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/gitter.py +0 -0
  26. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/googlechat.py +0 -0
  27. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/httppost.py +0 -0
  28. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/httppost2.py +0 -0
  29. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/indexer.py +0 -0
  30. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/iris.py +0 -0
  31. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/jira.py +0 -0
  32. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/lark.py +0 -0
  33. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/line.py +0 -0
  34. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/matrixhookshot.py +0 -0
  35. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/mattermost.py +0 -0
  36. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/opsgenie.py +0 -0
  37. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/pagerduty.py +0 -0
  38. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/pagertree.py +0 -0
  39. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/powerautomate.py +0 -0
  40. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/rocketchat.py +0 -0
  41. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/servicenow.py +0 -0
  42. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/ses.py +0 -0
  43. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/slack.py +0 -0
  44. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/smseagle.py +0 -0
  45. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/sns.py +0 -0
  46. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/sqs.py +0 -0
  47. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/stomp.py +0 -0
  48. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/telegram.py +0 -0
  49. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/tencentsms.py +0 -0
  50. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/thehive.py +0 -0
  51. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/twilio.py +0 -0
  52. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/victorops.py +0 -0
  53. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/webex_webhook.py +0 -0
  54. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/workwechat.py +0 -0
  55. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/yzj.py +0 -0
  56. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerters/zabbix.py +0 -0
  57. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/alerts.py +0 -0
  58. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/auth.py +0 -0
  59. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/config.py +0 -0
  60. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/create_index.py +0 -0
  61. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/enhancements.py +0 -0
  62. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/eql.py +0 -0
  63. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/7/elastalert.json +0 -0
  64. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/7/elastalert_error.json +0 -0
  65. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/7/elastalert_status.json +0 -0
  66. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/7/past_elastalert.json +0 -0
  67. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/7/silence.json +0 -0
  68. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/8/elastalert.json +0 -0
  69. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/8/elastalert_error.json +0 -0
  70. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/8/elastalert_status.json +0 -0
  71. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/8/past_elastalert.json +0 -0
  72. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/es_mappings/8/silence.json +0 -0
  73. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/kibana_external_url_formatter.py +0 -0
  74. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/loaders.py +0 -0
  75. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/opensearch_discover.py +0 -0
  76. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/opensearch_external_url_formatter.py +0 -0
  77. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/prometheus_wrapper.py +0 -0
  78. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/ruletypes.py +0 -0
  79. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/schema.yaml +0 -0
  80. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/util.py +0 -0
  81. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert/yaml.py +0 -0
  82. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert2.egg-info/dependency_links.txt +0 -0
  83. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert2.egg-info/entry_points.txt +0 -0
  84. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert2.egg-info/requires.txt +0 -0
  85. {elastalert2-2.30.0 → elastalert2-2.31.0}/elastalert2.egg-info/top_level.txt +0 -0
  86. {elastalert2-2.30.0 → elastalert2-2.31.0}/setup.cfg +0 -0
  87. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/__init__.py +0 -0
  88. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/alerta_test.py +0 -0
  89. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/alertmanager_test.py +0 -0
  90. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/chatwork_test.py +0 -0
  91. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/command_test.py +0 -0
  92. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/datadog_test.py +0 -0
  93. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/debug_test.py +0 -0
  94. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/dingtalk_test.py +0 -0
  95. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/discord_test.py +0 -0
  96. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/email_test.py +0 -0
  97. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/exotel_test.py +0 -0
  98. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/flashduty_test.py +0 -0
  99. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/gelf_test.py +0 -0
  100. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/gitter_test.py +0 -0
  101. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/googlechat_test.py +0 -0
  102. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/httppost2_test.py +0 -0
  103. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/httppost_test.py +0 -0
  104. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/indexer_test.py +0 -0
  105. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/iris_test.py +0 -0
  106. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/jira_test.py +0 -0
  107. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/lark_test.py +0 -0
  108. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/line_test.py +0 -0
  109. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/matrixhookshot_test.py +0 -0
  110. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/mattermost_test.py +0 -0
  111. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/opsgenie_test.py +0 -0
  112. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/pagerduty_test.py +0 -0
  113. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/pagertree_test.py +0 -0
  114. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/powerautomate_test.py +0 -0
  115. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/rocketchat_test.py +0 -0
  116. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/servicenow_test.py +0 -0
  117. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/ses_test.py +0 -0
  118. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/slack_test.py +0 -0
  119. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/smseagle_test.py +0 -0
  120. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/sns_test.py +0 -0
  121. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/sqs_test.py +0 -0
  122. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/stomp_test.py +0 -0
  123. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/telegram_test.py +0 -0
  124. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/tencentsms_test.py +0 -0
  125. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/thehive_test.py +0 -0
  126. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/twilio_test.py +0 -0
  127. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/victorops_test.py +0 -0
  128. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/webex_webhook_test.py +0 -0
  129. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/workwechat_test.py +0 -0
  130. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/yzj_test.py +0 -0
  131. {elastalert2-2.30.0 → elastalert2-2.31.0}/tests/alerters/zabbix_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elastalert2
3
- Version: 2.30.0
3
+ Version: 2.31.0
4
4
  Summary: Automated rule-based alerting for Elasticsearch
5
5
  Home-page: https://github.com/jertel/elastalert2
6
6
  License: Apache 2.0
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import copy
3
3
  import elastalert.eql as eql
4
+ import elastalert.esql as esql
4
5
 
5
6
  from elasticsearch import Elasticsearch
6
7
  from elasticsearch import RequestsHttpConnection
@@ -134,6 +135,7 @@ class ElasticSearchClient(Elasticsearch):
134
135
 
135
136
  path = _make_path(index, doc_type, "_search")
136
137
  eql_body = eql.format_request(body)
138
+ esql_body = esql.format_request(body)
137
139
  if eql_body is not None:
138
140
  path = path.replace('/_search', '/_eql/search')
139
141
  body = eql_body
@@ -143,6 +145,10 @@ class ElasticSearchClient(Elasticsearch):
143
145
  params.pop('scroll')
144
146
  if '_source_includes' in params:
145
147
  params.pop('_source_includes')
148
+ elif esql_body is not None:
149
+ path = "/_query"
150
+ body = esql_body
151
+ params = {'format': 'json'}
146
152
 
147
153
  results = self.transport.perform_request(
148
154
  "POST",
@@ -152,6 +158,9 @@ class ElasticSearchClient(Elasticsearch):
152
158
  body=body,
153
159
  )
154
160
 
155
- eql.format_results(results);
161
+ if esql_body is not None:
162
+ esql.format_results(results, default_index=index)
163
+ else:
164
+ eql.format_results(results)
156
165
 
157
166
  return results
@@ -337,9 +337,10 @@ class ElastAlerter(object):
337
337
 
338
338
  # Convert the timestamp to a datetime
339
339
  ts = lookup_es_key(hit['_source'], rule['timestamp_field'])
340
- if not ts and not rule["_source_enabled"]:
340
+ if ts is None:
341
341
  raise EAException(
342
- "Error: No timestamp was found for hit. '_source_enabled' is set to false, check your mappings for stored fields"
342
+ f"Error: The configured timestamp_field '{rule['timestamp_field']}' was not found in the query results. "
343
+ f"Available fields: {list(hit['_source'].keys())}"
343
344
  )
344
345
 
345
346
  set_es_key(hit['_source'], rule['timestamp_field'], rule['ts_to_dt'](ts))
@@ -0,0 +1,76 @@
1
+ import hashlib
2
+ import json
3
+
4
+
5
+ def format_request(body):
6
+ query = body.get('query')
7
+ if not query:
8
+ return None
9
+
10
+ query_bool = query.get('bool')
11
+ if not query_bool:
12
+ return None
13
+
14
+ filt = query_bool.get('filter')
15
+ if not filt:
16
+ return None
17
+
18
+ filter_bool = filt.get('bool')
19
+ if not filter_bool:
20
+ return None
21
+
22
+ filter_bool_must = filter_bool.get('must')
23
+ if not filter_bool_must:
24
+ return None
25
+
26
+ other_filters = []
27
+ esql = None
28
+ for f in filter_bool_must:
29
+ if f.get('esql'):
30
+ esql = f['esql']
31
+ else:
32
+ other_filters.append(f)
33
+
34
+ if esql:
35
+ new_body = {'filter': {'bool': {'must': other_filters}}, 'query': esql}
36
+ return new_body
37
+
38
+ return None
39
+
40
+
41
+ def format_results(results, default_index=None):
42
+ columns = results.pop('columns', None)
43
+ values = results.pop('values', None)
44
+ if columns is None or values is None:
45
+ return results
46
+
47
+ col_names = [col['name'] for col in columns]
48
+ hits = []
49
+ for val_row in values:
50
+ doc = dict(zip(col_names, val_row))
51
+
52
+ # Pull metadata fields if present
53
+ doc_id = doc.get('_id', None)
54
+ doc_index = doc.get('_index', default_index)
55
+
56
+ if not doc_id:
57
+ # Generate deterministic hash of the document to use as unique ID
58
+ doc_hash = hashlib.sha256(json.dumps(doc, sort_keys=True, default=str).encode('utf-8')).hexdigest()
59
+ doc_id = doc_hash
60
+
61
+ hit = {
62
+ '_index': doc_index,
63
+ '_id': doc_id,
64
+ '_source': doc
65
+ }
66
+ hits.append(hit)
67
+
68
+ results['hits'] = {
69
+ 'total': {
70
+ 'value': len(values),
71
+ 'relation': 'eq'
72
+ },
73
+ 'hits': hits
74
+ }
75
+ results['esql'] = True
76
+ return results
@@ -74,6 +74,36 @@ def kibana7_disover_global_state(from_time, to_time):
74
74
  } )
75
75
 
76
76
 
77
+ def _normalize_query_value(value):
78
+ """Collapse empty/single-element query_key values before filter emission.
79
+
80
+ Elasticsearch returns multi-valued fields as arrays even when they hold a
81
+ single item, and rules using such fields as ``query_key`` otherwise render
82
+ Kibana filters with ``params.query: ['']`` — a phrase filter that matches
83
+ no documents, producing an empty Discover view for the reviewer.
84
+
85
+ Returns:
86
+ - ``None`` when the value is absent, an empty string, or a list that
87
+ collapses to nothing after dropping ``None``/empty-string entries;
88
+ callers emit the existing ``negate exists`` filter for this case.
89
+ - The sole surviving element when the input is a single-item list.
90
+ - The original value otherwise (scalars pass through; multi-value lists
91
+ are returned unchanged so callers may extend to a ``phrases`` filter).
92
+ """
93
+ if value is None:
94
+ return None
95
+ if isinstance(value, str):
96
+ return None if value == '' else value
97
+ if isinstance(value, list):
98
+ cleaned = [v for v in value if v not in (None, '')]
99
+ if not cleaned:
100
+ return None
101
+ if len(cleaned) == 1:
102
+ return cleaned[0]
103
+ return cleaned
104
+ return value
105
+
106
+
77
107
  def kibana_discover_app_state(index, columns, filters, query_keys, match):
78
108
  app_filters = []
79
109
 
@@ -105,7 +135,7 @@ def kibana_discover_app_state(index, columns, filters, query_keys, match):
105
135
  } )
106
136
 
107
137
  for query_key in query_keys:
108
- query_value = lookup_es_key(match, query_key)
138
+ query_value = _normalize_query_value(lookup_es_key(match, query_key))
109
139
 
110
140
  if query_value is None:
111
141
  app_filters.append( {
@@ -211,7 +211,7 @@ class MockElastAlerter(object):
211
211
 
212
212
  terms = res['hits']['hits'][0]['_source']
213
213
 
214
- is_eql = res.get('eql') == True
214
+ is_eql = res.get('eql') == True or res.get('esql') == True
215
215
 
216
216
  if not is_eql:
217
217
  # Get a count of all docs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elastalert2
3
- Version: 2.30.0
3
+ Version: 2.31.0
4
4
  Summary: Automated rule-based alerting for Elasticsearch
5
5
  Home-page: https://github.com/jertel/elastalert2
6
6
  License: Apache 2.0
@@ -10,6 +10,7 @@ elastalert/create_index.py
10
10
  elastalert/elastalert.py
11
11
  elastalert/enhancements.py
12
12
  elastalert/eql.py
13
+ elastalert/esql.py
13
14
  elastalert/kibana_discover.py
14
15
  elastalert/kibana_external_url_formatter.py
15
16
  elastalert/loaders.py
@@ -8,7 +8,7 @@ from setuptools import setup
8
8
  base_dir = os.path.dirname(__file__)
9
9
  setup(
10
10
  name='elastalert2',
11
- version='2.30.0',
11
+ version='2.31.0',
12
12
  description='Automated rule-based alerting for Elasticsearch',
13
13
  long_description=open('README.md').read(),
14
14
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes