add timestamp field to POST request (#63)
taylor_socfortress committed
Jul 25, 2023 at 10:18 UTC
c21cc943c790ec48ca284018a7d12693bc2f3631
4 files changed
+134
-18
backend/app/routes/alerts.py
+36
-4
@@ -26,9 +26,16 @@ def get_alerts() -> jsonify:
26
timerange = str(data.get("timerange", "24h"))
27
alert_field = str(data.get("alert_field", "syslog_level"))
28
alert_value = str(data.get("alert_value", "ALERT"))
29
+ timestamp_field = str(data.get("timestamp_field", "timestamp_utc"))
30
31
service = AlertsService()
31
- alerts = service.collect_alerts(size=size, timerange=timerange, alert_field=alert_field, alert_value=alert_value)
32
+ alerts = service.collect_alerts(
33
+ size=size,
34
+ timerange=timerange,
35
+ alert_field=alert_field,
36
+ alert_value=alert_value,
37
+ timestamp_field=timestamp_field,
38
+ )
39
return jsonify(alerts)
40
41
@@ -51,6 +58,7 @@ def get_alerts_by_agent() -> jsonify:
58
agent_name = str(data.get("agent_name", "WIN-HFOU106TD7K"))
59
alert_field = str(data.get("alert_field", "syslog_level"))
60
alert_value = str(data.get("alert_value", "ALERT"))
61
+ timestamp_field = str(data.get("timestamp_field", "timestamp_utc"))
62
service = AlertsService()
63
# size = request.args.get("size", default=10, type=int)
64
# timerange = request.args.get("timerange", default="24h", type=str)
@@ -60,6 +68,7 @@ def get_alerts_by_agent() -> jsonify:
68
timerange=timerange,
69
alert_field=alert_field,
70
alert_value=alert_value,
71
+ timestamp_field=timestamp_field,
72
)
73
return jsonify(alerts)
74
@@ -83,6 +92,7 @@ def get_alerts_by_index() -> jsonify:
92
index_name = str(data.get("index_name", "wazuh*"))
93
alert_field = str(data.get("alert_field", "syslog_level"))
94
alert_value = str(data.get("alert_value", "ALERT"))
95
+ timestamp_field = str(data.get("timestamp_field", "timestamp_utc"))
96
service = AlertsService()
97
alerts = service.collect_alerts_by_index(
98
index_name=index_name,
@@ -90,6 +100,7 @@ def get_alerts_by_index() -> jsonify:
100
timerange=timerange,
101
alert_field=alert_field,
102
alert_value=alert_value,
103
+ timestamp_field=timestamp_field,
104
)
105
return jsonify(alerts)
106
@@ -112,8 +123,15 @@ def get_hosts() -> jsonify:
123
timerange = str(data.get("timerange", "24h"))
124
alert_field = str(data.get("alert_field", "syslog_level"))
125
alert_value = str(data.get("alert_value", "ALERT"))
126
+ timestamp_field = str(data.get("timestamp_field", "timestamp_utc"))
127
service = AlertsService()
116
- hosts = service.collect_alerts_by_host(size=size, timerange=timerange, alert_field=alert_field, alert_value=alert_value)
128
+ hosts = service.collect_alerts_by_host(
129
+ size=size,
130
+ timerange=timerange,
131
+ alert_field=alert_field,
132
+ alert_value=alert_value,
133
+ timestamp_field=timestamp_field,
134
+ )
135
return jsonify(hosts)
136
137
@@ -135,8 +153,15 @@ def get_rules() -> jsonify:
153
timerange = str(data.get("timerange", "24h"))
154
alert_field = str(data.get("alert_field", "syslog_level"))
155
alert_value = str(data.get("alert_value", "ALERT"))
156
+ timestamp_field = str(data.get("timestamp_field", "timestamp_utc"))
157
service = AlertsService()
139
- rules = service.collect_alerts_by_rule(size=size, timerange=timerange, alert_field=alert_field, alert_value=alert_value)
158
+ rules = service.collect_alerts_by_rule(
159
+ size=size,
160
+ timerange=timerange,
161
+ alert_field=alert_field,
162
+ alert_value=alert_value,
163
+ timestamp_field=timestamp_field,
164
+ )
165
return jsonify(rules)
166
167
@@ -158,8 +183,15 @@ def get_rules_by_host() -> jsonify:
183
timerange = str(data.get("timerange", "24h"))
184
alert_field = str(data.get("alert_field", "syslog_level"))
185
alert_value = str(data.get("alert_value", "ALERT"))
186
+ timestamp_field = str(data.get("timestamp_field", "timestamp_utc"))
187
service = AlertsService()
162
- rules = service.collect_alerts_by_rule_per_host(size=size, timerange=timerange, alert_field=alert_field, alert_value=alert_value)
188
+ rules = service.collect_alerts_by_rule_per_host(
189
+ size=size,
190
+ timerange=timerange,
191
+ alert_field=alert_field,
192
+ alert_value=alert_value,
193
+ timestamp_field=timestamp_field,
194
+ )
195
return jsonify(rules)
196
197
backend/app/services/WazuhIndexer/alerts.py
+66
-12
@@ -73,7 +73,7 @@ class AlertsService:
73
74
return {"success": True, "indices": valid_indices}
75
76
- def collect_alerts(self, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, object]:
76
+ def collect_alerts(self, size: int, timerange: str, alert_field: str, alert_value: str, timestamp_field: str) -> Dict[str, object]:
77
"""
78
Collects alerts from the Wazuh-Indexer.
79
@@ -82,6 +82,7 @@ class AlertsService:
82
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
83
alert_field (str): The field to match.
84
alert_value (str): The value to match.
85
+ timestamp_field (str): The timestamp field to sort by.
86
Returns:
87
Dict[str, object]: A dictionary containing success status and alerts or an error message.
88
"""
@@ -94,7 +95,7 @@ class AlertsService:
95
# matches = [("syslog_level", "ALERT")]
96
matches = [(alert_field, alert_value)]
97
for index_name in indices_validation["indices"]:
97
- alerts = self._collect_alerts(index_name, size=size, timerange=timerange, matches=matches)
98
+ alerts = self._collect_alerts(index_name, size=size, timerange=timerange, matches=matches, timestamp_field=timestamp_field)
99
if alerts["success"] and len(alerts["alerts"]) > 0:
100
alerts_summary.append(
101
{
@@ -134,7 +135,15 @@ class AlertsService:
135
"alerts_summary": alerts_summary,
136
}
137
137
- def collect_alerts_by_index(self, index_name: str, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, Any]:
138
+ def collect_alerts_by_index(
139
+ self,
140
+ index_name: str,
141
+ size: int,
142
+ timerange: str,
143
+ alert_field: str,
144
+ alert_value: str,
145
+ timestamp_field: str,
146
+ ) -> Dict[str, Any]:
147
"""
148
Collects alerts from the given index.
149
@@ -144,6 +153,7 @@ class AlertsService:
153
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
154
alert_field (str): The field to match.
155
alert_value (str): The value to match.
156
+ timestamp_field (str): The timestamp field to sort by.
157
158
Returns:
159
Dict[str, Any]: A dictionary containing success status, a message, and potentially the alerts from the given index.
@@ -152,7 +162,13 @@ class AlertsService:
162
return self._error_response("Invalid index name")
163
164
matches = [(alert_field, alert_value)]
155
- alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
165
+ alerts = self._collect_alerts(
166
+ index_name=index_name,
167
+ size=size,
168
+ timerange=timerange,
169
+ matches=matches,
170
+ timestamp_field=timestamp_field,
171
+ )
172
if not alerts["success"]:
173
return alerts
174
@@ -170,6 +186,7 @@ class AlertsService:
186
timerange: str,
187
alert_field: str,
188
alert_value: str,
189
+ timestamp_field: str,
190
) -> Dict[str, Any]:
191
"""
192
Collects alerts associated with a given agent name.
@@ -180,6 +197,7 @@ class AlertsService:
197
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
198
alert_field (str): The field to match.
199
alert_value (str): The value to match.
200
+ timestamp_field (str): The timestamp field to sort by.
201
202
Returns:
203
Dict[str, Any]: A dictionary containing success status, a message, and potentially the alerts associated with the agent.
@@ -191,7 +209,13 @@ class AlertsService:
209
alerts_by_agent_dict = {}
210
matches = [(alert_field, alert_value), ("agent_name", f"{agent_name}")]
211
for index_name in indices_validation["indices"]:
194
- alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
212
+ alerts = self._collect_alerts(
213
+ index_name=index_name,
214
+ size=size,
215
+ timerange=timerange,
216
+ matches=matches,
217
+ timestamp_field=timestamp_field,
218
+ )
219
if alerts["success"]:
220
for alert in alerts["alerts"]:
221
if alert["_source"]["agent_name"] == agent_name:
@@ -205,7 +229,7 @@ class AlertsService:
229
"alerts_by_agent": alerts_by_agent_list,
230
}
231
208
- def collect_alerts_by_host(self, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, int]:
232
+ def collect_alerts_by_host(self, size: int, timerange: str, alert_field: str, alert_value: str, timestamp_field: str) -> Dict[str, int]:
233
"""
234
Collects the number of alerts per host.
235
@@ -214,6 +238,7 @@ class AlertsService:
238
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
239
alert_field (str): The field to match.
240
alert_value (str): The value to match.
241
+ timestamp_field (str): The timestamp field to sort by.
242
243
Returns:
244
Dict[str, int]: A dictionary containing success status and the number of alerts per host or an error message.
@@ -225,7 +250,13 @@ class AlertsService:
250
alerts_by_host_dict = {}
251
matches = [(alert_field, alert_value)]
252
for index_name in indices_validation["indices"]:
228
- alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
253
+ alerts = self._collect_alerts(
254
+ index_name=index_name,
255
+ size=size,
256
+ timerange=timerange,
257
+ matches=matches,
258
+ timestamp_field=timestamp_field,
259
+ )
260
if alerts["success"]:
261
for alert in alerts["alerts"]:
262
host = alert["_source"]["agent_name"]
@@ -239,7 +270,7 @@ class AlertsService:
270
"alerts_by_host": alerts_by_host_list,
271
}
272
242
- def collect_alerts_by_rule(self, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, int]:
273
+ def collect_alerts_by_rule(self, size: int, timerange: str, alert_field: str, alert_value: str, timestamp_field: str) -> Dict[str, int]:
274
"""
275
Collects the number of alerts per rule.
276
@@ -248,6 +279,7 @@ class AlertsService:
279
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
280
alert_field (str): The field to match.
281
alert_value (str): The value to match.
282
+ timestamp_field (str): The timestamp field to sort by.
283
284
Returns:
285
Dict[str, int]: A dictionary containing success status and the number of alerts per rule or an error message.
@@ -259,7 +291,13 @@ class AlertsService:
291
alerts_by_rule_dict = {}
292
matches = [(alert_field, alert_value)]
293
for index_name in indices_validation["indices"]:
262
- alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
294
+ alerts = self._collect_alerts(
295
+ index_name=index_name,
296
+ size=size,
297
+ timerange=timerange,
298
+ matches=matches,
299
+ timestamp_field=timestamp_field,
300
+ )
301
if alerts["success"]:
302
for alert in alerts["alerts"]:
303
rule = alert["_source"]["rule_description"]
@@ -273,7 +311,14 @@ class AlertsService:
311
"alerts_by_rule": alerts_by_rule_list,
312
}
313
276
- def collect_alerts_by_rule_per_host(self, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, int]:
314
+ def collect_alerts_by_rule_per_host(
315
+ self,
316
+ size: int,
317
+ timerange: str,
318
+ alert_field: str,
319
+ alert_value: str,
320
+ timestamp_field: str,
321
+ ) -> Dict[str, int]:
322
"""
323
Collects the number of alerts per rule per host.
324
@@ -282,6 +327,7 @@ class AlertsService:
327
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
328
alert_field (str): The field to match.
329
alert_value (str): The value to match.
330
+ timestamp_field (str): The timestamp field to sort by.
331
332
Returns:
333
Dict[str, int]: A dictionary containing success status and the number of alerts per rule per host or an error message.
@@ -293,7 +339,13 @@ class AlertsService:
339
alerts_by_rule_per_host_dict = {}
340
matches = [(alert_field, alert_value)]
341
for index_name in indices_validation["indices"]:
296
- alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
342
+ alerts = self._collect_alerts(
343
+ index_name=index_name,
344
+ size=size,
345
+ timerange=timerange,
346
+ matches=matches,
347
+ timestamp_field=timestamp_field,
348
+ )
349
if alerts["success"]:
350
for alert in alerts["alerts"]:
351
rule = alert["_source"]["rule_description"]
@@ -325,6 +377,7 @@ class AlertsService:
377
size: int = None,
378
timerange: str = "24h",
379
matches: Iterable[Tuple[str, str]] = None,
380
+ timestamp_field: str = None,
381
) -> Dict[str, object]:
382
"""
383
Elasticsearch query to get the most recent alerts where the `rule_level` is 12 or higher or the
@@ -337,6 +390,7 @@ class AlertsService:
390
timerange (str, optional): The time range to collect alerts from. This is a string like "24h", "1w", etc.
391
matches (Iterable[Tuple[str, str]], optional): A list of tuples representing the field and value to match.
392
I.E: [("syslog_level", "ALERT"), ("agent_name", "WIN-39O01J5F7G5")]
393
+ timestamp_field (str, optional): The timestamp field to sort by.
394
395
Returns:
396
Dict[str, object]: A dictionary containing success status and alerts or an error message.
@@ -345,7 +399,7 @@ class AlertsService:
399
400
# Use QueryBuilder to construct the query
401
query_builder = QueryBuilder()
348
- query_builder.add_time_range(timerange)
402
+ query_builder.add_time_range(timerange, timestamp_field=timestamp_field)
403
if matches is not None:
404
query_builder.add_matches(matches)
405
else:
backend/app/services/WazuhIndexer/universal.py
+2
-2
@@ -192,9 +192,9 @@ class QueryBuilder:
192
"sort": [],
193
}
194
195
- def add_time_range(self, timerange: str):
195
+ def add_time_range(self, timerange: str, timestamp_field: str):
196
start = self._get_time_range_start(timerange)
197
- self.query["query"]["bool"]["must"].append({"range": {"timestamp_utc": {"gte": start, "lte": "now"}}})
197
+ self.query["query"]["bool"]["must"].append({"range": {timestamp_field: {"gte": start, "lte": "now"}}})
198
return self
199
200
def add_matches(self, matches: Iterable[Tuple[str, str]]):
backend/app/static/swagger.json
+30
@@ -975,6 +975,11 @@
975
"type": "string",
976
"default": "ALERT",
977
"description": "Value to filter alerts on. Defaults to 'ALERT'."
978
+ },
979
+ "timestamp_field": {
980
+ "type": "string",
981
+ "default": "timestamp_utc",
982
+ "description": "Field to filter alerts on. Defaults to '@timestamp'."
983
}
984
},
985
"required": []
@@ -1051,6 +1056,11 @@
1056
"type": "string",
1057
"default": "ALERT",
1058
"description": "Value to filter alerts on. Defaults to 'ALERT'."
1059
+ },
1060
+ "timestamp_field": {
1061
+ "type": "string",
1062
+ "default": "timestamp_utc",
1063
+ "description": "Field to filter alerts on. Defaults to '@timestamp'."
1064
}
1065
},
1066
"required": []
@@ -1163,6 +1173,11 @@
1173
"type": "string",
1174
"default": "ALERT",
1175
"description": "Value to filter alerts on. Defaults to 'ALERT'."
1176
+ },
1177
+ "timestamp_field": {
1178
+ "type": "string",
1179
+ "default": "timestamp_utc",
1180
+ "description": "Field to filter alerts on. Defaults to '@timestamp'."
1181
}
1182
},
1183
"required": []
@@ -1270,6 +1285,11 @@
1285
"type": "string",
1286
"default": "ALERT",
1287
"description": "Value to filter alerts on. Defaults to 'ALERT'."
1288
+ },
1289
+ "timestamp_field": {
1290
+ "type": "string",
1291
+ "default": "timestamp_utc",
1292
+ "description": "Field to filter alerts on. Defaults to '@timestamp'."
1293
}
1294
},
1295
"required": []
@@ -1377,6 +1397,11 @@
1397
"type": "string",
1398
"default": "ALERT",
1399
"description": "Value to filter alerts on. Defaults to 'ALERT'."
1400
+ },
1401
+ "timestamp_field": {
1402
+ "type": "string",
1403
+ "default": "timestamp_utc",
1404
+ "description": "Field to filter alerts on. Defaults to '@timestamp'."
1405
}
1406
},
1407
"required": []
@@ -1484,6 +1509,11 @@
1509
"type": "string",
1510
"default": "ALERT",
1511
"description": "Value to filter alerts on. Defaults to 'ALERT'."
1512
+ },
1513
+ "timestamp_field": {
1514
+ "type": "string",
1515
+ "default": "timestamp_utc",
1516
+ "description": "Field to filter alerts on. Defaults to '@timestamp'."
1517
}
1518
},
1519
"required": []