@cryptotaxi247 / CoPilot / commits / c21cc943

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": []