POST instead of get and removed range on query (#60)
taylor_socfortress committed
Jul 24, 2023 at 17:00 UTC
e930dd6b3960a7d61c99f3b7a6241dbbdabc840a
3 files changed
+177
-144
backend/app/routes/alerts.py
+41
-14
@@ -7,7 +7,7 @@ from app.services.WazuhIndexer.alerts import AlertsService
7
bp = Blueprint("alerts", __name__)
8
9
10
-@bp.route("/alerts", methods=["GET"])
10
+@bp.route("/alerts", methods=["POST"])
11
def get_alerts() -> jsonify:
12
"""
13
Retrieves all alerts from the AlertsService.
@@ -20,15 +20,20 @@ def get_alerts() -> jsonify:
20
jsonify: A JSON response containing a list of alerts. Each item in the list is a dictionary representing an alert,
21
containing all its associated data.
22
"""
23
- size = request.args.get("size", default=10, type=int)
24
- timerange = request.args.get("timerange", default="24h", type=str)
23
+ data = request.json
24
+
25
+ size = int(data.get("size", 10))
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
+
30
service = AlertsService()
26
- alerts = service.collect_alerts(size=size, timerange=timerange) # replace `collect_all_alerts` with `collect_alerts(size=1000)`
31
+ alerts = service.collect_alerts(size=size, timerange=timerange, alert_field=alert_field, alert_value=alert_value)
32
return jsonify(alerts)
33
34
30
-@bp.route("/alerts/<agent_name>", methods=["GET"])
31
-def get_alerts_by_agent(agent_name: str) -> jsonify:
35
+@bp.route("/alerts/agent", methods=["POST"])
36
+def get_alerts_by_agent() -> jsonify:
37
"""
38
Retrieves all alerts from the AlertsService by agent name.
39
@@ -40,15 +45,27 @@ def get_alerts_by_agent(agent_name: str) -> jsonify:
45
jsonify: A JSON response containing a list of alerts. Each item in the list is a dictionary representing an alert,
46
containing all its associated data.
47
"""
48
+ data = request.json
49
+ size = int(data.get("size", 10))
50
+ timerange = str(data.get("timerange", "24h"))
51
+ agent_name = str(data.get("agent_name", "WIN-HFOU106TD7K"))
52
+ alert_field = str(data.get("alert_field", "syslog_level"))
53
+ alert_value = str(data.get("alert_value", "ALERT"))
54
service = AlertsService()
44
- size = request.args.get("size", default=10, type=int)
45
- timerange = request.args.get("timerange", default="24h", type=str)
46
- alerts = service.collect_alerts_by_agent_name(agent_name=agent_name, size=size, timerange=timerange)
55
+ # size = request.args.get("size", default=10, type=int)
56
+ # timerange = request.args.get("timerange", default="24h", type=str)
57
+ alerts = service.collect_alerts_by_agent_name(
58
+ agent_name=agent_name,
59
+ size=size,
60
+ timerange=timerange,
61
+ alert_field=alert_field,
62
+ alert_value=alert_value,
63
+ )
64
return jsonify(alerts)
65
66
50
-@bp.route("/alerts/index/<index_name>", methods=["GET"])
51
-def get_alerts_by_index(index_name: str) -> jsonify:
67
+@bp.route("/alerts/index", methods=["POST"])
68
+def get_alerts_by_index() -> jsonify:
69
"""
70
Retrieves all alerts from the AlertsService by index name.
71
@@ -60,10 +77,20 @@ def get_alerts_by_index(index_name: str) -> jsonify:
77
jsonify: A JSON response containing a list of alerts. Each item in the list is a dictionary representing an alert,
78
containing all its associated data.
79
"""
63
- size = request.args.get("size", default=10, type=int)
64
- timerange = request.args.get("timerange", default="24h", type=str)
80
+ data = request.json
81
+ size = int(data.get("size", 10))
82
+ timerange = str(data.get("timerange", "24h"))
83
+ index_name = str(data.get("index_name", "wazuh*"))
84
+ alert_field = str(data.get("alert_field", "syslog_level"))
85
+ alert_value = str(data.get("alert_value", "ALERT"))
86
service = AlertsService()
66
- alerts = service.collect_alerts_by_index(index_name=index_name, size=size, timerange=timerange)
87
+ alerts = service.collect_alerts_by_index(
88
+ index_name=index_name,
89
+ size=size,
90
+ timerange=timerange,
91
+ alert_field=alert_field,
92
+ alert_value=alert_value,
93
+ )
94
return jsonify(alerts)
95
96
backend/app/services/WazuhIndexer/alerts.py
+22
-42
@@ -73,14 +73,15 @@ class AlertsService:
73
74
return {"success": True, "indices": valid_indices}
75
76
- def collect_alerts(self, size: int, timerange: str) -> Dict[str, object]:
76
+ def collect_alerts(self, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, object]:
77
"""
78
Collects alerts from the Wazuh-Indexer.
79
80
Args:
81
size (int): The maximum number of alerts to return.
82
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
83
-
83
+ alert_field (str): The field to match.
84
+ alert_value (str): The value to match.
85
Returns:
86
Dict[str, object]: A dictionary containing success status and alerts or an error message.
87
"""
@@ -90,7 +91,8 @@ class AlertsService:
91
92
alerts_summary = []
93
# matches = [("syslog_level", "ALERT"), ("agent_name", "WIN-39O01J5F7G5")]
93
- matches = [("syslog_level", "ALERT")]
94
+ # matches = [("syslog_level", "ALERT")]
95
+ matches = [(alert_field, alert_value)]
96
for index_name in indices_validation["indices"]:
97
alerts = self._collect_alerts(index_name, size=size, timerange=timerange, matches=matches)
98
if alerts["success"] and len(alerts["alerts"]) > 0:
@@ -132,7 +134,7 @@ class AlertsService:
134
"alerts_summary": alerts_summary,
135
}
136
135
- def collect_alerts_by_index(self, index_name: str, size: int, timerange: str) -> Dict[str, Any]:
137
+ def collect_alerts_by_index(self, index_name: str, size: int, timerange: str, alert_field: str, alert_value: str) -> Dict[str, Any]:
138
"""
139
Collects alerts from the given index.
140
@@ -140,6 +142,8 @@ class AlertsService:
142
index_name (str): The name of the index to query.
143
size (int): The maximum number of alerts to return.
144
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
145
+ alert_field (str): The field to match.
146
+ alert_value (str): The value to match.
147
148
Returns:
149
Dict[str, Any]: A dictionary containing success status, a message, and potentially the alerts from the given index.
@@ -147,7 +151,8 @@ class AlertsService:
151
if not self.is_valid_index(index_name):
152
return self._error_response("Invalid index name")
153
150
- alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange)
154
+ matches = [(alert_field, alert_value)]
155
+ alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
156
if not alerts["success"]:
157
return alerts
158
@@ -158,7 +163,14 @@ class AlertsService:
163
"total_alerts": len(alerts["alerts"]),
164
}
165
161
- def collect_alerts_by_agent_name(self, agent_name: str, size: int, timerange: str) -> Dict[str, Any]:
166
+ def collect_alerts_by_agent_name(
167
+ self,
168
+ agent_name: str,
169
+ size: int,
170
+ timerange: str,
171
+ alert_field: str,
172
+ alert_value: str,
173
+ ) -> Dict[str, Any]:
174
"""
175
Collects alerts associated with a given agent name.
176
@@ -166,6 +178,8 @@ class AlertsService:
178
agent_name (str): The agent name associated with the alerts.
179
size (int): The maximum number of alerts to return.
180
timerange (str): The time range to collect alerts from. This is a string like "24h", "1w", etc.
181
+ alert_field (str): The field to match.
182
+ alert_value (str): The value to match.
183
184
Returns:
185
Dict[str, Any]: A dictionary containing success status, a message, and potentially the alerts associated with the agent.
@@ -175,7 +189,7 @@ class AlertsService:
189
return indices_validation
190
191
alerts_by_agent_dict = {}
178
- matches = [("syslog_level", "ALERT"), ("agent_name", f"{agent_name}")]
192
+ matches = [(alert_field, alert_value), ("agent_name", f"{agent_name}")]
193
for index_name in indices_validation["indices"]:
194
alerts = self._collect_alerts(index_name=index_name, size=size, timerange=timerange, matches=matches)
195
if alerts["success"]:
@@ -296,40 +310,6 @@ class AlertsService:
310
"""
311
return {"message": message, "success": False}
312
299
- # def _collect_alerts(self, index_name: str, size: int = None, timerange: str = "24h") -> Dict[str, object]:
300
- # """
301
- # Elasticsearch query to get the most recent alerts where the `rule_level` is 12 or higher or the
302
- # `syslog_level` field is `ALERT` and return the results in descending order by the `timestamp_utc` field.
303
- # The number of alerts to return can be limited by the `size` parameter.
304
-
305
- # Args:
306
- # index_name (str): The name of the index to query.
307
- # size (int, optional): The maximum number of alerts to return. If None, all alerts are returned.
308
- # timerange (str, optional): The time range to collect alerts from. This is a string like "24h", "1w", etc.
309
-
310
- # Returns:
311
- # Dict[str, object]: A dictionary containing success status and alerts or an error message.
312
- # """
313
- # logger.info(f"Collecting alerts from {index_name}")
314
- # query = self._build_query(timerange=timerange) # Use the provided query
315
- # try:
316
- # alerts = self.es.search(index=index_name, body=query, size=size)
317
- # alerts_list = [alert for alert in alerts["hits"]["hits"]]
318
-
319
- # # Iterate over each alert and invoke invoke_socfortress function
320
- # for alert in alerts_list:
321
- # ask_socfortress = self.asksocfortress_service.invoke_asksocfortress(alert["_source"]["rule_description"])
322
- # alert["ask_socfortress"] = ask_socfortress # Add the result to the alert
323
-
324
- # return {
325
- # "message": "Successfully collected alerts",
326
- # "success": True,
327
- # "alerts": alerts_list, # Return the alerts list with the added results
328
- # }
329
- # except Exception as e:
330
- # logger.error(f"Failed to collect alerts: {e}")
331
- # return {"message": "Failed to collect alerts", "success": False}
332
-
313
def _collect_alerts(
314
self,
315
index_name: str,
@@ -361,7 +341,7 @@ class AlertsService:
341
query_builder.add_matches(matches)
342
else:
343
query_builder.add_matches([("syslog_level", "ALERT")])
364
- query_builder.add_range("rule_level", "12")
344
+ # query_builder.add_range("rule_level", "12") # removed to get all alerts regardless of rule level
345
query_builder.add_sort("timestamp_utc")
346
347
# Get the final query
backend/app/static/swagger.json
+114
-88
@@ -947,31 +947,41 @@
947
}
948
},
949
"/alerts": {
950
- "get": {
951
- "summary": "Get alerts",
952
- "description": "Endpoint to get alerts. Retrieves all alerts from the AlertsService.",
953
- "parameters": [
954
- {
955
- "name": "size",
956
- "in": "query",
957
- "description": "The number of alerts to return. Defaults to 10.",
958
- "required": false,
959
- "schema": {
960
- "type": "integer",
961
- "default": 10
962
- }
963
- },
964
- {
965
- "name": "timerange",
966
- "in": "query",
967
- "description": "The time range to consider for alerts. Defaults to '24h'.",
968
- "required": false,
969
- "schema": {
970
- "type": "string",
971
- "default": "24h"
950
+ "post": {
951
+ "summary": "Create and get alerts",
952
+ "description": "Endpoint to create and get alerts. Retrieves all available alerts from the AlertsService based on the provided parameters.",
953
+ "requestBody": {
954
+ "content": {
955
+ "application/json": {
956
+ "schema": {
957
+ "type": "object",
958
+ "properties": {
959
+ "size": {
960
+ "type": "integer",
961
+ "default": 10,
962
+ "description": "The number of alerts to return. Defaults to 10."
963
+ },
964
+ "timerange": {
965
+ "type": "string",
966
+ "default": "24h",
967
+ "description": "The time range to consider for alerts. Defaults to '24h'."
968
+ },
969
+ "alert_field": {
970
+ "type": "string",
971
+ "default": "syslog_level",
972
+ "description": "Field to filter alerts on. Defaults to 'syslog_level'."
973
+ },
974
+ "alert_value": {
975
+ "type": "string",
976
+ "default": "ALERT",
977
+ "description": "Value to filter alerts on. Defaults to 'ALERT'."
978
+ }
979
+ },
980
+ "required": []
981
+ }
982
}
983
}
974
- ],
984
+ },
985
"responses": {
986
"200": {
987
"description": "Successful operation",
@@ -1003,43 +1013,51 @@
1013
}
1014
}
1015
},
1006
- "operationId": "getAlerts",
1016
+ "operationId": "createAndGetAlerts",
1017
"tags": ["Wazuh-Indexer"]
1018
}
1019
},
1010
- "/alerts/{agent_name}": {
1011
- "get": {
1012
- "summary": "Get alerts from a specific agent",
1013
- "description": "Endpoint to get alerts from a specific agent.",
1014
- "parameters": [
1015
- {
1016
- "name": "agent_name",
1017
- "in": "path",
1018
- "description": "The name of the host to get alerts from.",
1019
- "required": true,
1020
- "type": "string"
1021
- },
1022
- {
1023
- "name": "size",
1024
- "in": "query",
1025
- "description": "The number of alerts to return. Defaults to 10.",
1026
- "required": false,
1027
- "schema": {
1028
- "type": "integer",
1029
- "default": 10
1030
- }
1031
- },
1032
- {
1033
- "name": "timerange",
1034
- "in": "query",
1035
- "description": "The time range to consider for alerts. Defaults to '24h'.",
1036
- "required": false,
1037
- "schema": {
1038
- "type": "string",
1039
- "default": "24h"
1020
+ "/alerts/agent": {
1021
+ "post": {
1022
+ "summary": "Create and get alerts from a specific agent",
1023
+ "description": "Endpoint to create and get alerts from a specific agent. Retrieves all available alerts from the AlertsService based on the provided parameters.",
1024
+ "requestBody": {
1025
+ "content": {
1026
+ "application/json": {
1027
+ "schema": {
1028
+ "type": "object",
1029
+ "properties": {
1030
+ "agent_name": {
1031
+ "type": "string",
1032
+ "default": "WIN-HFOU106TD7K",
1033
+ "description": "The name of the host to get alerts from."
1034
+ },
1035
+ "size": {
1036
+ "type": "integer",
1037
+ "default": 10,
1038
+ "description": "The number of alerts to return. Defaults to 10."
1039
+ },
1040
+ "timerange": {
1041
+ "type": "string",
1042
+ "default": "24h",
1043
+ "description": "The time range to consider for alerts. Defaults to '24h'."
1044
+ },
1045
+ "alert_field": {
1046
+ "type": "string",
1047
+ "default": "syslog_level",
1048
+ "description": "Field to filter alerts on. Defaults to 'syslog_level'."
1049
+ },
1050
+ "alert_value": {
1051
+ "type": "string",
1052
+ "default": "ALERT",
1053
+ "description": "Value to filter alerts on. Defaults to 'ALERT'."
1054
+ }
1055
+ },
1056
+ "required": []
1057
+ }
1058
}
1059
}
1042
- ],
1060
+ },
1061
"responses": {
1062
"200": {
1063
"description": "Successful operation",
@@ -1107,43 +1125,51 @@
1125
}
1126
}
1127
},
1110
- "operationId": "getAlertsFromAgent",
1128
+ "operationId": "createAndGetAlertsByAgent",
1129
"tags": ["Wazuh-Indexer"]
1130
}
1131
},
1114
- "/alerts/index/{index_name}": {
1115
- "get": {
1116
- "summary": "Get alerts from an index",
1117
- "description": "Endpoint to get alerts from an index.",
1118
- "parameters": [
1119
- {
1120
- "name": "index_name",
1121
- "in": "path",
1122
- "description": "The name of the index to get alerts from.",
1123
- "required": true,
1124
- "type": "string"
1125
- },
1126
- {
1127
- "name": "size",
1128
- "in": "query",
1129
- "description": "The number of alerts to return. Defaults to 10.",
1130
- "required": false,
1131
- "schema": {
1132
- "type": "integer",
1133
- "default": 10
1134
- }
1135
- },
1136
- {
1137
- "name": "timerange",
1138
- "in": "query",
1139
- "description": "The time range to consider for alerts. Defaults to '24h'.",
1140
- "required": false,
1141
- "schema": {
1142
- "type": "string",
1143
- "default": "24h"
1132
+ "/alerts/index": {
1133
+ "post": {
1134
+ "summary": "Create and get alerts from an index",
1135
+ "description": "Endpoint to create and get alerts from an index. Retrieves all available alerts from the AlertsService based on the provided parameters.",
1136
+ "requestBody": {
1137
+ "content": {
1138
+ "application/json": {
1139
+ "schema": {
1140
+ "type": "object",
1141
+ "properties": {
1142
+ "index_name": {
1143
+ "type": "string",
1144
+ "default": "wazuh*",
1145
+ "description": "The name of the index to get alerts from."
1146
+ },
1147
+ "size": {
1148
+ "type": "integer",
1149
+ "default": 10,
1150
+ "description": "The number of alerts to return. Defaults to 10."
1151
+ },
1152
+ "timerange": {
1153
+ "type": "string",
1154
+ "default": "24h",
1155
+ "description": "The time range to consider for alerts. Defaults to '24h'."
1156
+ },
1157
+ "alert_field": {
1158
+ "type": "string",
1159
+ "default": "syslog_level",
1160
+ "description": "Field to filter alerts on. Defaults to 'syslog_level'."
1161
+ },
1162
+ "alert_value": {
1163
+ "type": "string",
1164
+ "default": "ALERT",
1165
+ "description": "Value to filter alerts on. Defaults to 'ALERT'."
1166
+ }
1167
+ },
1168
+ "required": []
1169
+ }
1170
}
1171
}
1146
- ],
1172
+ },
1173
"responses": {
1174
"200": {
1175
"description": "Successful operation",
@@ -1211,7 +1237,7 @@
1237
}
1238
}
1239
},
1214
- "operationId": "getAlertsFromIndex",
1240
+ "operationId": "createAndGetAlertsFromIndex",
1241
"tags": ["Wazuh-Indexer"]
1242
}
1243
},