blacked
Taylor committed
Jul 10, 2023 at 17:04 UTC
b2bb1fb69b76495df931b423f86e2c1cdac03194
44 files changed
+2457
-1993
.pre-commit-config.yaml
+1
-1
@@ -25,7 +25,7 @@ repos:
25
rev: 23.3.0
26
hooks:
27
- id: black
28
- language_version: python3.9
28
+ language_version: python3.11
29
30
- repo: https://github.com/asottile/setup-cfg-fmt
31
rev: v2.3.0
README.md
+1
@@ -1,2 +1,3 @@
1
# CoPilot
2
+
3
SOCFortress CoPilot
backend/README.MD
+1
-3
@@ -79,7 +79,6 @@ classDiagram
79
}
80
```
81
82
-
82
# Connector Classes
83
84
```mermaid
@@ -132,7 +131,7 @@ classDiagram
131
}
132
```
133
135
-# Routes
134
+# Routes
135
136
```mermaid
137
graph TD;
@@ -165,4 +164,3 @@ graph TD;
164
O --> V[Return Data]
165
166
```
168
-
backend/app/__init__.py
+5
-5
@@ -37,15 +37,15 @@ db = SQLAlchemy(app)
37
migrate = Migrate(app, db)
38
ma = Marshmallow(app)
39
40
-from app.routes.connectors import bp as connectors_bp # Import the blueprint
40
from app.routes.agents import bp as agents_bp # Import the blueprint
42
-from app.routes.rules import bp as rules_bp # Import the blueprint
43
-from app.routes.graylog import bp as graylog_bp # Import the blueprint
41
from app.routes.alerts import bp as alerts_bp # Import the blueprint
45
-from app.routes.wazuhindexer import bp as wazuhindexer_bp # Import the blueprint
42
+from app.routes.connectors import bp as connectors_bp # Import the blueprint
43
+from app.routes.dfir_iris import bp as dfir_iris_bp # Import the blueprint
44
+from app.routes.graylog import bp as graylog_bp # Import the blueprint
45
+from app.routes.rules import bp as rules_bp # Import the blueprint
46
from app.routes.shuffle import bp as shuffle_bp # Import the blueprint
47
from app.routes.velociraptor import bp as velociraptor_bp # Import the blueprint
48
-from app.routes.dfir_iris import bp as dfir_iris_bp # Import the blueprint
48
+from app.routes.wazuhindexer import bp as wazuhindexer_bp # Import the blueprint
49
50
app.register_blueprint(connectors_bp) # Register the connectors blueprint
51
app.register_blueprint(agents_bp) # Register the agents blueprint
backend/app/models/connectors.py
+37
-32
@@ -1,23 +1,26 @@
1
import importlib
2
import json
3
import os
4
-import pika
4
+from abc import ABC
5
+from abc import abstractmethod
6
from dataclasses import dataclass
7
+
8
+import grpc
9
+import pika
10
+import pyvelociraptor
11
import requests
7
-from abc import ABC, abstractmethod
12
from elasticsearch7 import Elasticsearch
13
+from flask import current_app
14
from loguru import logger
10
-from sqlalchemy.orm.exc import NoResultFound
11
-import pyvelociraptor
15
from pyvelociraptor import api_pb2
16
from pyvelociraptor import api_pb2_grpc
14
-from werkzeug.utils import secure_filename
15
-import grpc
16
-
17
from sqlalchemy.exc import SQLAlchemyError
18
-from flask import current_app
18
+from sqlalchemy.orm.exc import NoResultFound
19
+from werkzeug.utils import secure_filename
20
20
-from app.models.models import Connectors, connectors_schema, ConnectorsAvailable
21
+from app.models.models import Connectors
22
+from app.models.models import ConnectorsAvailable
23
+from app.models.models import connectors_schema
24
25
26
def dynamic_import(module_name, class_name):
@@ -98,7 +101,7 @@ class WazuhIndexerConnector(Connector):
101
:return: A dictionary containing the status of the connection attempt and information about the cluster's health.
102
"""
103
logger.info(
101
- f"Verifying the wazuh-indexer connection to {self.attributes['connector_url']}"
104
+ f"Verifying the wazuh-indexer connection to {self.attributes['connector_url']}",
105
)
106
try:
107
es = Elasticsearch(
@@ -117,7 +120,7 @@ class WazuhIndexerConnector(Connector):
120
return {"connectionSuccessful": True}
121
except Exception as e:
122
logger.error(
120
- f"Connection to {self.attributes['connector_url']} failed with error: {e}"
123
+ f"Connection to {self.attributes['connector_url']} failed with error: {e}",
124
)
125
return {"connectionSuccessful": False, "clusterHealth": None}
126
@@ -140,7 +143,7 @@ class GraylogConnector(Connector):
143
dict: A dictionary containing 'connectionSuccessful' status and 'roles' if the connection is successful.
144
"""
145
logger.info(
143
- f"Verifying the graylog connection to {self.attributes['connector_url']}"
146
+ f"Verifying the graylog connection to {self.attributes['connector_url']}",
147
)
148
try:
149
graylog_roles = requests.get(
@@ -153,17 +156,17 @@ class GraylogConnector(Connector):
156
)
157
if graylog_roles.status_code == 200:
158
logger.info(
156
- f"Connection to {self.attributes['connector_url']} successful"
159
+ f"Connection to {self.attributes['connector_url']} successful",
160
)
161
return {"connectionSuccessful": True}
162
else:
163
logger.error(
161
- f"Connection to {self.attributes['connector_url']} failed with error: {graylog_roles.text}"
164
+ f"Connection to {self.attributes['connector_url']} failed with error: {graylog_roles.text}",
165
)
166
return {"connectionSuccessful": False, "roles": None}
167
except Exception as e:
168
logger.error(
166
- f"Connection to {self.attributes['connector_url']} failed with error: {e}"
169
+ f"Connection to {self.attributes['connector_url']} failed with error: {e}",
170
)
171
return {"connectionSuccessful": False, "roles": None}
172
@@ -186,7 +189,7 @@ class WazuhManagerConnector(Connector):
189
dict: A dictionary containing 'connectionSuccessful' status and 'authToken' if the connection is successful.
190
"""
191
logger.info(
189
- f"Verifying the wazuh-manager connection to {self.attributes['connector_url']}"
192
+ f"Verifying the wazuh-manager connection to {self.attributes['connector_url']}",
193
)
194
try:
195
wazuh_auth_token = requests.get(
@@ -204,12 +207,12 @@ class WazuhManagerConnector(Connector):
207
return {"connectionSuccessful": True, "authToken": wazuh_auth_token}
208
else:
209
logger.error(
207
- f"Connection to {self.attributes['connector_url']} failed with error: {wazuh_auth_token.text}"
210
+ f"Connection to {self.attributes['connector_url']} failed with error: {wazuh_auth_token.text}",
211
)
212
return {"connectionSuccessful": False, "authToken": None}
213
except Exception as e:
214
logger.error(
212
- f"Connection to {self.attributes['connector_url']} failed with error: {e}"
215
+ f"Connection to {self.attributes['connector_url']} failed with error: {e}",
216
)
217
return {"connectionSuccessful": False, "authToken": None}
218
@@ -241,11 +244,11 @@ class ShuffleConnector(Connector):
244
dict: A dictionary containing 'connectionSuccessful' status and 'apps' if the connection is successful.
245
"""
246
logger.info(
244
- f"Verifying the shuffle connection to {self.attributes['connector_url']}"
247
+ f"Verifying the shuffle connection to {self.attributes['connector_url']}",
248
)
249
try:
250
headers = {
248
- "Authorization": f"Bearer {self.attributes['connector_api_key']}"
251
+ "Authorization": f"Bearer {self.attributes['connector_api_key']}",
252
}
253
shuffle_apps = requests.get(
254
f"{self.attributes['connector_url']}/api/v1/apps",
@@ -254,17 +257,17 @@ class ShuffleConnector(Connector):
257
)
258
if shuffle_apps.status_code == 200:
259
logger.info(
257
- f"Connection to {self.attributes['connector_url']} successful"
260
+ f"Connection to {self.attributes['connector_url']} successful",
261
)
262
return {"connectionSuccessful": True}
263
else:
264
logger.error(
262
- f"Connection to {self.attributes['connector_url']} failed with error: {shuffle_apps.text}"
265
+ f"Connection to {self.attributes['connector_url']} failed with error: {shuffle_apps.text}",
266
)
267
return {"connectionSuccessful": False}
268
except Exception as e:
269
logger.error(
267
- f"Connection to {self.attributes['connector_url']} failed with error: {e}"
270
+ f"Connection to {self.attributes['connector_url']} failed with error: {e}",
271
)
272
return {"connectionSuccessful": False}
273
@@ -287,11 +290,11 @@ class DfirIrisConnector(Connector):
290
dict: A dictionary containing 'connectionSuccessful' status and 'response' if the connection is successful.
291
"""
292
logger.info(
290
- f"Verifying the dfir-iris connection to {self.attributes['connector_url']}"
293
+ f"Verifying the dfir-iris connection to {self.attributes['connector_url']}",
294
)
295
try:
296
headers = {
294
- "Authorization": f"Bearer {self.attributes['connector_api_key']}"
297
+ "Authorization": f"Bearer {self.attributes['connector_api_key']}",
298
}
299
dfir_iris = requests.get(
300
f"{self.attributes['connector_url']}/api/ping",
@@ -301,17 +304,17 @@ class DfirIrisConnector(Connector):
304
# See if 200 is returned
305
if dfir_iris.status_code == 200:
306
logger.info(
304
- f"Connection to {self.attributes['connector_url']} successful"
307
+ f"Connection to {self.attributes['connector_url']} successful",
308
)
309
return {"connectionSuccessful": True}
310
else:
311
logger.error(
309
- f"Connection to {self.attributes['connector_url']} failed with error: {dfir_iris.text}"
312
+ f"Connection to {self.attributes['connector_url']} failed with error: {dfir_iris.text}",
313
)
314
return {"connectionSuccessful": False, "response": None}
315
except Exception as e:
316
logger.error(
314
- f"Connection to {self.attributes['connector_url']} failed with error: {e}"
317
+ f"Connection to {self.attributes['connector_url']} failed with error: {e}",
318
)
319
return {"connectionSuccessful": False, "response": None}
320
@@ -351,7 +354,9 @@ class VelociraptorConnector(Connector):
354
options = (("grpc.ssl_target_name_override", "VelociraptorServer"),)
355
356
with grpc.secure_channel(
354
- config["api_connection_string"], creds, options
357
+ config["api_connection_string"],
358
+ creds,
359
+ options,
360
) as channel:
361
stub = api_pb2_grpc.APIStub(channel)
362
client_query = "SELECT * FROM info()"
@@ -395,7 +400,7 @@ class RabbitMQConnector(Connector):
400
Verifies the connection to RabbitMQ service.
401
"""
402
logger.info(
398
- f"Verifying the rabbitmq connection to {self.attributes['connector_url']}"
403
+ f"Verifying the rabbitmq connection to {self.attributes['connector_url']}",
404
)
405
try:
406
# For the connector_url, strip out the host and port and use that for the connection
@@ -415,7 +420,7 @@ class RabbitMQConnector(Connector):
420
connection = pika.BlockingConnection(parameters)
421
if connection.is_open:
422
logger.info(
418
- f"Connection to {self.attributes['connector_url']} successful"
423
+ f"Connection to {self.attributes['connector_url']} successful",
424
)
425
return {"connectionSuccessful": True}
426
else:
@@ -423,7 +428,7 @@ class RabbitMQConnector(Connector):
428
return {"connectionSuccessful": False, "response": None}
429
except Exception as e:
430
logger.error(
426
- f"Connection to {self.attributes['connector_url']} failed with error: {e}"
431
+ f"Connection to {self.attributes['connector_url']} failed with error: {e}",
432
)
433
return {"connectionSuccessful": False, "response": None}
434
backend/app/routes/agents.py
+8
-7
@@ -1,15 +1,16 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.agents.agents import AgentService, AgentSyncService
6
-
7
-from app.services.WazuhManager.universal import UniversalService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.agents.agents import AgentService
9
+from app.services.agents.agents import AgentSyncService
10
from app.services.WazuhManager.agent import WazuhManagerAgentService
11
+from app.services.WazuhManager.universal import UniversalService
12
from app.services.WazuhManager.vulnerability import VulnerabilityService
13
11
-
12
-
14
bp = Blueprint("agents", __name__)
15
16
backend/app/routes/alerts.py
+7
-3
@@ -1,8 +1,12 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.agents.agents import AgentService, AgentSyncService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.agents.agents import AgentService
9
+from app.services.agents.agents import AgentSyncService
10
from app.services.WazuhIndexer.alerts import AlertsService
11
12
bp = Blueprint("alerts", __name__)
backend/app/routes/connectors.py
+12
-10
@@ -1,13 +1,13 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.models import (
4
- ConnectorsAvailable,
5
- Connectors,
6
- connectors_available_schema,
7
-)
5
9
-from app.services.connectors.connectors import ConnectorService
6
from app import db
7
+from app.models.models import Connectors
8
+from app.models.models import ConnectorsAvailable
9
+from app.models.models import connectors_available_schema
10
+from app.services.connectors.connectors import ConnectorService
11
12
bp = Blueprint("connectors", __name__)
13
@@ -48,7 +48,7 @@ def get_connector_details(id):
48
# Call service function instead of direct function call
49
service = ConnectorService(db)
50
connector_validated = service.validate_connector_exists(
51
- int(id)
51
+ int(id),
52
) # convert id to integer
53
logger.info(connector_validated)
54
if connector_validated["success"] == False:
@@ -70,14 +70,16 @@ def update_connector_route(id):
70
id (str): The id of the connector to be updated.
71
72
Returns:
73
- json: A JSON response containing the success status of the update operation and a message indicating the status. If the update operation was successful, it returns the connector name and the status of the connection verification.
73
+ json: A JSON response containing the success status of the update operation and
74
+ a message indicating the status. If the update operation was successful,
75
+ it returns the connector name and the status of the connection verification.
76
"""
77
api_key_connector = ["Shuffle", "DFIR-IRIS", "Velociraptor"]
78
79
request_data = request.get_json()
80
service = ConnectorService(db)
81
connector_validated = service.validate_connector_exists(
80
- int(id)
82
+ int(id),
83
) # convert id to integer
84
logger.info(connector_validated)
85
if connector_validated["success"] == False:
backend/app/routes/dfir_iris.py
+21
-9
@@ -1,15 +1,18 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.Graylog.messages import MessagesService
6
-from app.services.Graylog.metrics import MetricsService
7
-from app.services.Graylog.index import IndexService
8
-from app.services.Graylog.inputs import InputsService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.DFIR_IRIS.alerts import AlertsService
9
+from app.services.DFIR_IRIS.assets import AssetsService
10
from app.services.DFIR_IRIS.cases import CasesService
11
from app.services.DFIR_IRIS.notes import NotesService
11
-from app.services.DFIR_IRIS.assets import AssetsService
12
-from app.services.DFIR_IRIS.alerts import AlertsService
12
+from app.services.Graylog.index import IndexService
13
+from app.services.Graylog.inputs import InputsService
14
+from app.services.Graylog.messages import MessagesService
15
+from app.services.Graylog.metrics import MetricsService
16
17
bp = Blueprint("dfir_iris", __name__)
18
@@ -26,6 +29,7 @@ def get_cases():
29
cases = service.list_cases()
30
return cases
31
32
+
33
@bp.route("/dfir_iris/cases/<case_id>", methods=["GET"])
34
def get_case(case_id):
35
"""
@@ -42,6 +46,7 @@ def get_case(case_id):
46
case = service.get_case(case_id=case_id)
47
return case
48
49
+
50
@bp.route("/dfir_iris/cases/<case_id>/notes", methods=["GET"])
51
def get_case_notes(case_id):
52
"""
@@ -60,6 +65,7 @@ def get_case_notes(case_id):
65
notes = notes_service.get_case_notes(search_term=search_term, cid=int(case_id))
66
return notes
67
68
+
69
@bp.route("/dfir_iris/cases/<case_id>/note", methods=["POST"])
70
def create_case_note(case_id):
71
"""
@@ -76,9 +82,14 @@ def create_case_note(case_id):
82
case_id_exists = case_service.check_case_id(case_id=case_id)
83
if case_id_exists["success"] == False:
84
return case_id_exists
79
- created_note = notes_service.create_case_note(cid=int(case_id), note_title=note_title, note_content=note_content)
85
+ created_note = notes_service.create_case_note(
86
+ cid=int(case_id),
87
+ note_title=note_title,
88
+ note_content=note_content,
89
+ )
90
return created_note
91
92
+
93
@bp.route("/dfir_iris/cases/<case_id>/assets", methods=["GET"])
94
def get_case_assets(case_id):
95
"""
@@ -96,6 +107,7 @@ def get_case_assets(case_id):
107
assets = asset_service.get_case_assets(cid=int(case_id))
108
return assets
109
110
+
111
@bp.route("/dfir_iris/alerts", methods=["GET"])
112
def get_alerts():
113
"""
backend/app/routes/graylog.py
+9
-6
@@ -1,11 +1,14 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.Graylog.messages import MessagesService
6
-from app.services.Graylog.metrics import MetricsService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
from app.services.Graylog.index import IndexService
9
from app.services.Graylog.inputs import InputsService
10
+from app.services.Graylog.messages import MessagesService
11
+from app.services.Graylog.metrics import MetricsService
12
from app.services.WazuhManager.wazuhmanager import WazuhManagerService
13
14
bp = Blueprint("graylog", __name__)
@@ -36,7 +39,7 @@ def get_metrics():
39
uncommitted_journal_size = service.collect_uncommitted_journal_size()
40
metrics = service.collect_throughput_metrics()
41
return jsonify(
39
- {"uncommitted_journal_size": uncommitted_journal_size, "metrics": metrics}
42
+ {"uncommitted_journal_size": uncommitted_journal_size, "metrics": metrics},
43
)
44
45
@@ -81,5 +84,5 @@ def get_inputs():
84
running_inputs = service.collect_running_inputs()
85
configured_inputs = service.collect_configured_inputs()
86
return jsonify(
84
- {"running_inputs": running_inputs, "configured_inputs": configured_inputs}
87
+ {"running_inputs": running_inputs, "configured_inputs": configured_inputs},
88
)
backend/app/routes/index.py
+7
-3
@@ -1,8 +1,12 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.agents.agents import AgentService, AgentSyncService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.agents.agents import AgentService
9
+from app.services.agents.agents import AgentSyncService
10
from app.services.WazuhIndexer.alerts import AlertsService
11
from app.services.WazuhIndexer.cluster import ClusterService
12
backend/app/routes/rules.py
+7
-8
@@ -1,16 +1,15 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
from app.models.rules import DisabledRules
6
-
7
-from app.services.WazuhManager.wazuhmanager import WazuhManagerService
8
-
9
-from app.services.WazuhManager.universal import UniversalService
10
-
9
from app.services.WazuhManager.disabled_rule import DisableRuleService
12
-
10
from app.services.WazuhManager.enabled_rule import EnableRuleService
11
+from app.services.WazuhManager.universal import UniversalService
12
+from app.services.WazuhManager.wazuhmanager import WazuhManagerService
13
14
bp = Blueprint("rules", __name__)
15
backend/app/routes/shuffle.py
+12
-4
@@ -1,8 +1,12 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.agents.agents import AgentService, AgentSyncService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.agents.agents import AgentService
9
+from app.services.agents.agents import AgentSyncService
10
from app.services.Shuffle.workflows import WorkflowsService
11
12
bp = Blueprint("shuffle", __name__)
@@ -20,6 +24,7 @@ def get_workflows():
24
workflows = service.collect_workflows()
25
return workflows
26
27
+
28
@bp.route("/shuffle/workflows/executions", methods=["GET"])
29
def get_workflows_executions():
30
"""
@@ -34,9 +39,12 @@ def get_workflows_executions():
39
message = "No workflows found"
40
return jsonify({"message": message, "success": False}), 500
41
for workflow in workflow_details["workflows"]:
37
- workflow["status"] = service.collect_workflow_executions_status(workflow["workflow_id"])
42
+ workflow["status"] = service.collect_workflow_executions_status(
43
+ workflow["workflow_id"],
44
+ )
45
return workflow_details
46
47
+
48
@bp.route("/shuffle/workflows/executions/<workflow_id>", methods=["GET"])
49
def get_workflow_executions(workflow_id):
50
"""
backend/app/routes/velociraptor.py
+31
-9
@@ -1,13 +1,18 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.agents.agents import AgentService, AgentSyncService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.agents.agents import AgentService
9
+from app.services.agents.agents import AgentSyncService
10
from app.services.Velociraptor.artifacts import ArtifactsService
11
from app.services.Velociraptor.universal import UniversalService
12
13
bp = Blueprint("velociraptor", __name__)
14
15
+
16
@bp.route("/velociraptor/artifacts", methods=["GET"])
17
def get_artifacts():
18
"""
@@ -21,11 +26,12 @@ def get_artifacts():
26
artifacts = service.collect_artifacts()
27
return artifacts
28
29
+
30
@bp.route("/velociraptor/artifacts/linux", methods=["GET"])
31
def get_artifacts_linux():
32
"""
33
Endpoint to list all available artifacts.
28
- It processes each artifact to verify the connection and returns the results where the name
34
+ It processes each artifact to verify the connection and returns the results where the name
35
begins with `Linux`.
36
37
Returns:
@@ -35,11 +41,12 @@ def get_artifacts_linux():
41
linux_artifacts = service.collect_artifacts_linux()
42
return linux_artifacts
43
44
+
45
@bp.route("/velociraptor/artifacts/windows", methods=["GET"])
46
def get_artifacts_windows():
47
"""
48
Endpoint to list all available artifacts.
42
- It processes each artifact to verify the connection and returns the results where the name
49
+ It processes each artifact to verify the connection and returns the results where the name
50
begins with `Windows`.
51
52
Returns:
@@ -49,11 +56,12 @@ def get_artifacts_windows():
56
windows_artifacts = service.collect_artifacts_windows()
57
return windows_artifacts
58
59
+
60
@bp.route("/velociraptor/artifacts/mac", methods=["GET"])
61
def get_artifacts_mac():
62
"""
63
Endpoint to list all available artifacts.
56
- It processes each artifact to verify the connection and returns the results where the name
64
+ It processes each artifact to verify the connection and returns the results where the name
65
begins with `MacOS`.
66
67
Returns:
@@ -63,6 +71,7 @@ def get_artifacts_mac():
71
mac_artifacts = service.collect_artifacts_macos()
72
return mac_artifacts
73
74
+
75
@bp.route("/velociraptor/artifacts/collection", methods=["POST"])
76
def collect_artifact():
77
"""
@@ -76,10 +85,23 @@ def collect_artifact():
85
artifact_name = req_data["artifact_name"]
86
client_name = req_data["client_name"]
87
service = UniversalService()
79
- client_id = service.get_client_id(client_name=client_name)["results"][0]["client_id"]
88
+ client_id = service.get_client_id(client_name=client_name)["results"][0][
89
+ "client_id"
90
+ ]
91
if client_id is None:
81
- return jsonify({"message": f"{client_name} has not been seen in the last 30 seconds and may not be online with the Velociraptor server.", "success": False}), 500
92
+ return (
93
+ jsonify(
94
+ {
95
+ "message": f"{client_name} has not been seen in the last 30 seconds and may not be online with the Velociraptor server.",
96
+ "success": False,
97
+ },
98
+ ),
99
+ 500,
100
+ )
101
102
artifact_service = ArtifactsService()
84
- artifact_results = artifact_service.run_artifact_collection(client_id=client_id, artifact=artifact_name)
103
+ artifact_results = artifact_service.run_artifact_collection(
104
+ client_id=client_id,
105
+ artifact=artifact_name,
106
+ )
107
return artifact_results
backend/app/routes/wazuhindexer.py
+11
-4
@@ -1,11 +1,15 @@
1
-from flask import Blueprint, jsonify, request
1
+from flask import Blueprint
2
+from flask import jsonify
3
+from flask import request
4
from loguru import logger
3
-from app.models.connectors import Connector, WazuhManagerConnector
5
5
-from app.services.agents.agents import AgentService, AgentSyncService
6
+from app.models.connectors import Connector
7
+from app.models.connectors import WazuhManagerConnector
8
+from app.services.agents.agents import AgentService
9
+from app.services.agents.agents import AgentSyncService
10
from app.services.WazuhIndexer.alerts import AlertsService
7
-from app.services.WazuhIndexer.index import IndexService
11
from app.services.WazuhIndexer.cluster import ClusterService
12
+from app.services.WazuhIndexer.index import IndexService
13
14
bp = Blueprint("wazuh_indexer", __name__)
15
@@ -30,6 +34,7 @@ def get_indices_summary():
34
indices = service.collect_indices_summary()
35
return indices
36
37
+
38
@bp.route("/wazuh_indexer/allocation", methods=["GET"])
39
def get_node_allocation():
40
"""
@@ -50,6 +55,7 @@ def get_node_allocation():
55
indices = service.collect_node_allocation()
56
return indices
57
58
+
59
@bp.route("/wazuh_indexer/health", methods=["GET"])
60
def get_cluster_health():
61
"""
@@ -62,6 +68,7 @@ def get_cluster_health():
68
indices = service.collect_cluster_health()
69
return indices
70
71
+
72
@bp.route("/wazuh_indexer/shards", methods=["GET"])
73
def get_shards():
74
"""
backend/app/services/DFIR_IRIS/alerts.py
+22
-10
@@ -1,10 +1,12 @@
1
from typing import Dict
2
+
3
import requests
3
-from loguru import logger
4
-from app.services.DFIR_IRIS.universal import UniversalService
4
+from dfir_iris_client.alert import Alert
5
from dfir_iris_client.helper.utils import assert_api_resp
6
from dfir_iris_client.helper.utils import get_data_from_resp
7
-from dfir_iris_client.alert import Alert
7
+from loguru import logger
8
+
9
+from app.services.DFIR_IRIS.universal import UniversalService
10
11
12
class AlertsService:
@@ -15,12 +17,12 @@ class AlertsService:
17
def __init__(self):
18
self.universal_service = UniversalService("DFIR-IRIS")
19
session_result = self.universal_service.create_session()
18
-
19
- if not session_result['success']:
20
- logger.error(session_result['message'])
20
+
21
+ if not session_result["success"]:
22
+ logger.error(session_result["message"])
23
self.iris_session = None
24
else:
23
- self.iris_session = session_result['session']
25
+ self.iris_session = session_result["session"]
26
27
def list_alerts(self) -> Dict[str, object]:
28
"""
@@ -37,9 +39,19 @@ class AlertsService:
39
40
logger.info("Collecting cases from DFIR-IRIS")
41
alert = Alert(session=self.iris_session)
40
- result = self.universal_service.fetch_and_parse_data(self.iris_session, alert.filter_alerts)
42
+ result = self.universal_service.fetch_and_parse_data(
43
+ self.iris_session,
44
+ alert.filter_alerts,
45
+ )
46
47
if not result["success"]:
43
- return {"success": False, "message": "Failed to collect cases from DFIR-IRIS"}
48
+ return {
49
+ "success": False,
50
+ "message": "Failed to collect cases from DFIR-IRIS",
51
+ }
52
45
- return {"success": True, "message": "Successfully collected cases from DFIR-IRIS", "results": result["data"]}
53
+ return {
54
+ "success": True,
55
+ "message": "Successfully collected cases from DFIR-IRIS",
56
+ "results": result["data"],
57
+ }
backend/app/services/DFIR_IRIS/assets.py
+22
-11
@@ -1,11 +1,13 @@
1
from typing import Dict
2
+
3
import requests
3
-from loguru import logger
4
-from app.services.DFIR_IRIS.universal import UniversalService
4
from dfir_iris_client.case import Case
5
from dfir_iris_client.helper.utils import assert_api_resp
6
from dfir_iris_client.helper.utils import get_data_from_resp
7
from dfir_iris_client.session import ClientSession
8
+from loguru import logger
9
+
10
+from app.services.DFIR_IRIS.universal import UniversalService
11
12
13
class AssetsService:
@@ -16,12 +18,12 @@ class AssetsService:
18
def __init__(self):
19
self.universal_service = UniversalService("DFIR-IRIS")
20
session_result = self.universal_service.create_session()
19
-
20
- if not session_result['success']:
21
- logger.error(session_result['message'])
21
+
22
+ if not session_result["success"]:
23
+ logger.error(session_result["message"])
24
self.iris_session = None
25
else:
24
- self.iris_session = session_result['session']
26
+ self.iris_session = session_result["session"]
27
28
def get_case_assets(self, cid: int) -> Dict[str, object]:
29
"""
@@ -34,14 +36,23 @@ class AssetsService:
36
dict: A dictionary containing the success status, a message and potentially the notes of a given case.
37
"""
38
if self.iris_session is None:
37
- return {"success": False, "message": "DFIR-IRIS session was not successfully created."}
39
+ return {
40
+ "success": False,
41
+ "message": "DFIR-IRIS session was not successfully created.",
42
+ }
43
44
logger.info(f"Collecting case {cid} assets from DFIR-IRIS")
45
case = Case(session=self.iris_session)
41
- result = self.universal_service.fetch_and_parse_data(self.iris_session, case.list_assets, cid)
46
+ result = self.universal_service.fetch_and_parse_data(
47
+ self.iris_session,
48
+ case.list_assets,
49
+ cid,
50
+ )
51
52
if not result["success"]:
44
- return {"success": False, "message": "Failed to collect notes from DFIR-IRIS"}
45
-
53
+ return {
54
+ "success": False,
55
+ "message": "Failed to collect notes from DFIR-IRIS",
56
+ }
57
+
58
return result
47
-
backend/app/services/DFIR_IRIS/cases.py
+39
-19
@@ -1,11 +1,13 @@
1
from typing import Dict
2
+
3
import requests
3
-from loguru import logger
4
-from app.services.DFIR_IRIS.universal import UniversalService
4
from dfir_iris_client.case import Case
5
from dfir_iris_client.helper.utils import assert_api_resp
6
from dfir_iris_client.helper.utils import get_data_from_resp
7
from dfir_iris_client.session import ClientSession
8
+from loguru import logger
9
+
10
+from app.services.DFIR_IRIS.universal import UniversalService
11
12
13
class CasesService:
@@ -16,12 +18,12 @@ class CasesService:
18
def __init__(self):
19
self.universal_service = UniversalService("DFIR-IRIS")
20
session_result = self.universal_service.create_session()
19
-
20
- if not session_result['success']:
21
- logger.error(session_result['message'])
21
+
22
+ if not session_result["success"]:
23
+ logger.error(session_result["message"])
24
self.iris_session = None
25
else:
24
- self.iris_session = session_result['session']
26
+ self.iris_session = session_result["session"]
27
28
def list_cases(self) -> Dict[str, object]:
29
"""
@@ -38,12 +40,22 @@ class CasesService:
40
41
logger.info("Collecting cases from DFIR-IRIS")
42
case = Case(session=self.iris_session)
41
- result = self.universal_service.fetch_and_parse_data(self.iris_session, case.list_cases)
43
+ result = self.universal_service.fetch_and_parse_data(
44
+ self.iris_session,
45
+ case.list_cases,
46
+ )
47
48
if not result["success"]:
44
- return {"success": False, "message": "Failed to collect cases from DFIR-IRIS"}
49
+ return {
50
+ "success": False,
51
+ "message": "Failed to collect cases from DFIR-IRIS",
52
+ }
53
46
- return {"success": True, "message": "Successfully collected cases from DFIR-IRIS", "cases": result["data"]}
54
+ return {
55
+ "success": True,
56
+ "message": "Successfully collected cases from DFIR-IRIS",
57
+ "cases": result["data"],
58
+ }
59
60
def get_case(self, case_id: int) -> bool:
61
"""
@@ -53,16 +65,30 @@ class CasesService:
65
dict: A dictionary containing the success status, a message and potentially the case.
66
"""
67
if self.iris_session is None:
56
- return {"success": False, "message": "DFIR-IRIS session was not successfully created."}
68
+ return {
69
+ "success": False,
70
+ "message": "DFIR-IRIS session was not successfully created.",
71
+ }
72
73
logger.info(f"Collecting case {case_id} from DFIR-IRIS")
74
case = Case(session=self.iris_session)
60
- result = self.universal_service.fetch_and_parse_data(self.iris_session, case.get_case, case_id)
75
+ result = self.universal_service.fetch_and_parse_data(
76
+ self.iris_session,
77
+ case.get_case,
78
+ case_id,
79
+ )
80
81
if not result["success"]:
63
- return {"success": False, "message": f"Failed to collect case {case_id} from DFIR-IRIS"}
82
+ return {
83
+ "success": False,
84
+ "message": f"Failed to collect case {case_id} from DFIR-IRIS",
85
+ }
86
65
- return {"success": True, "message": f"Successfully collected case {case_id} from DFIR-IRIS", "case": result["data"]}
87
+ return {
88
+ "success": True,
89
+ "message": f"Successfully collected case {case_id} from DFIR-IRIS",
90
+ "case": result["data"],
91
+ }
92
93
def check_case_id(self, case_id: int) -> bool:
94
"""
@@ -72,9 +98,3 @@ class CasesService:
98
dict: A dictionary containing the success status, a message and potentially the case.
99
"""
100
return self.get_case(case_id)
75
-
76
-
77
-
78
-
79
-
80
-
backend/app/services/DFIR_IRIS/notes.py
+85
-29
@@ -1,11 +1,13 @@
1
from typing import Dict
2
+
3
import requests
3
-from loguru import logger
4
-from app.services.DFIR_IRIS.universal import UniversalService
4
from dfir_iris_client.case import Case
5
from dfir_iris_client.helper.utils import assert_api_resp
6
from dfir_iris_client.helper.utils import get_data_from_resp
7
from dfir_iris_client.session import ClientSession
8
+from loguru import logger
9
+
10
+from app.services.DFIR_IRIS.universal import UniversalService
11
12
13
class NotesService:
@@ -16,12 +18,12 @@ class NotesService:
18
def __init__(self):
19
self.universal_service = UniversalService("DFIR-IRIS")
20
session_result = self.universal_service.create_session()
19
-
20
- if not session_result['success']:
21
- logger.error(session_result['message'])
21
+
22
+ if not session_result["success"]:
23
+ logger.error(session_result["message"])
24
self.iris_session = None
25
else:
24
- self.iris_session = session_result['session']
26
+ self.iris_session = session_result["session"]
27
28
def get_case_notes(self, search_term: str, cid: int) -> Dict[str, object]:
29
"""
@@ -35,24 +37,38 @@ class NotesService:
37
dict: A dictionary containing the success status, a message and potentially the notes of a given case.
38
"""
39
if self.iris_session is None:
38
- return {"success": False, "message": "DFIR-IRIS session was not successfully created."}
40
+ return {
41
+ "success": False,
42
+ "message": "DFIR-IRIS session was not successfully created.",
43
+ }
44
45
logger.info(f"Collecting case {cid} from DFIR-IRIS")
46
case = Case(session=self.iris_session)
42
- result = self.universal_service.fetch_and_parse_data(self.iris_session, case.search_notes, search_term, cid)
47
+ result = self.universal_service.fetch_and_parse_data(
48
+ self.iris_session,
49
+ case.search_notes,
50
+ search_term,
51
+ cid,
52
+ )
53
54
if not result["success"]:
45
- return {"success": False, "message": "Failed to collect notes from DFIR-IRIS"}
46
-
55
+ return {
56
+ "success": False,
57
+ "message": "Failed to collect notes from DFIR-IRIS",
58
+ }
59
+
60
# Loop through the notes and get the details
48
- for note in result['data']:
49
- note_details = self._get_case_note_details(note['note_id'], cid)
50
- if not note_details['success']:
51
- return {"success": False, "message": "Failed to collect notes from DFIR-IRIS"}
52
- note['note_details'] = note_details['notes']
61
+ for note in result["data"]:
62
+ note_details = self._get_case_note_details(note["note_id"], cid)
63
+ if not note_details["success"]:
64
+ return {
65
+ "success": False,
66
+ "message": "Failed to collect notes from DFIR-IRIS",
67
+ }
68
+ note["note_details"] = note_details["notes"]
69
70
return result
55
-
71
+
72
def _get_case_note_details(self, note_id: int, cid: int) -> Dict[str, object]:
73
"""
74
Gets a case's notes from DFIR-IRIS and returns the note details such as the content
@@ -65,18 +81,38 @@ class NotesService:
81
dict: A dictionary containing the success status, a message and potentially the notes of a given case.
82
"""
83
if self.iris_session is None:
68
- return {"success": False, "message": "DFIR-IRIS session was not successfully created."}
84
+ return {
85
+ "success": False,
86
+ "message": "DFIR-IRIS session was not successfully created.",
87
+ }
88
89
logger.info(f"Collecting case {cid} from DFIR-IRIS")
90
case = Case(session=self.iris_session)
72
- result = self.universal_service.fetch_and_parse_data(self.iris_session, case.get_note, note_id, cid)
91
+ result = self.universal_service.fetch_and_parse_data(
92
+ self.iris_session,
93
+ case.get_note,
94
+ note_id,
95
+ cid,
96
+ )
97
98
if not result["success"]:
75
- return {"success": False, "message": "Failed to collect notes from DFIR-IRIS"}
76
-
77
- return {"success": True, "message": "Successfully collected notes from DFIR-IRIS", "notes": result["data"]}
78
-
79
- def create_case_note(self, cid: int, note_title: str, note_content: str) -> Dict[str, object]:
99
+ return {
100
+ "success": False,
101
+ "message": "Failed to collect notes from DFIR-IRIS",
102
+ }
103
+
104
+ return {
105
+ "success": True,
106
+ "message": "Successfully collected notes from DFIR-IRIS",
107
+ "notes": result["data"],
108
+ }
109
+
110
+ def create_case_note(
111
+ self,
112
+ cid: int,
113
+ note_title: str,
114
+ note_content: str,
115
+ ) -> Dict[str, object]:
116
"""
117
Creates a case note in DFIR-IRIS
118
@@ -89,20 +125,40 @@ class NotesService:
125
dict: A dictionary containing the success status, a message and potentially the notes of a given case.
126
"""
127
if self.iris_session is None:
92
- return {"success": False, "message": "DFIR-IRIS session was not successfully created."}
128
+ return {
129
+ "success": False,
130
+ "message": "DFIR-IRIS session was not successfully created.",
131
+ }
132
133
logger.info(f"Creating case {cid} note in DFIR-IRIS")
134
case = Case(session=self.iris_session)
135
# Creating Group for New Note
97
- note_group = self.universal_service.fetch_and_parse_data(self.iris_session, case.add_notes_group, note_title, cid)
98
-
136
+ note_group = self.universal_service.fetch_and_parse_data(
137
+ self.iris_session,
138
+ case.add_notes_group,
139
+ note_title,
140
+ cid,
141
+ )
142
+
143
if not note_group["success"]:
144
return {"success": False, "message": "Failed to create note in DFIR-IRIS"}
101
- note_group_id = note_group['data']['group_id']
145
+ note_group_id = note_group["data"]["group_id"]
146
custom_attributes = {}
103
- result = self.universal_service.fetch_and_parse_data(self.iris_session, case.add_note, note_title, note_content, note_group_id, custom_attributes, cid)
147
+ result = self.universal_service.fetch_and_parse_data(
148
+ self.iris_session,
149
+ case.add_note,
150
+ note_title,
151
+ note_content,
152
+ note_group_id,
153
+ custom_attributes,
154
+ cid,
155
+ )
156
157
if not result["success"]:
158
return {"success": False, "message": "Failed to create note in DFIR-IRIS"}
159
108
- return {"success": True, "message": "Successfully created note in DFIR-IRIS", "notes": result["data"]}
160
+ return {
161
+ "success": True,
162
+ "message": "Successfully created note in DFIR-IRIS",
163
+ "notes": result["data"],
164
+ }
backend/app/services/DFIR_IRIS/universal.py
+19
-16
@@ -1,25 +1,26 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
from datetime import datetime
9
-import requests
10
-from loguru import logger
11
-from elasticsearch7 import Elasticsearch
12
-from app.models.connectors import connector_factory, Connector
13
-import dfir_iris_client
14
-from dfir_iris_client.session import ClientSession
2
from typing import Any
3
from typing import Dict
4
+from typing import List
5
from typing import Optional
6
from typing import Set
7
from typing import Tuple
8
+
9
+import dfir_iris_client
10
+import requests
11
from dfir_iris_client.case import Case
12
from dfir_iris_client.helper.utils import assert_api_resp
13
from dfir_iris_client.helper.utils import get_data_from_resp
14
+from dfir_iris_client.session import ClientSession
15
+from elasticsearch7 import Elasticsearch
16
+from loguru import logger
17
+
18
+from app import db
19
+from app.models.agents import AgentMetadata
20
+from app.models.agents import agent_metadata_schema
21
+from app.models.agents import agent_metadatas_schema
22
+from app.models.connectors import Connector
23
+from app.models.connectors import connector_factory
24
25
26
class UniversalService:
@@ -28,7 +29,9 @@ class UniversalService:
29
"""
30
31
def __init__(self, connector_name: str) -> None:
31
- self.connector_url, self.connector_api_key = self.collect_iris_details(connector_name)
32
+ self.connector_url, self.connector_api_key = self.collect_iris_details(
33
+ connector_name,
34
+ )
35
36
def collect_iris_details(self, connector_name: str):
37
"""
@@ -50,7 +53,7 @@ class UniversalService:
53
)
54
else:
55
return None, None
53
-
56
+
57
def create_session(self) -> Optional[ClientSession]:
58
"""
59
Create a session with DFIR-IRIS.
@@ -79,7 +82,7 @@ class UniversalService:
82
"success": False,
83
"message": "Connection to DFIR-IRIS unsuccessful.",
84
}
82
-
85
+
86
def fetch_and_parse_data(self, session, action, *args):
87
"""
88
General method to fetch and parse data from DFIR-IRIS.
backend/app/services/Graylog/index.py
+11
-8
@@ -1,14 +1,17 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
from datetime import datetime
2
+from typing import Dict
3
+from typing import List
4
+
5
import requests
6
from loguru import logger
11
-from app.models.connectors import connector_factory, Connector, GraylogConnector
7
+
8
+from app import db
9
+from app.models.agents import AgentMetadata
10
+from app.models.agents import agent_metadata_schema
11
+from app.models.agents import agent_metadatas_schema
12
+from app.models.connectors import Connector
13
+from app.models.connectors import GraylogConnector
14
+from app.models.connectors import connector_factory
15
from app.services.Graylog.universal import UniversalService
16
17
backend/app/services/Graylog/inputs.py
+11
-8
@@ -1,14 +1,17 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
from datetime import datetime
2
+from typing import Dict
3
+from typing import List
4
+
5
import requests
6
from loguru import logger
11
-from app.models.connectors import connector_factory, Connector, GraylogConnector
7
+
8
+from app import db
9
+from app.models.agents import AgentMetadata
10
+from app.models.agents import agent_metadata_schema
11
+from app.models.agents import agent_metadatas_schema
12
+from app.models.connectors import Connector
13
+from app.models.connectors import GraylogConnector
14
+from app.models.connectors import connector_factory
15
from app.services.Graylog.universal import UniversalService
16
17
backend/app/services/Graylog/messages.py
+11
-9
@@ -1,13 +1,15 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from app import db
1
from datetime import datetime
2
+
3
import requests
4
from loguru import logger
10
-from app.models.connectors import connector_factory, Connector, GraylogConnector
5
+
6
+from app import db
7
+from app.models.agents import AgentMetadata
8
+from app.models.agents import agent_metadata_schema
9
+from app.models.agents import agent_metadatas_schema
10
+from app.models.connectors import Connector
11
+from app.models.connectors import GraylogConnector
12
+from app.models.connectors import connector_factory
13
from app.services.Graylog.universal import UniversalService
14
15
@@ -46,7 +48,7 @@ class MessagesService:
48
# If the response is successful, return the messages as a list
49
if graylog_messages.status_code == 200:
50
logger.info(
49
- f"Received {len(graylog_messages.json()['messages'])} messages from Graylog"
51
+ f"Received {len(graylog_messages.json()['messages'])} messages from Graylog",
52
)
53
return {
54
"message": "Successfully retrieved messages",
@@ -56,7 +58,7 @@ class MessagesService:
58
# Otherwise, return an error message
59
else:
60
logger.error(
59
- f"Failed to collect messages from Graylog: {graylog_messages.json()}"
61
+ f"Failed to collect messages from Graylog: {graylog_messages.json()}",
62
)
63
return {
64
"message": "Failed to collect messages from Graylog",
backend/app/services/Graylog/metrics.py
+35
-17
@@ -1,14 +1,17 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
from datetime import datetime
2
+from typing import Dict
3
+from typing import List
4
+
5
import requests
6
from loguru import logger
11
-from app.models.connectors import connector_factory, Connector, GraylogConnector
7
+
8
+from app import db
9
+from app.models.agents import AgentMetadata
10
+from app.models.agents import agent_metadata_schema
11
+from app.models.agents import agent_metadatas_schema
12
+from app.models.connectors import Connector
13
+from app.models.connectors import GraylogConnector
14
+from app.models.connectors import connector_factory
15
from app.services.Graylog.universal import UniversalService
16
17
@@ -49,7 +52,9 @@ class MetricsService:
52
return {"message": "Failed to collect Graylog details", "success": False}
53
else:
54
journal_size = self._collect_metrics_uncommitted_journal_size(
52
- connector_url, connector_username, connector_password
55
+ connector_url,
56
+ connector_username,
57
+ connector_password,
58
)
59
60
if journal_size["success"] is False:
@@ -83,7 +88,9 @@ class MetricsService:
88
return {"message": "Failed to collect Graylog details", "success": False}
89
else:
90
throughput_usage = self._collect_metrics_throughput_usage(
86
- connector_url, connector_username, connector_password
91
+ connector_url,
92
+ connector_username,
93
+ connector_password,
94
)
95
96
if throughput_usage["success"] is False:
@@ -91,7 +98,10 @@ class MetricsService:
98
return throughput_usage
99
100
def _collect_metrics_uncommitted_journal_size(
94
- self, connector_url: str, connector_username: str, connector_password: str
101
+ self,
102
+ connector_url: str,
103
+ connector_username: str,
104
+ connector_password: str,
105
):
106
"""
107
Collects the journal size of uncommitted messages from Graylog.
@@ -117,13 +127,14 @@ class MetricsService:
127
uncommitted_journal_size = uncommitted_journal_size_response.json()
128
129
logger.info(
120
- f"Received {uncommitted_journal_size} uncommitted journal entries from Graylog"
130
+ f"Received {uncommitted_journal_size} uncommitted journal entries from Graylog",
131
)
132
return {
133
"message": "Successfully retrieved journal size",
134
"success": True,
135
"uncommitted_journal_entries": uncommitted_journal_size.get(
126
- "uncommitted_journal_entries", 0
136
+ "uncommitted_journal_entries",
137
+ 0,
138
),
139
}
140
except Exception as e:
@@ -134,7 +145,10 @@ class MetricsService:
145
}
146
147
def _collect_metrics_throughput_usage(
137
- self, connector_url: str, connector_username: str, connector_password: str
148
+ self,
149
+ connector_url: str,
150
+ connector_username: str,
151
+ connector_password: str,
152
) -> Dict[str, object]:
153
"""
154
Collects throughput usage from Graylog.
@@ -151,7 +165,10 @@ class MetricsService:
165
166
try:
167
throughput_metrics = self._make_throughput_api_call(
154
- connector_url, self.HEADERS, connector_username, connector_password
168
+ connector_url,
169
+ self.HEADERS,
170
+ connector_username,
171
+ connector_password,
172
)
173
return self._parse_throughput_metrics(throughput_metrics)
174
except Exception as e:
@@ -194,7 +211,8 @@ class MetricsService:
211
return throughput_metrics
212
213
def _parse_throughput_metrics(
197
- self, throughput_metrics: Dict[str, object]
214
+ self,
215
+ throughput_metrics: Dict[str, object],
216
) -> Dict[str, object]:
217
"""
218
Parses throughput metrics.
@@ -218,7 +236,7 @@ class MetricsService:
236
results[variable_name] = value
237
238
logger.info(
221
- f"Received throughput usage from Graylog: {throughput_metrics_list}"
239
+ f"Received throughput usage from Graylog: {throughput_metrics_list}",
240
)
241
return {
242
"message": "Successfully retrieved throughput usage",
backend/app/services/Graylog/universal.py
+9
-7
@@ -1,13 +1,15 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from app import db
1
from datetime import datetime
2
+
3
import requests
4
from loguru import logger
10
-from app.models.connectors import connector_factory, Connector, GraylogConnector
5
+
6
+from app import db
7
+from app.models.agents import AgentMetadata
8
+from app.models.agents import agent_metadata_schema
9
+from app.models.agents import agent_metadatas_schema
10
+from app.models.connectors import Connector
11
+from app.models.connectors import GraylogConnector
12
+from app.models.connectors import connector_factory
13
14
15
class UniversalService:
backend/app/services/Shuffle/universal.py
+11
-9
@@ -1,15 +1,17 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
from datetime import datetime
2
+from typing import Dict
3
+from typing import List
4
+
5
import requests
10
-from loguru import logger
6
from elasticsearch7 import Elasticsearch
12
-from app.models.connectors import connector_factory, Connector
7
+from loguru import logger
8
+
9
+from app import db
10
+from app.models.agents import AgentMetadata
11
+from app.models.agents import agent_metadata_schema
12
+from app.models.agents import agent_metadatas_schema
13
+from app.models.connectors import Connector
14
+from app.models.connectors import connector_factory
15
16
17
class UniversalService:
backend/app/services/Shuffle/workflows.py
+21
-11
@@ -1,6 +1,8 @@
1
from typing import Dict
2
+
3
import requests
4
from loguru import logger
5
+
6
from app.services.Shuffle.universal import UniversalService
7
8
@@ -12,10 +14,15 @@ class WorkflowsService:
14
def __init__(self):
15
self._collect_shuffle_details()
16
self.session = requests.Session()
15
- self.session.headers.update({"Authorization" : f"Bearer {self.connector_api_key}"})
17
+ self.session.headers.update(
18
+ {"Authorization": f"Bearer {self.connector_api_key}"},
19
+ )
20
21
def _collect_shuffle_details(self):
18
- self.connector_url, self.connector_api_key = UniversalService().collect_shuffle_details("Shuffle")
22
+ (
23
+ self.connector_url,
24
+ self.connector_api_key,
25
+ ) = UniversalService().collect_shuffle_details("Shuffle")
26
27
def _are_details_collected(self) -> bool:
28
return all([self.connector_url, self.connector_api_key])
@@ -74,7 +81,7 @@ class WorkflowsService:
81
"success": True,
82
"workflows": response.json(),
83
}
77
-
84
+
85
def collect_workflow_details(self) -> Dict[str, object]:
86
"""
87
Collects the workflow ID and workflow name from Shuffle.
@@ -97,7 +104,7 @@ class WorkflowsService:
104
"success": True,
105
"workflows": workflows["workflows"],
106
}
100
-
107
+
108
def _collect_workflow_details(self) -> Dict[str, object]:
109
"""
110
Collects the workflow ID and workflow name from Shuffle.
@@ -114,10 +121,9 @@ class WorkflowsService:
121
workflows = response.json()
122
workflow_details = []
123
for workflow in workflows:
117
- workflow_details.append({
118
- "workflow_id": workflow["id"],
119
- "workflow_name": workflow["name"]
120
- })
124
+ workflow_details.append(
125
+ {"workflow_id": workflow["id"], "workflow_name": workflow["name"]},
126
+ )
127
128
return {
129
"message": "Successfully collected workflow details from Shuffle",
@@ -148,7 +154,10 @@ class WorkflowsService:
154
"executions": executions["executions"],
155
}
156
151
- def _collect_workflow_executions_status(self, workflow_id: str) -> Dict[str, object]:
157
+ def _collect_workflow_executions_status(
158
+ self,
159
+ workflow_id: str,
160
+ ) -> Dict[str, object]:
161
"""
162
Collects the execution status of a Shuffle Workflow by its ID.
163
@@ -156,7 +165,9 @@ class WorkflowsService:
165
dict: A dictionary containing the success status, a message and potentially the workflow execution status.
166
"""
167
try:
159
- response = self._send_request(f"{self.connector_url}/api/v1/workflows/{workflow_id}/executions")
168
+ response = self._send_request(
169
+ f"{self.connector_url}/api/v1/workflows/{workflow_id}/executions",
170
+ )
171
response.raise_for_status()
172
except requests.exceptions.HTTPError as err:
173
return self._handle_request_error(err)
@@ -174,5 +185,4 @@ class WorkflowsService:
185
"message": "Successfully collected workflow executions from Shuffle",
186
"success": True,
187
"executions": status,
177
-
188
}
backend/app/services/Velociraptor/artifacts.py
+8
-4
@@ -1,9 +1,11 @@
1
+import json
2
from typing import Dict
3
+
4
from loguru import logger
5
from pyvelociraptor import api_pb2
6
from werkzeug.utils import secure_filename
7
+
8
from app.services.Velociraptor.universal import UniversalService
6
-import json
9
10
11
class ArtifactsService:
@@ -80,7 +82,7 @@ class ArtifactsService:
82
83
def collect_artifacts_windows(self):
84
return self.collect_artifacts_prefixed("Windows.")
83
-
85
+
86
def collect_artifacts_macos(self):
87
return self.collect_artifacts_prefixed("MacOS.")
88
@@ -97,7 +99,7 @@ class ArtifactsService:
99
"""
100
try:
101
query = self._create_query(
100
- f"SELECT collect_client(client_id='{client_id}', artifacts=['{artifact}']) FROM scope()"
102
+ f"SELECT collect_client(client_id='{client_id}', artifacts=['{artifact}']) FROM scope()",
103
)
104
flow = self.universal_service.execute_query(query)
105
logger.info(f"Successfully ran artifact collection on {flow}")
@@ -110,7 +112,9 @@ class ArtifactsService:
112
logger.info(f"Successfully watched flow completion on {completed}")
113
114
results = self.universal_service.read_collection_results(
113
- client_id, flow_id, artifact
115
+ client_id,
116
+ flow_id,
117
+ artifact,
118
)
119
return results
120
except Exception as err:
backend/app/services/Velociraptor/universal.py
+41
-27
@@ -1,20 +1,22 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
+import json
2
from datetime import datetime
3
+from typing import Dict
4
+from typing import List
5
+
6
+import grpc
7
+import pyvelociraptor
8
import requests
10
-from loguru import logger
9
from elasticsearch7 import Elasticsearch
12
-from app.models.connectors import connector_factory, Connector
13
-import pyvelociraptor
10
+from loguru import logger
11
from pyvelociraptor import api_pb2
12
from pyvelociraptor import api_pb2_grpc
16
-import grpc
17
-import json
13
+
14
+from app import db
15
+from app.models.agents import AgentMetadata
16
+from app.models.agents import agent_metadata_schema
17
+from app.models.agents import agent_metadatas_schema
18
+from app.models.connectors import Connector
19
+from app.models.connectors import connector_factory
20
21
22
class UniversalService:
@@ -33,7 +35,9 @@ class UniversalService:
35
Args:
36
connector_name (str): The name of the Velociraptor connector.
37
"""
36
- self.connector_url, self.connector_api_key = self.collect_velociraptor_details(connector_name)
38
+ self.connector_url, self.connector_api_key = self.collect_velociraptor_details(
39
+ connector_name,
40
+ )
41
self.config = pyvelociraptor.LoadConfigFile(self.connector_api_key)
42
43
def collect_velociraptor_details(self, connector_name: str):
@@ -67,7 +71,11 @@ class UniversalService:
71
certificate_chain=self.config["client_cert"].encode("utf8"),
72
)
73
options = (("grpc.ssl_target_name_override", "VelociraptorServer"),)
70
- self.channel = grpc.secure_channel(self.config["api_connection_string"], creds, options)
74
+ self.channel = grpc.secure_channel(
75
+ self.config["api_connection_string"],
76
+ creds,
77
+ options,
78
+ )
79
self.stub = api_pb2_grpc.APIStub(self.channel)
80
81
def create_vql_request(self, vql: str):
@@ -117,7 +125,6 @@ class UniversalService:
125
"message": f"Failed to execute query: {e}",
126
}
127
120
-
128
def watch_flow_completion(self, flow_id: str):
129
"""
130
Watch for the completion of a flow.
@@ -131,7 +138,12 @@ class UniversalService:
138
vql = f"SELECT * FROM watch_monitoring(artifact='System.Flow.Completion') WHERE FlowId='{flow_id}' LIMIT 1"
139
return self.execute_query(vql)
140
134
- def read_collection_results(self, client_id: str, flow_id: str, artifact: str = 'Generic.Client.Info/BasicInformation'):
141
+ def read_collection_results(
142
+ self,
143
+ client_id: str,
144
+ flow_id: str,
145
+ artifact: str = "Generic.Client.Info/BasicInformation",
146
+ ):
147
"""
148
Read the results of a collection.
149
@@ -158,20 +170,22 @@ class UniversalService:
170
"""
171
# Formulate queries
172
try:
161
- vql_client_id = f"select client_id from clients(search='host:{client_name}')"
162
- vql_last_seen_at = f"select last_seen_at from clients(search='host:{client_name}')"
163
-
173
+ vql_client_id = (
174
+ f"select client_id from clients(search='host:{client_name}')"
175
+ )
176
+ vql_last_seen_at = (
177
+ f"select last_seen_at from clients(search='host:{client_name}')"
178
+ )
179
+
180
# Get the last seen timestamp
181
last_seen_at = self._get_last_seen_timestamp(vql_last_seen_at)
166
-
182
+
183
# if last_seen_at is longer than 30 seconds from now, return False
184
if self._is_offline(last_seen_at):
185
return {
186
"success": False,
187
"message": f"{client_name} has not been seen in the last 30 seconds and may not be online with the Velociraptor server.",
172
- "results": [
173
- {"client_id": None}
174
- ]
188
+ "results": [{"client_id": None}],
189
}
190
191
return self.execute_query(vql_client_id)
@@ -179,9 +193,7 @@ class UniversalService:
193
return {
194
"success": False,
195
"message": f"Failed to get Client ID for {client_name}: {e}",
182
- "results": [
183
- {"client_id": None}
184
- ]
196
+ "results": [{"client_id": None}],
197
}
198
199
def _get_last_seen_timestamp(self, vql: str):
@@ -206,4 +218,6 @@ class UniversalService:
218
Returns:
219
bool: True if the client is offline, False otherwise.
220
"""
209
- return (datetime.now() - datetime.fromtimestamp(last_seen_at / 1000000)).total_seconds() > 30
221
+ return (
222
+ datetime.now() - datetime.fromtimestamp(last_seen_at / 1000000)
223
+ ).total_seconds() > 30
backend/app/services/WazuhIndexer/alerts.py
+8
-6
@@ -1,9 +1,11 @@
1
+from typing import Dict
2
+from typing import List
3
+
4
from elasticsearch7 import Elasticsearch
5
from loguru import logger
3
-from typing import Dict, List
6
5
-from app.services.WazuhIndexer.universal import UniversalService
7
from app.services.WazuhIndexer.index import IndexService
8
+from app.services.WazuhIndexer.universal import UniversalService
9
10
11
class AlertsService:
@@ -47,7 +49,7 @@ class AlertsService:
49
Dict[str, object]: A dictionary containing success status and alerts or an error message.
50
"""
51
if not all(
50
- [self.connector_url, self.connector_username, self.connector_password]
52
+ [self.connector_url, self.connector_username, self.connector_password],
53
):
54
return {
55
"message": "Failed to collect Wazuh-Indexer details",
@@ -70,7 +72,7 @@ class AlertsService:
72
"index_name": index_name,
73
"total_alerts": len(alerts["alerts"]),
74
"last_10_alerts": alerts["alerts"],
73
- }
75
+ },
76
)
77
78
return {
@@ -113,8 +115,8 @@ class AlertsService:
115
"should": [
116
{"range": {"rule_level": {"gte": 12}}},
117
{"match": {"syslog_level": "ALERT"}},
116
- ]
117
- }
118
+ ],
119
+ },
120
},
121
"sort": [{"timestamp_utc": {"order": "desc"}}],
122
}
backend/app/services/WazuhIndexer/cluster.py
+15
-8
@@ -1,7 +1,9 @@
1
from typing import Dict
2
+
3
import requests
4
from elasticsearch7 import Elasticsearch
5
from loguru import logger
6
+
7
from app.services.WazuhIndexer.universal import UniversalService
8
9
@@ -15,7 +17,11 @@ class ClusterService:
17
self._initialize_es_client()
18
19
def _collect_wazuhindexer_details(self):
18
- self.connector_url, self.connector_username, self.connector_password = UniversalService().collect_wazuhindexer_details("Wazuh-Indexer")
20
+ (
21
+ self.connector_url,
22
+ self.connector_username,
23
+ self.connector_password,
24
+ ) = UniversalService().collect_wazuhindexer_details("Wazuh-Indexer")
25
26
def _initialize_es_client(self):
27
self.es = Elasticsearch(
@@ -28,7 +34,9 @@ class ClusterService:
34
)
35
36
def _are_details_collected(self) -> bool:
31
- return all([self.connector_url, self.connector_username, self.connector_password])
37
+ return all(
38
+ [self.connector_url, self.connector_username, self.connector_password],
39
+ )
40
41
def collect_node_allocation(self) -> Dict[str, object]:
42
"""
@@ -83,7 +91,7 @@ class ClusterService:
91
}
92
for node in node_allocation
93
]
86
-
94
+
95
def collect_cluster_health(self) -> Dict[str, object]:
96
"""
97
Collects the cluster health from the Wazuh-Indexer.
@@ -106,7 +114,7 @@ class ClusterService:
114
"success": True,
115
"cluster_health": index_summary["cluster_health"],
116
}
109
-
117
+
118
def _collect_cluster_health(self) -> Dict[str, object]:
119
"""
120
Collects the cluster health from the Wazuh-Indexer.
@@ -124,7 +132,7 @@ class ClusterService:
132
except Exception as e:
133
logger.error(f"Failed to collect cluster health: {e}")
134
return {"message": "Failed to collect cluster health", "success": False}
127
-
135
+
136
def collect_shards(self) -> Dict[str, object]:
137
"""
138
Collects the shards from the Wazuh-Indexer.
@@ -147,7 +155,7 @@ class ClusterService:
155
"success": True,
156
"shards": index_summary["shards"],
157
}
150
-
158
+
159
def _collect_shards(self) -> Dict[str, object]:
160
"""
161
Collects the shards from the Wazuh-Indexer.
@@ -166,7 +174,7 @@ class ClusterService:
174
except Exception as e:
175
logger.error(f"Failed to collect shards: {e}")
176
return {"message": "Failed to collect shards", "success": False}
169
-
177
+
178
def _format_shards(self, shards):
179
return [
180
{
@@ -178,4 +186,3 @@ class ClusterService:
186
}
187
for shard in shards
188
]
181
-
backend/app/services/WazuhIndexer/index.py
+20
-5
@@ -1,7 +1,9 @@
1
from typing import Dict
2
+
3
import requests
4
from elasticsearch7 import Elasticsearch
5
from loguru import logger
6
+
7
from app.services.WazuhIndexer.universal import UniversalService
8
9
@@ -15,7 +17,11 @@ class IndexService:
17
self._initialize_es_client()
18
19
def _collect_wazuhindexer_details(self):
18
- self.connector_url, self.connector_username, self.connector_password = UniversalService().collect_wazuhindexer_details("Wazuh-Indexer")
20
+ (
21
+ self.connector_url,
22
+ self.connector_username,
23
+ self.connector_password,
24
+ ) = UniversalService().collect_wazuhindexer_details("Wazuh-Indexer")
25
26
def _initialize_es_client(self):
27
self.es = Elasticsearch(
@@ -28,7 +34,9 @@ class IndexService:
34
)
35
36
def _are_details_collected(self) -> bool:
31
- return all([self.connector_url, self.connector_username, self.connector_password])
37
+ return all(
38
+ [self.connector_url, self.connector_username, self.connector_password],
39
+ )
40
41
def collect_indices_summary(self) -> Dict[str, object]:
42
"""
@@ -38,8 +46,11 @@ class IndexService:
46
dict: A dictionary containing the success status, a message, and potentially the indices.
47
"""
48
if not self._are_details_collected():
41
- return {"message": "Failed to collect Wazuh-Indexer details", "success": False}
42
-
49
+ return {
50
+ "message": "Failed to collect Wazuh-Indexer details",
51
+ "success": False,
52
+ }
53
+
54
index_summary = self._collect_indices()
55
if not index_summary["success"]:
56
return index_summary
@@ -73,7 +84,11 @@ class IndexService:
84
"""
85
try:
86
indices = self.es.cat.indices(format="json")
76
- return {"message": "Successfully collected indices", "success": True, "indices": indices}
87
+ return {
88
+ "message": "Successfully collected indices",
89
+ "success": True,
90
+ "indices": indices,
91
+ }
92
except Exception as e:
93
logger.error(e)
94
return {"message": "Failed to collect indices", "success": False}
backend/app/services/WazuhIndexer/universal.py
+11
-9
@@ -1,15 +1,17 @@
1
-from app.models.agents import (
2
- AgentMetadata,
3
- agent_metadata_schema,
4
- agent_metadatas_schema,
5
-)
6
-from typing import Dict, List
7
-from app import db
1
from datetime import datetime
2
+from typing import Dict
3
+from typing import List
4
+
5
import requests
10
-from loguru import logger
6
from elasticsearch7 import Elasticsearch
12
-from app.models.connectors import connector_factory, Connector
7
+from loguru import logger
8
+
9
+from app import db
10
+from app.models.agents import AgentMetadata
11
+from app.models.agents import agent_metadata_schema
12
+from app.models.agents import agent_metadatas_schema
13
+from app.models.connectors import Connector
14
+from app.models.connectors import connector_factory
15
16
17
class UniversalService:
backend/app/services/WazuhManager/agent.py
+23
-5
@@ -1,12 +1,19 @@
1
-from typing import Dict, Optional, List, Any
1
+from typing import Any
2
+from typing import Dict
3
+from typing import List
4
+from typing import Optional
5
+
6
+import requests
7
from loguru import logger
8
+
9
from app.services.WazuhManager.universal import UniversalService
4
-import requests
10
+
11
12
class WazuhHttpRequests:
13
"""
14
Class to handle HTTP requests to the Wazuh API.
15
"""
16
+
17
def __init__(self, connector_url: str, wazuh_auth_token: str) -> None:
18
"""
19
Args:
@@ -17,7 +24,11 @@ class WazuhHttpRequests:
24
self.wazuh_auth_token = wazuh_auth_token
25
self.headers = {"Authorization": f"Bearer {wazuh_auth_token}"}
26
20
- def delete_request(self, endpoint: str, params: Optional[Dict[str, str]] = None) -> Dict[str, bool]:
27
+ def delete_request(
28
+ self,
29
+ endpoint: str,
30
+ params: Optional[Dict[str, str]] = None,
31
+ ) -> Dict[str, bool]:
32
"""
33
Function to handle DELETE requests.
34
@@ -43,10 +54,12 @@ class WazuhHttpRequests:
54
logger.error(f"Failed to delete {endpoint}: {e}")
55
return {"agentDeleted": False}
56
57
+
58
class WazuhManagerAgentService:
59
"""
60
A service class that encapsulates the logic for handling agent related operations in Wazuh Manager.
61
"""
62
+
63
def __init__(self, universal_service: UniversalService) -> None:
64
"""
65
Args:
@@ -54,7 +67,10 @@ class WazuhManagerAgentService:
67
"""
68
self.universal_service = universal_service
69
self.auth_token = universal_service.get_auth_token()
57
- self.wazuh_http_requests = WazuhHttpRequests(self.universal_service.connector_url, self.auth_token)
70
+ self.wazuh_http_requests = WazuhHttpRequests(
71
+ self.universal_service.connector_url,
72
+ self.auth_token,
73
+ )
74
75
def collect_agents(self) -> Optional[List[Dict[str, str]]]:
76
"""
@@ -85,7 +101,9 @@ class WazuhManagerAgentService:
101
headers = {"Authorization": f"Bearer {self.auth_token}"}
102
limit = 1000
103
response = requests.get(
88
- f"{self.universal_service.connector_url}/agents?limit={limit}", headers=headers, verify=False
104
+ f"{self.universal_service.connector_url}/agents?limit={limit}",
105
+ headers=headers,
106
+ verify=False,
107
)
108
if response.status_code == 200:
109
return response.json()["data"]["affected_items"]
backend/app/services/WazuhManager/disabled_rule.py
+97
-28
@@ -1,18 +1,28 @@
1
-from typing import Dict, Optional, Union, List, Any, Tuple
2
-from loguru import logger
3
-from app.services.WazuhManager.universal import UniversalService
1
+import json
2
+import xml.etree.ElementTree as ET
3
+from typing import Any
4
+from typing import Dict
5
+from typing import List
6
+from typing import Optional
7
+from typing import Tuple
8
+from typing import Union
9
+
10
import requests
5
-from app.models.rules import DisabledRules
6
-from app.models.connectors import connector_factory, Connector
7
-from app import db
11
import xmltodict
9
-import xml.etree.ElementTree as ET
10
-import json
12
+from loguru import logger
13
+
14
+from app import db
15
+from app.models.connectors import Connector
16
+from app.models.connectors import connector_factory
17
+from app.models.rules import DisabledRules
18
+from app.services.WazuhManager.universal import UniversalService
19
+
20
21
class WazuhHttpRequests:
22
"""
23
Class to handle HTTP requests to the Wazuh API.
24
"""
25
+
26
def __init__(self, connector_url: str, wazuh_auth_token: str) -> None:
27
"""
28
Args:
@@ -23,7 +33,11 @@ class WazuhHttpRequests:
33
self.wazuh_auth_token = wazuh_auth_token
34
self.headers = {"Authorization": f"Bearer {wazuh_auth_token}"}
35
26
- def get_request(self, endpoint: str, params: Optional[Dict[str, str]] = None) -> Dict[str, Union[str, bool]]:
36
+ def get_request(
37
+ self,
38
+ endpoint: str,
39
+ params: Optional[Dict[str, str]] = None,
40
+ ) -> Dict[str, Union[str, bool]]:
41
"""
42
Function to handle GET requests.
43
@@ -46,9 +60,17 @@ class WazuhHttpRequests:
60
61
except Exception as e:
62
logger.error(f"GET request to {endpoint} failed: {e}")
49
- return {"message": f"GET request to {endpoint} failed: {e}", "success": False}
50
-
51
- def put_request(self, endpoint: str, data: str, params: Optional[Dict[str, str]] = None) -> Dict[str, bool]:
63
+ return {
64
+ "message": f"GET request to {endpoint} failed: {e}",
65
+ "success": False,
66
+ }
67
+
68
+ def put_request(
69
+ self,
70
+ endpoint: str,
71
+ data: str,
72
+ params: Optional[Dict[str, str]] = None,
73
+ ) -> Dict[str, bool]:
74
"""
75
Function to handle PUT requests.
76
@@ -83,6 +105,7 @@ class DisableRuleService:
105
"""
106
A service class that encapsulates the logic for handling rule disabling related operations in Wazuh Manager.
107
"""
108
+
109
def __init__(self, universal_service: UniversalService) -> None:
110
"""
111
Args:
@@ -90,20 +113,37 @@ class DisableRuleService:
113
"""
114
self.universal_service = universal_service
115
self.auth_token = universal_service.get_auth_token()
93
- self.wazuh_http_requests = WazuhHttpRequests(self.universal_service.connector_url, self.auth_token)
116
+ self.wazuh_http_requests = WazuhHttpRequests(
117
+ self.universal_service.connector_url,
118
+ self.auth_token,
119
+ )
120
95
- def disable_rule(self, request: Dict[str, Union[str, int]]) -> Dict[str, Union[str, bool]]:
121
+ def disable_rule(
122
+ self,
123
+ request: Dict[str, Union[str, int]],
124
+ ) -> Dict[str, Union[str, bool]]:
125
try:
126
self._validate_request(request)
127
rule_id = request["rule_id"]
128
filename = self._fetch_filename(rule_id)
129
file_content = self._fetch_file_content(filename)
101
- previous_level, updated_file_content = self._set_level_1(file_content, rule_id)
130
+ previous_level, updated_file_content = self._set_level_1(
131
+ file_content,
132
+ rule_id,
133
+ )
134
xml_content = self._convert_to_xml(updated_file_content)
103
- self._store_disabled_rule_info(rule_id, previous_level, request["reason"], request["length_of_time"])
135
+ self._store_disabled_rule_info(
136
+ rule_id,
137
+ previous_level,
138
+ request["reason"],
139
+ request["length_of_time"],
140
+ )
141
self._upload_updated_rule(filename, xml_content)
142
UniversalService().restart_service()
106
- return {"message": f"Rule {rule_id} successfully disabled in file {filename}.", "success": True}
143
+ return {
144
+ "message": f"Rule {rule_id} successfully disabled in file {filename}.",
145
+ "success": True,
146
+ }
147
148
except Exception as e:
149
logger.error(str(e))
@@ -118,21 +158,35 @@ class DisableRuleService:
158
if "length_of_time" not in request:
159
raise ValueError("Request missing length_of_time")
160
request["length_of_time"] = int(request["length_of_time"])
121
-
161
+
162
def _fetch_filename(self, rule_id: str) -> str:
123
- filename_data = self.wazuh_http_requests.get_request("rules", {"rule_ids": rule_id})
163
+ filename_data = self.wazuh_http_requests.get_request(
164
+ "rules",
165
+ {"rule_ids": rule_id},
166
+ )
167
if not filename_data["success"]:
168
raise ValueError(filename_data["message"])
169
return filename_data["data"]["data"]["affected_items"][0]["filename"]
170
128
- def _fetch_file_content(self, filename: str) -> Union[Dict[str, str], List[Dict[str, str]]]:
129
- file_content_data = self.wazuh_http_requests.get_request(f"rules/files/{filename}")
171
+ def _fetch_file_content(
172
+ self,
173
+ filename: str,
174
+ ) -> Union[Dict[str, str], List[Dict[str, str]]]:
175
+ file_content_data = self.wazuh_http_requests.get_request(
176
+ f"rules/files/{filename}",
177
+ )
178
if not file_content_data["success"]:
179
raise ValueError(file_content_data["message"])
180
return file_content_data["data"]["data"]["affected_items"][0]["group"]
181
134
- def _set_level_1(self, file_content: Union[Dict[str, str], List[Dict[str, str]]], rule_id: str) -> Tuple[str, Union[Dict[str, str], List[Dict[str, str]]]]:
135
- logger.info(f"Setting rule {rule_id} level to 1 for file_content: {file_content}")
182
+ def _set_level_1(
183
+ self,
184
+ file_content: Union[Dict[str, str], List[Dict[str, str]]],
185
+ rule_id: str,
186
+ ) -> Tuple[str, Union[Dict[str, str], List[Dict[str, str]]]]:
187
+ logger.info(
188
+ f"Setting rule {rule_id} level to 1 for file_content: {file_content}",
189
+ )
190
previous_level = None
191
if isinstance(file_content, dict):
192
file_content = [file_content]
@@ -149,19 +203,31 @@ class DisableRuleService:
203
break
204
return previous_level, file_content
205
152
- def _convert_to_xml(self, updated_file_content: Union[Dict[str, str], List[Dict[str, str]]]) -> str:
206
+ def _convert_to_xml(
207
+ self,
208
+ updated_file_content: Union[Dict[str, str], List[Dict[str, str]]],
209
+ ) -> str:
210
logger.info(f"Received updated_file_content: {updated_file_content}")
211
xml_content_list = []
212
for group in updated_file_content:
213
xml_dict = {"group": group}
214
xml_content = xmltodict.unparse(xml_dict, pretty=True)
158
- xml_content = xml_content.replace('<?xml version="1.0" encoding="utf-8"?>', "")
215
+ xml_content = xml_content.replace(
216
+ '<?xml version="1.0" encoding="utf-8"?>',
217
+ "",
218
+ )
219
xml_content_list.append(xml_content)
220
xml_content = "\n".join(xml_content_list)
221
xml_content = xml_content.strip()
222
return xml_content
223
164
- def _store_disabled_rule_info(self, rule_id: str, previous_level: str, reason: str, length_of_time: str):
224
+ def _store_disabled_rule_info(
225
+ self,
226
+ rule_id: str,
227
+ previous_level: str,
228
+ reason: str,
229
+ length_of_time: str,
230
+ ):
231
disabled_rule = DisabledRules(
232
rule_id=rule_id,
233
previous_level=previous_level,
@@ -173,7 +239,10 @@ class DisableRuleService:
239
db.session.commit()
240
241
def _upload_updated_rule(self, filename: str, xml_content: str):
176
- response = self.wazuh_http_requests.put_request(f"rules/files/{filename}", xml_content, {"overwrite": "true"})
242
+ response = self.wazuh_http_requests.put_request(
243
+ f"rules/files/{filename}",
244
+ xml_content,
245
+ {"overwrite": "true"},
246
+ )
247
if not response["success"]:
248
raise ValueError(response["message"])
179
-
backend/app/services/WazuhManager/enabled_rule.py
+65
-19
@@ -1,19 +1,30 @@
1
-from typing import Dict, Optional, Union, List, Any, Tuple
2
-from loguru import logger
3
-from app.services.WazuhManager.universal import UniversalService
1
+import json
2
+import xml.etree.ElementTree as ET
3
+
4
+# from typing import Tuple
5
+from typing import Any
6
+from typing import Dict
7
+from typing import List
8
+from typing import Optional
9
+from typing import Union
10
+
11
import requests
5
-from app.models.rules import DisabledRules
6
-from app.models.connectors import connector_factory, Connector
7
-from app import db
12
import xmltodict
9
-import xml.etree.ElementTree as ET
10
-import json
13
+from loguru import logger
14
+
15
+from app import db
16
+
17
+# from app.models.connectors import Connector
18
+# from app.models.connectors import connector_factory
19
+from app.models.rules import DisabledRules
20
+from app.services.WazuhManager.universal import UniversalService
21
22
23
class WazuhHttpRequests:
24
"""
25
Class to handle HTTP requests to the Wazuh API.
26
"""
27
+
28
def __init__(self, connector_url: str, wazuh_auth_token: str) -> None:
29
"""
30
Args:
@@ -24,7 +35,11 @@ class WazuhHttpRequests:
35
self.wazuh_auth_token = wazuh_auth_token
36
self.headers = {"Authorization": f"Bearer {wazuh_auth_token}"}
37
27
- def get_request(self, endpoint: str, params: Optional[Dict[str, str]] = None) -> Dict[str, Union[str, bool]]:
38
+ def get_request(
39
+ self,
40
+ endpoint: str,
41
+ params: Optional[Dict[str, str]] = None,
42
+ ) -> Dict[str, Union[str, bool]]:
43
"""
44
Function to handle GET requests.
45
@@ -49,9 +64,17 @@ class WazuhHttpRequests:
64
65
except Exception as e:
66
logger.error(f"GET request to {endpoint} failed: {e}")
52
- return {"message": f"GET request to {endpoint} failed: {e}", "success": False}
67
+ return {
68
+ "message": f"GET request to {endpoint} failed: {e}",
69
+ "success": False,
70
+ }
71
54
- def put_request(self, endpoint: str, data: str, params: Optional[Dict[str, str]] = None) -> Dict[str, bool]:
72
+ def put_request(
73
+ self,
74
+ endpoint: str,
75
+ data: str,
76
+ params: Optional[Dict[str, str]] = None,
77
+ ) -> Dict[str, bool]:
78
"""
79
Function to handle PUT requests.
80
@@ -86,6 +109,7 @@ class EnableRuleService:
109
"""
110
A service class that encapsulates the logic for handling rule enabling related operations in Wazuh Manager.
111
"""
112
+
113
def __init__(self, universal_service: UniversalService) -> None:
114
"""
115
Args:
@@ -93,7 +117,10 @@ class EnableRuleService:
117
"""
118
self.universal_service = universal_service
119
self.auth_token = universal_service.get_auth_token()
96
- self.wazuh_http_requests = WazuhHttpRequests(self.universal_service.connector_url, self.auth_token)
120
+ self.wazuh_http_requests = WazuhHttpRequests(
121
+ self.universal_service.connector_url,
122
+ self.auth_token,
123
+ )
124
125
def enable_rule(self, request: Dict[str, str]) -> Dict[str, Union[str, bool]]:
126
"""
@@ -112,7 +139,11 @@ class EnableRuleService:
139
logger.info(f"Getting file content of {filename}")
140
file_content = self._fetch_file_content(filename)
141
previous_level = self._get_previous_level(rule_id)
115
- updated_file_content = self._set_level_previous(file_content, rule_id, previous_level)
142
+ updated_file_content = self._set_level_previous(
143
+ file_content,
144
+ rule_id,
145
+ previous_level,
146
+ )
147
xml_content = self._json_to_xml(updated_file_content)
148
self._delete_rule_from_db(rule_id)
149
self._put_updated_rule(filename, xml_content)
@@ -155,7 +186,10 @@ class EnableRuleService:
186
Returns:
187
str: The filename of the rule to be enabled.
188
"""
158
- filename_data = self.wazuh_http_requests.get_request("rules", {"rule_ids": rule_id})
189
+ filename_data = self.wazuh_http_requests.get_request(
190
+ "rules",
191
+ {"rule_ids": rule_id},
192
+ )
193
if not filename_data["success"]:
194
raise ValueError(filename_data["message"])
195
return filename_data["data"]["data"]["affected_items"][0]["filename"]
@@ -173,7 +207,9 @@ class EnableRuleService:
207
Returns:
208
Any: The content of the rule file.
209
"""
176
- file_content_data = self.wazuh_http_requests.get_request(f"rules/files/{filename}")
210
+ file_content_data = self.wazuh_http_requests.get_request(
211
+ f"rules/files/{filename}",
212
+ )
213
if not file_content_data["success"]:
214
raise ValueError(file_content_data["message"])
215
return file_content_data["data"]["data"]["affected_items"][0]["group"]
@@ -196,7 +232,12 @@ class EnableRuleService:
232
raise ValueError(f"Rule {rule_id} is not disabled.")
233
return disabled_rule.previous_level
234
199
- def _set_level_previous(self, file_content: Any, rule_id: str, previous_level: str) -> Any:
235
+ def _set_level_previous(
236
+ self,
237
+ file_content: Any,
238
+ rule_id: str,
239
+ previous_level: str,
240
+ ) -> Any:
241
"""
242
Set the level of the rule to be enabled to the previous level.
243
@@ -209,7 +250,7 @@ class EnableRuleService:
250
Any: The content of the rule with the level set to the previous level.
251
"""
252
logger.info(
212
- f"Setting rule {rule_id} level to {previous_level} for file_content: {file_content}"
253
+ f"Setting rule {rule_id} level to {previous_level} for file_content: {file_content}",
254
)
255
# If 'file_content' is a dictionary (representing a single group), make it a list of one group
256
if isinstance(file_content, dict):
@@ -252,7 +293,8 @@ class EnableRuleService:
293
# Remove the `<?xml version="1.0" encoding="utf-8"?>` from the
294
# beginning of the XML string.
295
xml_content = xml_content.replace(
255
- '<?xml version="1.0" encoding="utf-8"?>', ""
296
+ '<?xml version="1.0" encoding="utf-8"?>',
297
+ "",
298
)
299
xml_content_list.append(xml_content)
300
@@ -285,6 +327,10 @@ class EnableRuleService:
327
Raises:
328
RuntimeError: If the PUT operation fails.
329
"""
288
- response = self.wazuh_http_requests.put_request(f"rules/files/{filename}", xml_content, params={"overwrite": "true"})
330
+ response = self.wazuh_http_requests.put_request(
331
+ f"rules/files/{filename}",
332
+ xml_content,
333
+ params={"overwrite": "true"},
334
+ )
335
if not response["success"]:
336
raise RuntimeError(f"Could not PUT rule {filename}")
backend/app/services/WazuhManager/universal.py
+8
-5
@@ -1,6 +1,9 @@
1
-from loguru import logger
1
import requests
3
-from app.models.connectors import connector_factory, Connector
2
+from loguru import logger
3
+
4
+from app.models.connectors import Connector
5
+from app.models.connectors import connector_factory
6
+
7
8
class UniversalService:
9
"""
@@ -47,7 +50,7 @@ class UniversalService:
50
auth_token = response.json()["data"]["token"]
51
logger.info(f"Authentication token: {auth_token}")
52
return auth_token
50
-
53
+
54
def restart_service(self):
55
"""
56
Restart the Wazuh Manager service.
@@ -63,11 +66,11 @@ class UniversalService:
66
verify=False,
67
)
68
if response.status_code == 200:
66
- logger.info(f"Wazuh Manager service restarted")
69
+ logger.info("Wazuh Manager service restarted")
70
return {"message": "Wazuh Manager service restarted", "success": True}
71
else:
72
logger.error(
70
- f"Wazuh Manager service restart failed with error: {response.text}"
73
+ f"Wazuh Manager service restart failed with error: {response.text}",
74
)
75
return {
76
"message": "Wazuh Manager service restart failed",
backend/app/services/WazuhManager/vulnerability.py
+31
-7
@@ -1,12 +1,19 @@
1
-from typing import Dict, List, Optional, Any
1
+from typing import Any
2
+from typing import Dict
3
+from typing import List
4
+from typing import Optional
5
+
6
+import requests
7
from loguru import logger
8
+
9
from app.services.WazuhManager.universal import UniversalService
4
-import requests
10
+
11
12
class WazuhHttpRequests:
13
"""
14
Class to handle HTTP requests to the Wazuh API.
15
"""
16
+
17
def __init__(self, connector_url: str, wazuh_auth_token: str) -> None:
18
"""
19
Args:
@@ -17,7 +24,11 @@ class WazuhHttpRequests:
24
self.wazuh_auth_token = wazuh_auth_token
25
self.headers = {"Authorization": f"Bearer {wazuh_auth_token}"}
26
20
- def get_request(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
27
+ def get_request(
28
+ self,
29
+ endpoint: str,
30
+ params: Optional[Dict[str, Any]] = None,
31
+ ) -> Optional[Dict[str, Any]]:
32
"""
33
Function to handle GET requests.
34
@@ -39,13 +50,17 @@ class WazuhHttpRequests:
50
return response.json()
51
52
except Exception as e:
42
- logger.error(f"GET request to {self.connector_url}/{endpoint} failed with error: {e}")
53
+ logger.error(
54
+ f"GET request to {self.connector_url}/{endpoint} failed with error: {e}",
55
+ )
56
return None
57
58
+
59
class VulnerabilityService:
60
"""
61
A service class that encapsulates the logic for pulling API data from Wazuh Manager.
62
"""
63
+
64
def __init__(self, universal_service: UniversalService) -> None:
65
"""
66
Args:
@@ -53,7 +68,10 @@ class VulnerabilityService:
68
"""
69
self.universal_service = universal_service
70
self.auth_token = universal_service.get_auth_token()
56
- self.wazuh_http_requests = WazuhHttpRequests(self.universal_service.connector_url, self.auth_token)
71
+ self.wazuh_http_requests = WazuhHttpRequests(
72
+ self.universal_service.connector_url,
73
+ self.auth_token,
74
+ )
75
76
def agent_vulnerabilities(self, agent_id: str) -> List[Dict[str, Any]]:
77
"""
@@ -65,14 +83,20 @@ class VulnerabilityService:
83
Returns:
84
List[Dict[str, Any]]: A list of processed vulnerabilities.
85
"""
68
- response = self.wazuh_http_requests.get_request(f"vulnerability/{agent_id}", params={"wait_for_complete": True})
86
+ response = self.wazuh_http_requests.get_request(
87
+ f"vulnerability/{agent_id}",
88
+ params={"wait_for_complete": True},
89
+ )
90
91
if response is not None:
92
processed_vulnerabilities = self._process_agent_vulnerabilities(response)
93
return processed_vulnerabilities
94
return []
95
75
- def _process_agent_vulnerabilities(self, response: Dict[str, Any]) -> List[Dict[str, Any]]:
96
+ def _process_agent_vulnerabilities(
97
+ self,
98
+ response: Dict[str, Any],
99
+ ) -> List[Dict[str, Any]]:
100
"""
101
Process the vulnerabilities of an agent from Wazuh Manager.
102
backend/app/services/agents/agents.py
+27
-17
@@ -1,14 +1,16 @@
1
# services.py
2
-from app.models.agents import (
3
- AgentMetadata,
4
- agent_metadata_schema,
5
- agent_metadatas_schema,
6
-)
7
-from app import db
2
from datetime import datetime
3
+
4
import requests
5
from loguru import logger
11
-from app.models.connectors import connector_factory, Connector, WazuhManagerConnector
6
+
7
+from app import db
8
+from app.models.agents import AgentMetadata
9
+from app.models.agents import agent_metadata_schema
10
+from app.models.agents import agent_metadatas_schema
11
+from app.models.connectors import Connector
12
+from app.models.connectors import WazuhManagerConnector
13
+from app.models.connectors import connector_factory
14
15
16
class AgentService:
@@ -34,7 +36,8 @@ class AgentService:
36
agent_id (str): The ID of the agent to retrieve.
37
38
Returns:
37
- dict: A dictionary representing the serialized data of the agent if found, otherwise a message indicating that the agent was not found.
39
+ dict: A dictionary representing the serialized data of the agent if found, otherwise a message indicating
40
+ that the agent was not found.
41
"""
42
agent = db.session.query(AgentMetadata).filter_by(agent_id=agent_id).first()
43
if agent is None:
@@ -49,7 +52,8 @@ class AgentService:
52
agent_id (str): The ID of the agent to mark as critical.
53
54
Returns:
52
- dict: A dictionary representing a success message if the operation was successful, otherwise an error message.
55
+ dict: A dictionary representing a success message if the operation was successful, otherwise an error
56
+ message.
57
"""
58
agent = db.session.query(AgentMetadata).filter_by(agent_id=agent_id).first()
59
@@ -58,7 +62,7 @@ class AgentService:
62
63
agent.mark_as_critical()
64
agent_details = agent_metadata_schema.dump(agent)
61
- if agent_details["critical_asset"] == False:
65
+ if agent_details["critical_asset"] is False:
66
return {
67
"message": f"Agent {agent_id} failed to mark agent as critical",
68
"success": False,
@@ -73,7 +77,8 @@ class AgentService:
77
agent_id (str): The ID of the agent to mark as non-critical.
78
79
Returns:
76
- dict: A dictionary representing a success message if the operation was successful, otherwise an error message.
80
+ dict: A dictionary representing a success message if the operation was successful, otherwise an error
81
+ message.
82
"""
83
agent = db.session.query(AgentMetadata).filter_by(agent_id=agent_id).first()
84
@@ -82,7 +87,7 @@ class AgentService:
87
88
agent.mark_as_non_critical()
89
agent_details = agent_metadata_schema.dump(agent)
85
- if agent_details["critical_asset"] == True:
90
+ if agent_details["critical_asset"] is True:
91
return {
92
"message": f"Agent {agent_id} failed to mark agent as non-critical",
93
"success": False,
@@ -101,14 +106,16 @@ class AgentService:
106
"""
107
try:
108
agent_last_seen = datetime.strptime(
104
- agent["agent_last_seen"], "%Y-%m-%dT%H:%M:%S+00:00"
109
+ agent["agent_last_seen"],
110
+ "%Y-%m-%dT%H:%M:%S+00:00",
111
) # Convert to datetime
112
except ValueError:
113
logger.info(
108
- f"Invalid format for agent_last_seen: {agent['agent_last_seen']}. Fixing..."
114
+ f"Invalid format for agent_last_seen: {agent['agent_last_seen']}. Fixing...",
115
)
116
agent_last_seen = datetime.strptime(
111
- "1970-01-01T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S+00:00"
117
+ "1970-01-01T00:00:00+00:00",
118
+ "%Y-%m-%dT%H:%M:%S+00:00",
119
) # Use the epoch time as default
120
121
agent_metadata = AgentMetadata(
@@ -137,7 +144,8 @@ class AgentService:
144
agent_id (str): The ID of the agent to delete.
145
146
Returns:
140
- dict: A dictionary representing a success message if the operation was successful, otherwise an error message.
147
+ dict: A dictionary representing a success message if the operation was successful, otherwise an error
148
+ message.
149
"""
150
agent = db.session.query(AgentMetadata).filter_by(agent_id=agent_id).first()
151
if agent is None:
@@ -189,7 +197,9 @@ class AgentSyncService:
197
headers = {"Authorization": f"Bearer {wazuh_auth_token}"}
198
limit = 1000
199
agents_collected = requests.get(
192
- f"{connection_url}/agents?limit={limit}", headers=headers, verify=False
200
+ f"{connection_url}/agents?limit={limit}",
201
+ headers=headers,
202
+ verify=False,
203
)
204
if agents_collected.status_code == 200:
205
wazuh_agents_list = []
backend/app/services/connectors/connectors.py
+26
-12
@@ -1,8 +1,14 @@
1
-from app.models.connectors import connector_factory, Connector, ConnectorFactory
2
-from app.models.models import Connectors, connectors_schema, ConnectorsAvailable
3
-from sqlalchemy.exc import SQLAlchemyError
4
-from loguru import logger
1
from flask import current_app
2
+from loguru import logger
3
+from sqlalchemy.exc import SQLAlchemyError
4
+
5
+# from app.models.connectors import ConnectorFactory
6
+from app.models.connectors import Connector
7
+from app.models.connectors import connector_factory
8
+from app.models.models import Connectors
9
+
10
+# from app.models.models import ConnectorsAvailable
11
+# from app.models.models import connectors_schema
12
13
14
class ConnectorService:
@@ -53,7 +59,8 @@ class ConnectorService:
59
60
def validate_connector_exists(self, connector_id: int):
61
"""
56
- Validates that a connector exists in the database. Returns a dictionary containing the validation status and a message indicating the status.
62
+ Validates that a connector exists in the database. Returns a dictionary containing the validation status
63
+ and a message indicating the status.
64
65
Args:
66
connector_id (int): The id of the connector to be validated.
@@ -91,7 +98,8 @@ class ConnectorService:
98
updated_data (dict): A dictionary containing the updated data for the connector.
99
100
Returns:
94
- dict: A dictionary containing the success status and a message indicating the status. If the update operation was successful, it returns the connector name.
101
+ dict: A dictionary containing the success status and a message indicating the status.
102
+ If the update operation was successful, it returns the connector name.
103
"""
104
try:
105
connector = (
@@ -126,7 +134,8 @@ class ConnectorService:
134
connector_id (int): The id of the connector to be verified.
135
136
Returns:
129
- dict: A dictionary containing the success status and a message indicating the status. If the verification operation was successful, it returns the connector name.
137
+ dict: A dictionary containing the success status and a message indicating the status. If the verification
138
+ operation was successful, it returns the connector name.
139
"""
140
try:
141
connector = (
@@ -138,7 +147,8 @@ class ConnectorService:
147
"success": False,
148
}
149
connector_instance = connector_factory.create(
141
- connector.connector_name, connector.connector_name
150
+ connector.connector_name,
151
+ connector.connector_name,
152
)
153
connection_successful = connector_instance.verify_connection()
154
# Connection successful: {'connectionSuccessful': False}
@@ -160,7 +170,8 @@ class ConnectorService:
170
171
def validate_request_data(self, request_data: dict):
172
"""
163
- Validates the request data to ensure `connector_url`, `connector_username` and `connector_password` are present. Returns a dictionary containing the validation status and a message indicating the status.
173
+ Validates the request data to ensure `connector_url`, `connector_username` and `connector_password` are present.
174
+ Returns a dictionary containing the validation status and a message indicating the status.
175
176
Args:
177
request_data (dict): A dictionary containing the request data.
@@ -176,13 +187,15 @@ class ConnectorService:
187
return {"message": "Request data is valid", "success": True}
188
else:
189
return {
179
- "message": "Request data is invalid. Ensure connector_url, connector_username and connector_password are present",
190
+ "message": "Request data is invalid. Ensure connector_url, connector_username and connector_password "
191
+ "are present",
192
"success": False,
193
}
194
195
def validate_request_data_api_key(self, request_data: dict):
196
"""
185
- Validates the request data to ensure `connector_url` and `connector_api_key` are present. Returns a dictionary containing the validation status and a message indicating the status.
197
+ Validates the request data to ensure `connector_url` and `connector_api_key` are present. Returns a dictionary
198
+ containing the validation status and a message indicating the status.
199
200
Args:
201
request_data (dict): A dictionary containing the request data.
@@ -191,7 +204,8 @@ class ConnectorService:
204
dict: A dictionary containing the validation status and a message indicating the status.
205
"""
206
if request_data.get("connector_url", None) and request_data.get(
194
- "connector_api_key", None
207
+ "connector_api_key",
208
+ None,
209
):
210
return {"message": "Request data is valid", "success": True}
211
else:
backend/app/static/swagger.json
+1498
-1578
@@ -11,76 +11,74 @@
11
],
12
"tags": [
13
{
14
- "name": "Connectors",
15
- "description": "Everything about your Connectors",
16
- "externalDocs": {
17
- "description": "Find out more",
18
- "url": "http://swagger.io"
19
- }
14
+ "name": "Connectors",
15
+ "description": "Everything about your Connectors",
16
+ "externalDocs": {
17
+ "description": "Find out more",
18
+ "url": "http://swagger.io"
19
+ }
20
},
21
{
22
- "name": "Agents",
23
- "description": "Everything about your Agents",
24
- "externalDocs": {
25
- "description": "Find out more",
26
- "url": "http://swagger.io"
27
- }
22
+ "name": "Agents",
23
+ "description": "Everything about your Agents",
24
+ "externalDocs": {
25
+ "description": "Find out more",
26
+ "url": "http://swagger.io"
27
+ }
28
},
29
{
30
- "name": "Rules",
31
- "description": "Everything about your Wazuh Rules",
32
- "externalDocs": {
33
- "description": "Find out more",
34
- "url": "http://swagger.io"
35
- }
30
+ "name": "Rules",
31
+ "description": "Everything about your Wazuh Rules",
32
+ "externalDocs": {
33
+ "description": "Find out more",
34
+ "url": "http://swagger.io"
35
+ }
36
},
37
{
38
- "name": "Graylog",
39
- "description": "Everything about Graylog",
40
- "externalDocs": {
41
- "description": "Find out more",
42
- "url": "http://swagger.io"
43
- }
38
+ "name": "Graylog",
39
+ "description": "Everything about Graylog",
40
+ "externalDocs": {
41
+ "description": "Find out more",
42
+ "url": "http://swagger.io"
43
+ }
44
},
45
{
46
- "name": "Wazuh-Indexer",
47
- "description": "Everything about Wazuh-Indexer",
48
- "externalDocs": {
49
- "description": "Find out more",
50
- "url": "http://swagger.io"
51
- }
46
+ "name": "Wazuh-Indexer",
47
+ "description": "Everything about Wazuh-Indexer",
48
+ "externalDocs": {
49
+ "description": "Find out more",
50
+ "url": "http://swagger.io"
51
+ }
52
},
53
{
54
- "name": "Shuffle",
55
- "description": "Everything about Shuffle",
56
- "externalDocs": {
57
- "description": "Find out more",
58
- "url": "http://swagger.io"
59
- }
54
+ "name": "Shuffle",
55
+ "description": "Everything about Shuffle",
56
+ "externalDocs": {
57
+ "description": "Find out more",
58
+ "url": "http://swagger.io"
59
+ }
60
},
61
{
62
- "name": "Velociraptor",
63
- "description": "Everything about Velociraptor",
64
- "externalDocs": {
65
- "description": "Find out more",
66
- "url": "http://swagger.io"
67
- }
62
+ "name": "Velociraptor",
63
+ "description": "Everything about Velociraptor",
64
+ "externalDocs": {
65
+ "description": "Find out more",
66
+ "url": "http://swagger.io"
67
+ }
68
},
69
{
70
- "name": "DFIR-IRIS",
71
- "description": "Everything about DFIR-IRIS",
72
- "externalDocs": {
73
- "description": "Find out more",
74
- "url": "http://swagger.io"
75
- }
70
+ "name": "DFIR-IRIS",
71
+ "description": "Everything about DFIR-IRIS",
72
+ "externalDocs": {
73
+ "description": "Find out more",
74
+ "url": "http://swagger.io"
75
+ }
76
}
77
- ],
77
+ ],
78
"paths": {
79
"/connectors": {
80
"get": {
81
- "tags": [
82
- "Connectors"
83
- ],
81
+ "tags": ["Connectors"],
82
"summary": "List all available connectors",
83
"description": "Endpoint to list all available connectors. It processes each connector to verify the connection and returns the results.",
84
"responses": {
@@ -95,38 +93,38 @@
93
"properties": {
94
"connectionSuccessful": {
95
"type": "boolean"
98
- },
99
- "connector_api_key": {
96
+ },
97
+ "connector_api_key": {
98
"type": "string",
99
"nullable": true
102
- },
103
- "connector_last_updated": {
100
+ },
101
+ "connector_last_updated": {
102
"type": "string",
103
"format": "date-time"
106
- },
107
- "connector_name": {
104
+ },
105
+ "connector_name": {
106
"type": "string"
109
- },
110
- "connector_password": {
107
+ },
108
+ "connector_password": {
109
"type": "string",
110
"nullable": true
113
- },
114
- "connector_type": {
111
+ },
112
+ "connector_type": {
113
"type": "string"
116
- },
117
- "connector_url": {
114
+ },
115
+ "connector_url": {
116
"type": "string"
119
- },
120
- "connector_username": {
117
+ },
118
+ "connector_username": {
119
"type": "string",
120
"nullable": true
123
- },
124
- "id": {
121
+ },
122
+ "id": {
123
"type": "integer"
126
- },
127
- "name": {
124
+ },
125
+ "name": {
126
"type": "string"
129
- }
127
+ }
128
}
129
}
130
}
@@ -138,78 +136,74 @@
136
},
137
"/connectors/{id}": {
138
"get": {
141
- "tags": [
142
- "Connectors"
143
- ],
144
- "summary": "Get the details of a specific connector.",
145
- "operationId": "getConnectorDetails",
146
- "parameters": [
147
- {
148
- "name": "id",
149
- "in": "path",
150
- "description": "The id of the connector to be fetched.",
151
- "required": true,
152
- "schema": {
153
- "type": "integer"
154
- }
155
- }
156
- ],
157
- "responses": {
158
- "200": {
159
- "description": "A JSON response containing the details of the connector.",
160
- "content": {
161
- "application/json": {
162
- "schema": {
163
- "type": "object",
164
- "properties": {
165
- "name": {
166
- "type": "string"
167
- },
168
- "connectionSuccessful": {
169
- "type": "boolean"
170
- },
171
- "connector_api_key": {
172
- "type": "string",
173
- "nullable": true
174
- },
175
- "connector_last_updated": {
176
- "type": "string",
177
- "format": "date-time"
178
- },
179
- "connector_name": {
180
- "type": "string"
181
- },
182
- "connector_password": {
183
- "type": "string",
184
- "nullable": true
185
- },
186
- "connector_type": {
187
- "type": "string"
188
- },
189
- "connector_url": {
190
- "type": "string"
191
- },
192
- "connector_username": {
193
- "type": "string",
194
- "nullable": true
195
- },
196
- "id": {
139
+ "tags": ["Connectors"],
140
+ "summary": "Get the details of a specific connector.",
141
+ "operationId": "getConnectorDetails",
142
+ "parameters": [
143
+ {
144
+ "name": "id",
145
+ "in": "path",
146
+ "description": "The id of the connector to be fetched.",
147
+ "required": true,
148
+ "schema": {
149
"type": "integer"
198
- }
150
}
200
- }
151
}
202
- }
203
- },
204
- "404": {
205
- "description": "The connector could not be found."
152
+ ],
153
+ "responses": {
154
+ "200": {
155
+ "description": "A JSON response containing the details of the connector.",
156
+ "content": {
157
+ "application/json": {
158
+ "schema": {
159
+ "type": "object",
160
+ "properties": {
161
+ "name": {
162
+ "type": "string"
163
+ },
164
+ "connectionSuccessful": {
165
+ "type": "boolean"
166
+ },
167
+ "connector_api_key": {
168
+ "type": "string",
169
+ "nullable": true
170
+ },
171
+ "connector_last_updated": {
172
+ "type": "string",
173
+ "format": "date-time"
174
+ },
175
+ "connector_name": {
176
+ "type": "string"
177
+ },
178
+ "connector_password": {
179
+ "type": "string",
180
+ "nullable": true
181
+ },
182
+ "connector_type": {
183
+ "type": "string"
184
+ },
185
+ "connector_url": {
186
+ "type": "string"
187
+ },
188
+ "connector_username": {
189
+ "type": "string",
190
+ "nullable": true
191
+ },
192
+ "id": {
193
+ "type": "integer"
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+ },
200
+ "404": {
201
+ "description": "The connector could not be found."
202
+ }
203
}
207
- }
204
},
205
"put": {
210
- "tags": [
211
- "Connectors"
212
- ],
206
+ "tags": ["Connectors"],
207
"summary": "Update a connector",
208
"description": "Endpoint to update a connector. If the update operation was successful, it returns the connection verification status for the updated connector.",
209
"operationId": "update_connector_route",
@@ -314,9 +308,7 @@
308
},
309
"/agents": {
310
"get": {
317
- "tags": [
318
- "Agents"
319
- ],
311
+ "tags": ["Agents"],
312
"summary": "List all available agents",
313
"description": "Endpoint to list all available agents. It processes each agent to verify the connection and returns the results.",
314
"responses": {
@@ -338,9 +330,7 @@
330
},
331
"/agents/{id}": {
332
"get": {
341
- "tags": [
342
- "Agents"
343
- ],
333
+ "tags": ["Agents"],
334
"summary": "Get details of a specific agent",
335
"description": "Endpoint to get the details of an agent.",
336
"parameters": [
@@ -370,1531 +360,1468 @@
360
},
361
"/agents/{id}/critical": {
362
"post": {
373
- "tags": [
374
- "Agents"
375
- ],
376
- "summary": "Marks an agent as critical",
377
- "description": "Marks an agent as a critical asset in the database.",
378
- "parameters": [
379
- {
380
- "name": "id",
381
- "in": "path",
382
- "description": "ID of the agent to be marked as critical",
383
- "required": true,
384
- "type": "string"
385
- }
386
- ],
387
- "responses": {
388
- "200": {
389
- "description": "Agent marked as critical",
390
- "schema": {
391
- "$ref": "#/definitions/Agent"
392
- }
393
- },
394
- "400": {
395
- "description": "Invalid ID supplied"
396
- },
397
- "404": {
398
- "description": "Agent not found"
399
- }
400
- }
401
- }
402
- },
403
- "/agents/{id}/delete": {
404
- "post": {
405
- "tags": [
406
- "Agents"
407
- ],
408
- "summary": "Deletes an agent",
409
- "description": "Deletes an agent from the database.",
410
- "parameters": [
411
- {
412
- "name": "id",
413
- "in": "path",
414
- "description": "ID of the agent to be deleted",
415
- "required": true,
416
- "type": "string"
417
- }
418
- ],
419
- "responses": {
420
- "200": {
421
- "description": "Agent deleted",
422
- "schema": {
423
- "$ref": "#/definitions/Agent"
424
- }
425
- },
426
- "400": {
427
- "description": "Invalid ID supplied"
428
- },
429
- "404": {
430
- "description": "Agent not found"
431
- }
432
- }
433
- }
434
-
435
- },
436
- "/agents/{id}/noncritical": {
437
- "post": {
438
- "tags": [
439
- "Agents"
440
- ],
441
- "summary": "Unmarks an agent as critical",
442
- "description": "Marks an agent as a non-critical asset in the database.",
443
- "parameters": [
444
- {
445
- "name": "id",
446
- "in": "path",
447
- "description": "ID of the agent to be unmarked as critical",
448
- "required": true,
449
- "type": "string"
450
- }
451
- ],
452
- "responses": {
453
- "200": {
454
- "description": "Agent unmarked as critical",
455
- "schema": {
456
- "$ref": "#/definitions/Agent"
457
- }
458
- },
459
- "400": {
460
- "description": "Invalid ID supplied"
461
- },
462
- "404": {
463
- "description": "Agent not found"
464
- }
465
- }
466
- }
467
- },
468
- "/agents/sync": {
469
- "post": {
470
- "summary": "Sync all agents",
471
- "description": "Endpoint to sync all agents.",
472
- "responses": {
473
- "200": {
474
- "description": "Successful operation",
475
- "content": {
476
- "application/json": {
477
- "schema": {
478
- "type": "object",
479
- "properties": {
480
- "message": {
481
- "type": "string",
482
- "description": "Operation message"
483
- },
484
- "success": {
485
- "type": "boolean",
486
- "description": "Indicates if the operation was successful"
487
- },
488
- "agents_added": {
489
- "type": "array",
490
- "items": {
491
- "type": "object",
492
- "description": "Agent details"
493
- }
494
- }
495
- }
496
- }
363
+ "tags": ["Agents"],
364
+ "summary": "Marks an agent as critical",
365
+ "description": "Marks an agent as a critical asset in the database.",
366
+ "parameters": [
367
+ {
368
+ "name": "id",
369
+ "in": "path",
370
+ "description": "ID of the agent to be marked as critical",
371
+ "required": true,
372
+ "type": "string"
373
}
498
- }
499
- },
500
- "default": {
501
- "description": "Unexpected error",
502
- "content": {
503
- "application/json": {
504
- "schema": {
505
- "$ref": "#/components/schemas/Error"
506
- }
374
+ ],
375
+ "responses": {
376
+ "200": {
377
+ "description": "Agent marked as critical",
378
+ "schema": {
379
+ "$ref": "#/definitions/Agent"
380
+ }
381
+ },
382
+ "400": {
383
+ "description": "Invalid ID supplied"
384
+ },
385
+ "404": {
386
+ "description": "Agent not found"
387
}
508
- }
388
}
510
- },
511
- "operationId": "syncAgents",
512
- "tags": [
513
- "Agents"
514
- ]
389
}
516
- },
517
- "/agents/{id}/vulnerabilities": {
518
- "get": {
519
- "tags": [
520
- "Agents"
521
- ],
522
- "summary": "Get vulnerabilities of a specific agent",
523
- "description": "Endpoint to get the vulnerabilities of an agent.",
524
- "parameters": [
525
- {
526
- "name": "id",
527
- "in": "path",
528
- "required": true,
529
- "description": "ID of the agent to be fetched",
530
- "schema": {
531
- "type": "string"
532
- }
533
- }
534
- ],
535
- "responses": {
536
- "200": {
537
- "description": "Successful operation",
538
- "content": {
539
- "application/json": {
540
- "schema": {
541
- "type": "array",
542
- "items": {
543
- "$ref": "#/components/schemas/Vulnerability"
544
- }
545
- }
546
- }
547
- }
548
- }
549
- }
550
- }
551
- },
552
- "/rule/disable": {
390
+ },
391
+ "/agents/{id}/delete": {
392
"post": {
554
- "summary": "Disable a rule",
555
- "description": "Endpoint to disable a rule.",
556
- "requestBody": {
557
- "content": {
558
- "application/json": {
559
- "schema": {
560
- "type": "object",
561
- "properties": {
562
- "rule_id": {
563
- "type": "string",
564
- "description": "The ID of the rule to be disabled."
565
- },
566
- "reason": {
567
- "type": "string",
568
- "description": "The reason for disabling the rule."
569
- },
570
- "length_of_time": {
571
- "type": "integer",
572
- "description": "The length of time the rule should be disabled for."
573
- }
574
- }
575
- }
576
- }
577
- }
578
- },
579
- "responses": {
580
- "200": {
581
- "description": "Successful operation",
582
- "content": {
583
- "application/json": {
584
- "schema": {
585
- "type": "object",
586
- "properties": {
587
- "message": {
588
- "type": "string",
589
- "description": "Operation message"
590
- },
591
- "success": {
592
- "type": "boolean",
593
- "description": "Indicates if the operation was successful"
594
- }
595
- }
596
- }
393
+ "tags": ["Agents"],
394
+ "summary": "Deletes an agent",
395
+ "description": "Deletes an agent from the database.",
396
+ "parameters": [
397
+ {
398
+ "name": "id",
399
+ "in": "path",
400
+ "description": "ID of the agent to be deleted",
401
+ "required": true,
402
+ "type": "string"
403
}
598
- }
599
- },
600
- "default": {
601
- "description": "Unexpected error",
602
- "content": {
603
- "application/json": {
604
- "schema": {
605
- "$ref": "#/components/schemas/Error"
606
- }
404
+ ],
405
+ "responses": {
406
+ "200": {
407
+ "description": "Agent deleted",
408
+ "schema": {
409
+ "$ref": "#/definitions/Agent"
410
+ }
411
+ },
412
+ "400": {
413
+ "description": "Invalid ID supplied"
414
+ },
415
+ "404": {
416
+ "description": "Agent not found"
417
}
608
- }
418
}
610
- },
611
- "operationId": "disableRule",
612
- "tags": [
613
- "Rules"
614
- ]
419
}
616
- },
617
- "/rule/enable": {
420
+ },
421
+ "/agents/{id}/noncritical": {
422
"post": {
619
- "summary": "Enable a rule",
620
- "description": "Endpoint to enable a rule.",
621
- "requestBody": {
622
- "content": {
623
- "application/json": {
624
- "schema": {
625
- "type": "object",
626
- "properties": {
627
- "rule_id": {
628
- "type": "string",
629
- "description": "The ID of the rule to be enabled."
630
- }
631
- }
632
- }
633
- }
634
- }
635
- },
636
- "responses": {
637
- "200": {
638
- "description": "Successful operation",
639
- "content": {
640
- "application/json": {
641
- "schema": {
642
- "type": "object",
643
- "properties": {
644
- "message": {
645
- "type": "string",
646
- "description": "Operation message"
647
- },
648
- "success": {
649
- "type": "boolean",
650
- "description": "Indicates if the operation was successful"
651
- }
652
- }
653
- }
654
- }
655
- }
656
- },
657
- "default": {
658
- "description": "Unexpected error",
659
- "content": {
660
- "application/json": {
661
- "schema": {
662
- "$ref": "#/components/schemas/Error"
663
- }
664
- }
665
- }
666
- }
667
- },
668
- "operationId": "enableRule",
669
- "tags": [
670
- "Rules"
671
- ]
672
- }
673
- },
674
- "/graylog/messages": {
675
- "get": {
676
- "summary": "Get messages from Graylog",
677
- "description": "Endpoint to get messages from Graylog.",
678
- "responses": {
679
- "200": {
680
- "description": "Successful operation",
681
- "content": {
682
- "application/json": {
683
- "schema": {
684
- "type": "object",
685
- "properties": {
686
- "messages": {
687
- "type": "array",
688
- "items": {
689
- "type": "object",
690
- "description": "Message details"
691
- }
692
- }
693
- }
694
- }
423
+ "tags": ["Agents"],
424
+ "summary": "Unmarks an agent as critical",
425
+ "description": "Marks an agent as a non-critical asset in the database.",
426
+ "parameters": [
427
+ {
428
+ "name": "id",
429
+ "in": "path",
430
+ "description": "ID of the agent to be unmarked as critical",
431
+ "required": true,
432
+ "type": "string"
433
}
696
- }
697
- },
698
- "default": {
699
- "description": "Unexpected error",
700
- "content": {
701
- "application/json": {
702
- "schema": {
703
- "$ref": "#/components/schemas/Error"
704
- }
434
+ ],
435
+ "responses": {
436
+ "200": {
437
+ "description": "Agent unmarked as critical",
438
+ "schema": {
439
+ "$ref": "#/definitions/Agent"
440
+ }
441
+ },
442
+ "400": {
443
+ "description": "Invalid ID supplied"
444
+ },
445
+ "404": {
446
+ "description": "Agent not found"
447
}
706
- }
448
}
708
- },
709
- "operationId": "getGraylogMessages",
710
- "tags": [
711
- "Graylog"
712
- ]
449
}
714
- },
715
- "/graylog/metrics": {
716
- "get": {
717
- "summary": "Get metrics from Graylog",
718
- "description": "Endpoint to get metrics from Graylog.",
719
- "responses": {
720
- "200": {
721
- "description": "Successful operation",
722
- "content": {
723
- "application/json": {
724
- "schema": {
725
- "type": "object",
726
- "properties": {
727
- "metrics": {
728
- "type": "array",
729
- "items": {
730
- "type": "object",
731
- "description": "Metric details"
732
- }
733
- }
734
- }
735
- }
450
+ },
451
+ "/agents/sync": {
452
+ "post": {
453
+ "summary": "Sync all agents",
454
+ "description": "Endpoint to sync all agents.",
455
+ "responses": {
456
+ "200": {
457
+ "description": "Successful operation",
458
+ "content": {
459
+ "application/json": {
460
+ "schema": {
461
+ "type": "object",
462
+ "properties": {
463
+ "message": {
464
+ "type": "string",
465
+ "description": "Operation message"
466
+ },
467
+ "success": {
468
+ "type": "boolean",
469
+ "description": "Indicates if the operation was successful"
470
+ },
471
+ "agents_added": {
472
+ "type": "array",
473
+ "items": {
474
+ "type": "object",
475
+ "description": "Agent details"
476
+ }
477
+ }
478
+ }
479
+ }
480
+ }
481
+ }
482
+ },
483
+ "default": {
484
+ "description": "Unexpected error",
485
+ "content": {
486
+ "application/json": {
487
+ "schema": {
488
+ "$ref": "#/components/schemas/Error"
489
+ }
490
+ }
491
+ }
492
}
737
- }
493
},
739
- "default": {
740
- "description": "Unexpected error",
741
- "content": {
742
- "application/json": {
743
- "schema": {
744
- "$ref": "#/components/schemas/Error"
745
- }
746
- }
747
- }
748
- }
749
- },
750
- "operationId": "getGraylogMetrics",
751
- "tags": [
752
- "Graylog"
753
- ]
494
+ "operationId": "syncAgents",
495
+ "tags": ["Agents"]
496
}
755
- },
756
- "/graylog/indices": {
497
+ },
498
+ "/agents/{id}/vulnerabilities": {
499
"get": {
758
- "summary": "Get indices from Graylog",
759
- "description": "Endpoint to get indices from Graylog.",
760
- "responses": {
761
- "200": {
762
- "description": "Successful operation",
763
- "content": {
764
- "application/json": {
765
- "schema": {
766
- "type": "object",
767
- "properties": {
768
- "indices": {
769
- "type": "array",
770
- "items": {
771
- "type": "object",
772
- "description": "Index details"
773
- }
774
- }
775
- }
776
- }
500
+ "tags": ["Agents"],
501
+ "summary": "Get vulnerabilities of a specific agent",
502
+ "description": "Endpoint to get the vulnerabilities of an agent.",
503
+ "parameters": [
504
+ {
505
+ "name": "id",
506
+ "in": "path",
507
+ "required": true,
508
+ "description": "ID of the agent to be fetched",
509
+ "schema": {
510
+ "type": "string"
511
+ }
512
}
778
- }
779
- },
780
- "default": {
781
- "description": "Unexpected error",
782
- "content": {
783
- "application/json": {
784
- "schema": {
785
- "$ref": "#/components/schemas/Error"
786
- }
513
+ ],
514
+ "responses": {
515
+ "200": {
516
+ "description": "Successful operation",
517
+ "content": {
518
+ "application/json": {
519
+ "schema": {
520
+ "type": "array",
521
+ "items": {
522
+ "$ref": "#/components/schemas/Vulnerability"
523
+ }
524
+ }
525
+ }
526
+ }
527
}
788
- }
528
}
790
- },
791
- "operationId": "getGraylogIndices",
792
- "tags": [
793
- "Graylog"
794
- ]
529
}
796
- },
797
- "/graylog/indices/{index_name}/delete": {
798
- "delete": {
799
- "tags": [
800
- "Graylog"
801
- ],
802
- "summary": "Deletes a Graylog index",
803
- "description": "Endpoint to delete a Graylog index.",
804
- "parameters": [
805
- {
806
- "name": "index_name",
807
- "in": "path",
808
- "description": "The name of the index to be deleted.",
809
- "required": true,
810
- "type": "string"
811
- }
812
- ],
813
- "responses": {
814
- "200": {
815
- "description": "Successful operation",
816
- "schema": {
817
- "type": "object",
818
- "properties": {
819
- "message": {
820
- "type": "string"
821
- },
822
- "success": {
823
- "type": "boolean"
824
- }
530
+ },
531
+ "/rule/disable": {
532
+ "post": {
533
+ "summary": "Disable a rule",
534
+ "description": "Endpoint to disable a rule.",
535
+ "requestBody": {
536
+ "content": {
537
+ "application/json": {
538
+ "schema": {
539
+ "type": "object",
540
+ "properties": {
541
+ "rule_id": {
542
+ "type": "string",
543
+ "description": "The ID of the rule to be disabled."
544
+ },
545
+ "reason": {
546
+ "type": "string",
547
+ "description": "The reason for disabling the rule."
548
+ },
549
+ "length_of_time": {
550
+ "type": "integer",
551
+ "description": "The length of time the rule should be disabled for."
552
+ }
553
+ }
554
+ }
555
+ }
556
}
826
- }
557
},
828
- "400": {
829
- "description": "Bad request",
830
- "schema": {
831
- "type": "object",
832
- "properties": {
833
- "message": {
834
- "type": "string"
835
- },
836
- "success": {
837
- "type": "boolean"
838
- }
558
+ "responses": {
559
+ "200": {
560
+ "description": "Successful operation",
561
+ "content": {
562
+ "application/json": {
563
+ "schema": {
564
+ "type": "object",
565
+ "properties": {
566
+ "message": {
567
+ "type": "string",
568
+ "description": "Operation message"
569
+ },
570
+ "success": {
571
+ "type": "boolean",
572
+ "description": "Indicates if the operation was successful"
573
+ }
574
+ }
575
+ }
576
+ }
577
+ }
578
+ },
579
+ "default": {
580
+ "description": "Unexpected error",
581
+ "content": {
582
+ "application/json": {
583
+ "schema": {
584
+ "$ref": "#/components/schemas/Error"
585
+ }
586
+ }
587
+ }
588
}
840
- }
589
},
842
- "404": {
843
- "description": "Index not found",
844
- "schema": {
845
- "type": "object",
846
- "properties": {
847
- "message": {
848
- "type": "string"
849
- },
850
- "success": {
851
- "type": "boolean"
852
- }
853
- }
854
- }
855
- }
856
- }
590
+ "operationId": "disableRule",
591
+ "tags": ["Rules"]
592
}
858
- },
859
- "/graylog/inputs": {
860
- "get": {
861
- "summary": "Get inputs from Graylog",
862
- "description": "Endpoint to get inputs from Graylog.",
863
- "responses": {
864
- "200": {
865
- "description": "Successful operation",
866
- "content": {
867
- "application/json": {
868
- "schema": {
869
- "type": "object",
870
- "properties": {
871
- "inputs": {
872
- "type": "array",
873
- "items": {
874
- "type": "object",
875
- "description": "Input details"
876
- }
877
- }
878
- }
879
- }
593
+ },
594
+ "/rule/enable": {
595
+ "post": {
596
+ "summary": "Enable a rule",
597
+ "description": "Endpoint to enable a rule.",
598
+ "requestBody": {
599
+ "content": {
600
+ "application/json": {
601
+ "schema": {
602
+ "type": "object",
603
+ "properties": {
604
+ "rule_id": {
605
+ "type": "string",
606
+ "description": "The ID of the rule to be enabled."
607
+ }
608
+ }
609
+ }
610
+ }
611
}
881
- }
612
},
883
- "default": {
884
- "description": "Unexpected error",
885
- "content": {
886
- "application/json": {
887
- "schema": {
888
- "$ref": "#/components/schemas/Error"
889
- }
890
- }
891
- }
892
- }
893
- },
894
- "operationId": "getGraylogInputs",
895
- "tags": [
896
- "Graylog"
897
- ]
898
- }
899
- },
900
- "/alerts": {
901
- "get": {
902
- "summary": "Get alerts",
903
- "description": "Endpoint to get alerts.",
904
- "responses": {
905
- "200": {
906
- "description": "Successful operation",
907
- "content": {
908
- "application/json": {
909
- "schema": {
910
- "type": "object",
911
- "properties": {
912
- "alerts": {
913
- "type": "array",
914
- "items": {
915
- "type": "object",
916
- "description": "Alert details"
917
- }
918
- }
919
- }
920
- }
921
- }
922
- }
923
- },
924
- "default": {
925
- "description": "Unexpected error",
926
- "content": {
927
- "application/json": {
928
- "schema": {
929
- "$ref": "#/components/schemas/Error"
930
- }
931
- }
932
- }
933
- }
934
- },
935
- "operationId": "getAlerts",
936
- "tags": [
937
- "Wazuh-Indexer"
938
- ]
939
- }
940
- },
941
- "/wazuh_indexer/allocation": {
942
- "get": {
943
- "summary": "Get node allocation of the Wazuh-Indexer nodes",
944
- "description": "Endpoint to get node allocation.",
945
- "responses": {
946
- "200": {
947
- "description": "Successful operation",
948
- "content": {
949
- "application/json": {
950
- "schema": {
951
- "type": "object",
952
- "properties": {
953
- "indices": {
954
- "type": "array",
955
- "items": {
956
- "type": "object",
957
- "description": "Node details"
958
- }
959
- }
960
- }
961
- }
962
- }
963
- }
964
- },
965
- "default": {
966
- "description": "Unexpected error",
967
- "content": {
968
- "application/json": {
969
- "schema": {
970
- "$ref": "#/components/schemas/Error"
971
- }
972
- }
973
- }
974
- }
975
- },
976
- "operationId": "getNodeAllocation",
977
- "tags": [
978
- "Wazuh-Indexer"
979
- ]
980
- }
981
- },
982
- "/wazuh_indexer/indices": {
983
- "get": {
984
- "summary": "Get indices from Wazuh-Indexer",
985
- "description": "Endpoint to get indices from Wazuh-Indexer.",
986
- "responses": {
987
- "200": {
988
- "description": "Successful operation",
989
- "content": {
990
- "application/json": {
991
- "schema": {
992
- "type": "object",
993
- "properties": {
994
- "indices": {
995
- "type": "array",
996
- "items": {
997
- "type": "object",
998
- "description": "Index details"
999
- }
1000
- }
1001
- }
1002
- }
613
+ "responses": {
614
+ "200": {
615
+ "description": "Successful operation",
616
+ "content": {
617
+ "application/json": {
618
+ "schema": {
619
+ "type": "object",
620
+ "properties": {
621
+ "message": {
622
+ "type": "string",
623
+ "description": "Operation message"
624
+ },
625
+ "success": {
626
+ "type": "boolean",
627
+ "description": "Indicates if the operation was successful"
628
+ }
629
+ }
630
+ }
631
+ }
632
+ }
633
+ },
634
+ "default": {
635
+ "description": "Unexpected error",
636
+ "content": {
637
+ "application/json": {
638
+ "schema": {
639
+ "$ref": "#/components/schemas/Error"
640
+ }
641
+ }
642
+ }
643
}
1004
- }
644
},
1006
- "default": {
1007
- "description": "Unexpected error",
1008
- "content": {
1009
- "application/json": {
1010
- "schema": {
1011
- "$ref": "#/components/schemas/Error"
1012
- }
1013
- }
1014
- }
1015
- }
1016
- },
1017
- "operationId": "getWazuhIndices",
1018
- "tags": [
1019
- "Wazuh-Indexer"
1020
- ]
645
+ "operationId": "enableRule",
646
+ "tags": ["Rules"]
647
}
1022
- },
1023
- "/wazuh_indexer/health": {
648
+ },
649
+ "/graylog/messages": {
650
"get": {
1025
- "summary": "Get health of the Wazuh-Indexer nodes",
1026
- "description": "Endpoint to get health.",
1027
- "responses": {
1028
- "200": {
1029
- "description": "Successful operation",
1030
- "content": {
1031
- "application/json": {
1032
- "schema": {
1033
- "type": "object",
1034
- "properties": {
1035
- "cluster_name": {
1036
- "type": "string"
1037
- },
1038
- "status": {
1039
- "type": "string"
1040
- },
1041
- "number_of_nodes": {
1042
- "type": "integer"
1043
- },
1044
- "number_of_data_nodes": {
1045
- "type": "integer"
1046
- },
1047
- "active_primary_shards": {
1048
- "type": "integer"
1049
- },
1050
- "active_shards": {
1051
- "type": "integer"
1052
- },
1053
- "relocating_shards": {
1054
- "type": "integer"
1055
- },
1056
- "initializing_shards": {
1057
- "type": "integer"
1058
- },
1059
- "unassigned_shards": {
1060
- "type": "integer"
1061
- },
1062
- "delayed_unassigned_shards": {
1063
- "type": "integer"
1064
- },
1065
- "number_of_pending_tasks": {
1066
- "type": "integer"
1067
- },
1068
- "number_of_in_flight_fetch": {
1069
- "type": "integer"
1070
- },
1071
- "task_max_waiting_in_queue_millis": {
1072
- "type": "integer"
1073
- },
1074
- "active_shards_percent_as_number": {
1075
- "type": "integer"
1076
- }
651
+ "summary": "Get messages from Graylog",
652
+ "description": "Endpoint to get messages from Graylog.",
653
+ "responses": {
654
+ "200": {
655
+ "description": "Successful operation",
656
+ "content": {
657
+ "application/json": {
658
+ "schema": {
659
+ "type": "object",
660
+ "properties": {
661
+ "messages": {
662
+ "type": "array",
663
+ "items": {
664
+ "type": "object",
665
+ "description": "Message details"
666
+ }
667
+ }
668
+ }
669
+ }
670
+ }
671
+ }
672
+ },
673
+ "default": {
674
+ "description": "Unexpected error",
675
+ "content": {
676
+ "application/json": {
677
+ "schema": {
678
+ "$ref": "#/components/schemas/Error"
679
+ }
680
+ }
681
}
1078
- }
682
}
1080
- }
683
},
1082
- "default": {
1083
- "description": "Unexpected error",
1084
- "content": {
1085
- "application/json": {
1086
- "schema": {
1087
- "$ref": "#/components/schemas/Error"
1088
- }
1089
- }
1090
- }
1091
- }
1092
- },
1093
- "operationId": "getHealth",
1094
- "tags": [
1095
- "Wazuh-Indexer"
1096
- ]
684
+ "operationId": "getGraylogMessages",
685
+ "tags": ["Graylog"]
686
}
1098
- },
1099
- "/wazuh_indexer/shards": {
687
+ },
688
+ "/graylog/metrics": {
689
"get": {
1101
- "summary": "Get shards from Wazuh-Indexer",
1102
- "description": "Endpoint to get shards from Wazuh-Indexer.",
1103
- "responses": {
1104
- "200": {
1105
- "description": "Successful operation",
1106
- "content": {
1107
- "application/json": {
1108
- "schema": {
1109
- "type": "object",
1110
- "properties": {
1111
- "shards": {
1112
- "type": "array",
1113
- "items": {
1114
- "type": "object",
1115
- "description": "Shard details"
1116
- }
1117
- }
1118
- }
1119
- }
690
+ "summary": "Get metrics from Graylog",
691
+ "description": "Endpoint to get metrics from Graylog.",
692
+ "responses": {
693
+ "200": {
694
+ "description": "Successful operation",
695
+ "content": {
696
+ "application/json": {
697
+ "schema": {
698
+ "type": "object",
699
+ "properties": {
700
+ "metrics": {
701
+ "type": "array",
702
+ "items": {
703
+ "type": "object",
704
+ "description": "Metric details"
705
+ }
706
+ }
707
+ }
708
+ }
709
+ }
710
+ }
711
+ },
712
+ "default": {
713
+ "description": "Unexpected error",
714
+ "content": {
715
+ "application/json": {
716
+ "schema": {
717
+ "$ref": "#/components/schemas/Error"
718
+ }
719
+ }
720
+ }
721
}
1121
- }
722
},
1123
- "default": {
1124
- "description": "Unexpected error",
1125
- "content": {
1126
- "application/json": {
1127
- "schema": {
1128
- "$ref": "#/components/schemas/Error"
1129
- }
1130
- }
1131
- }
1132
- }
1133
- },
1134
- "operationId": "getShards",
1135
- "tags": [
1136
- "Wazuh-Indexer"
1137
- ]
723
+ "operationId": "getGraylogMetrics",
724
+ "tags": ["Graylog"]
725
}
1139
- },
1140
- "/shuffle/workflows": {
726
+ },
727
+ "/graylog/indices": {
728
"get": {
1142
- "summary": "Get all workflows",
1143
- "description": "Endpoint to get all workflows.",
1144
- "responses": {
1145
- "200": {
1146
- "description": "Successful operation",
1147
- "content": {
1148
- "application/json": {
1149
- "schema": {
1150
- "type": "object",
1151
- "properties": {
1152
- "workflows": {
1153
- "type": "array",
1154
- "items": {
1155
- "type": "object",
1156
- "description": "Workflow details"
1157
- }
1158
- }
1159
- }
1160
- }
729
+ "summary": "Get indices from Graylog",
730
+ "description": "Endpoint to get indices from Graylog.",
731
+ "responses": {
732
+ "200": {
733
+ "description": "Successful operation",
734
+ "content": {
735
+ "application/json": {
736
+ "schema": {
737
+ "type": "object",
738
+ "properties": {
739
+ "indices": {
740
+ "type": "array",
741
+ "items": {
742
+ "type": "object",
743
+ "description": "Index details"
744
+ }
745
+ }
746
+ }
747
+ }
748
+ }
749
+ }
750
+ },
751
+ "default": {
752
+ "description": "Unexpected error",
753
+ "content": {
754
+ "application/json": {
755
+ "schema": {
756
+ "$ref": "#/components/schemas/Error"
757
+ }
758
+ }
759
+ }
760
}
1162
- }
761
},
1164
- "default": {
1165
- "description": "Unexpected error",
1166
- "content": {
1167
- "application/json": {
1168
- "schema": {
1169
- "$ref": "#/components/schemas/Error"
1170
- }
1171
- }
1172
- }
1173
- }
1174
- },
1175
- "operationId": "getAllWorkflows",
1176
- "tags": [
1177
- "Shuffle"
1178
- ]
762
+ "operationId": "getGraylogIndices",
763
+ "tags": ["Graylog"]
764
}
1180
- },
1181
- "/shuffle/workflows/executions": {
1182
- "get": {
1183
- "summary": "Get all workflow executions",
1184
- "description": "Endpoint to get all workflow executions.",
1185
- "responses": {
1186
- "200": {
1187
- "description": "Successful operation",
1188
- "content": {
1189
- "application/json": {
1190
- "schema": {
1191
- "type": "object",
1192
- "properties": {
1193
- "executions": {
1194
- "type": "array",
1195
- "items": {
1196
- "type": "object",
1197
- "description": "Workflow execution details"
1198
- }
1199
- }
1200
- }
1201
- }
765
+ },
766
+ "/graylog/indices/{index_name}/delete": {
767
+ "delete": {
768
+ "tags": ["Graylog"],
769
+ "summary": "Deletes a Graylog index",
770
+ "description": "Endpoint to delete a Graylog index.",
771
+ "parameters": [
772
+ {
773
+ "name": "index_name",
774
+ "in": "path",
775
+ "description": "The name of the index to be deleted.",
776
+ "required": true,
777
+ "type": "string"
778
}
1203
- }
1204
- },
1205
- "default": {
1206
- "description": "Unexpected error",
1207
- "content": {
1208
- "application/json": {
1209
- "schema": {
1210
- "$ref": "#/components/schemas/Error"
1211
- }
779
+ ],
780
+ "responses": {
781
+ "200": {
782
+ "description": "Successful operation",
783
+ "schema": {
784
+ "type": "object",
785
+ "properties": {
786
+ "message": {
787
+ "type": "string"
788
+ },
789
+ "success": {
790
+ "type": "boolean"
791
+ }
792
+ }
793
+ }
794
+ },
795
+ "400": {
796
+ "description": "Bad request",
797
+ "schema": {
798
+ "type": "object",
799
+ "properties": {
800
+ "message": {
801
+ "type": "string"
802
+ },
803
+ "success": {
804
+ "type": "boolean"
805
+ }
806
+ }
807
+ }
808
+ },
809
+ "404": {
810
+ "description": "Index not found",
811
+ "schema": {
812
+ "type": "object",
813
+ "properties": {
814
+ "message": {
815
+ "type": "string"
816
+ },
817
+ "success": {
818
+ "type": "boolean"
819
+ }
820
+ }
821
+ }
822
}
1213
- }
823
}
1215
- },
1216
- "operationId": "getAllWorkflowExecutions",
1217
- "tags": [
1218
- "Shuffle"
1219
- ]
824
}
1221
- },
1222
- "/shuffle/workflows/executions/{workflow_id}": {
825
+ },
826
+ "/graylog/inputs": {
827
"get": {
1224
- "summary": "Get workflow executions by workflow id",
1225
- "description": "Endpoint to get workflow executions by workflow id.",
1226
- "parameters": [
1227
- {
1228
- "name": "workflow_id",
1229
- "in": "path",
1230
- "description": "The workflow id",
1231
- "required": true,
1232
- "schema": {
1233
- "type": "string"
1234
- }
1235
- }
1236
- ],
1237
- "responses": {
1238
- "200": {
1239
- "description": "Successful operation",
1240
- "content": {
1241
- "application/json": {
1242
- "schema": {
1243
- "type": "object",
1244
- "properties": {
1245
- "executions": {
1246
- "type": "array",
1247
- "items": {
1248
- "type": "object",
1249
- "description": "Workflow execution details"
1250
- }
1251
- }
1252
- }
1253
- }
828
+ "summary": "Get inputs from Graylog",
829
+ "description": "Endpoint to get inputs from Graylog.",
830
+ "responses": {
831
+ "200": {
832
+ "description": "Successful operation",
833
+ "content": {
834
+ "application/json": {
835
+ "schema": {
836
+ "type": "object",
837
+ "properties": {
838
+ "inputs": {
839
+ "type": "array",
840
+ "items": {
841
+ "type": "object",
842
+ "description": "Input details"
843
+ }
844
+ }
845
+ }
846
+ }
847
+ }
848
+ }
849
+ },
850
+ "default": {
851
+ "description": "Unexpected error",
852
+ "content": {
853
+ "application/json": {
854
+ "schema": {
855
+ "$ref": "#/components/schemas/Error"
856
+ }
857
+ }
858
+ }
859
}
1255
- }
860
},
1257
- "default": {
1258
- "description": "Unexpected error",
1259
- "content": {
1260
- "application/json": {
1261
- "schema": {
1262
- "$ref": "#/components/schemas/Error"
1263
- }
1264
- }
1265
- }
1266
- }
1267
- },
1268
- "operationId": "getWorkflowExecutionsByWorkflowId",
1269
- "tags": [
1270
- "Shuffle"
1271
- ]
861
+ "operationId": "getGraylogInputs",
862
+ "tags": ["Graylog"]
863
}
1273
- },
1274
- "/velociraptor/artifacts": {
864
+ },
865
+ "/alerts": {
866
"get": {
1276
- "summary": "Get all artifacts",
1277
- "description": "Endpoint to get all artifacts.",
1278
- "responses": {
1279
- "200": {
1280
- "description": "Successful operation",
1281
- "content": {
1282
- "application/json": {
1283
- "schema": {
1284
- "type": "object",
1285
- "properties": {
1286
- "artifacts": {
1287
- "type": "array",
1288
- "items": {
1289
- "type": "object",
1290
- "description": "Artifact details"
1291
- }
1292
- }
1293
- }
1294
- }
867
+ "summary": "Get alerts",
868
+ "description": "Endpoint to get alerts.",
869
+ "responses": {
870
+ "200": {
871
+ "description": "Successful operation",
872
+ "content": {
873
+ "application/json": {
874
+ "schema": {
875
+ "type": "object",
876
+ "properties": {
877
+ "alerts": {
878
+ "type": "array",
879
+ "items": {
880
+ "type": "object",
881
+ "description": "Alert details"
882
+ }
883
+ }
884
+ }
885
+ }
886
+ }
887
+ }
888
+ },
889
+ "default": {
890
+ "description": "Unexpected error",
891
+ "content": {
892
+ "application/json": {
893
+ "schema": {
894
+ "$ref": "#/components/schemas/Error"
895
+ }
896
+ }
897
+ }
898
}
1296
- }
899
},
1298
- "default": {
1299
- "description": "Unexpected error",
1300
- "content": {
1301
- "application/json": {
1302
- "schema": {
1303
- "$ref": "#/components/schemas/Error"
1304
- }
1305
- }
1306
- }
1307
- }
1308
- },
1309
- "operationId": "getAllArtifacts",
1310
- "tags": [
1311
- "Velociraptor"
1312
- ]
900
+ "operationId": "getAlerts",
901
+ "tags": ["Wazuh-Indexer"]
902
}
1314
- },
1315
- "/velociraptor/artifacts/linux": {
903
+ },
904
+ "/wazuh_indexer/allocation": {
905
"get": {
1317
- "summary": "Get all linux artifacts",
1318
- "description": "Endpoint to get all linux artifacts.",
1319
- "responses": {
1320
- "200": {
1321
- "description": "Successful operation",
1322
- "content": {
1323
- "application/json": {
1324
- "schema": {
1325
- "type": "object",
1326
- "properties": {
1327
- "artifacts": {
1328
- "type": "array",
1329
- "items": {
1330
- "type": "object",
1331
- "description": "Artifact details"
1332
- }
1333
- }
1334
- }
1335
- }
906
+ "summary": "Get node allocation of the Wazuh-Indexer nodes",
907
+ "description": "Endpoint to get node allocation.",
908
+ "responses": {
909
+ "200": {
910
+ "description": "Successful operation",
911
+ "content": {
912
+ "application/json": {
913
+ "schema": {
914
+ "type": "object",
915
+ "properties": {
916
+ "indices": {
917
+ "type": "array",
918
+ "items": {
919
+ "type": "object",
920
+ "description": "Node details"
921
+ }
922
+ }
923
+ }
924
+ }
925
+ }
926
+ }
927
+ },
928
+ "default": {
929
+ "description": "Unexpected error",
930
+ "content": {
931
+ "application/json": {
932
+ "schema": {
933
+ "$ref": "#/components/schemas/Error"
934
+ }
935
+ }
936
+ }
937
}
1337
- }
938
},
1339
- "default": {
1340
- "description": "Unexpected error",
1341
- "content": {
1342
- "application/json": {
1343
- "schema": {
1344
- "$ref": "#/components/schemas/Error"
1345
- }
1346
- }
1347
- }
1348
- }
1349
- },
1350
- "operationId": "getAllLinuxArtifacts",
1351
- "tags": [
1352
- "Velociraptor"
1353
- ]
939
+ "operationId": "getNodeAllocation",
940
+ "tags": ["Wazuh-Indexer"]
941
}
1355
- },
1356
- "/velociraptor/artifacts/windows": {
942
+ },
943
+ "/wazuh_indexer/indices": {
944
"get": {
1358
- "summary": "Get all windows artifacts",
1359
- "description": "Endpoint to get all windows artifacts.",
1360
- "responses": {
1361
- "200": {
1362
- "description": "Successful operation",
1363
- "content": {
1364
- "application/json": {
1365
- "schema": {
1366
- "type": "object",
1367
- "properties": {
1368
- "artifacts": {
1369
- "type": "array",
1370
- "items": {
1371
- "type": "object",
1372
- "description": "Artifact details"
1373
- }
1374
- }
1375
- }
1376
- }
945
+ "summary": "Get indices from Wazuh-Indexer",
946
+ "description": "Endpoint to get indices from Wazuh-Indexer.",
947
+ "responses": {
948
+ "200": {
949
+ "description": "Successful operation",
950
+ "content": {
951
+ "application/json": {
952
+ "schema": {
953
+ "type": "object",
954
+ "properties": {
955
+ "indices": {
956
+ "type": "array",
957
+ "items": {
958
+ "type": "object",
959
+ "description": "Index details"
960
+ }
961
+ }
962
+ }
963
+ }
964
+ }
965
+ }
966
+ },
967
+ "default": {
968
+ "description": "Unexpected error",
969
+ "content": {
970
+ "application/json": {
971
+ "schema": {
972
+ "$ref": "#/components/schemas/Error"
973
+ }
974
+ }
975
+ }
976
}
1378
- }
977
},
1380
- "default": {
1381
- "description": "Unexpected error",
1382
- "content": {
1383
- "application/json": {
1384
- "schema": {
1385
- "$ref": "#/components/schemas/Error"
1386
- }
1387
- }
1388
- }
1389
- }
1390
- },
1391
- "operationId": "getAllWindowsArtifacts",
1392
- "tags": [
1393
- "Velociraptor"
1394
- ]
978
+ "operationId": "getWazuhIndices",
979
+ "tags": ["Wazuh-Indexer"]
980
}
1396
- },
1397
- "/velociraptor/artifacts/mac": {
981
+ },
982
+ "/wazuh_indexer/health": {
983
"get": {
1399
- "summary": "Get all mac artifacts",
1400
- "description": "Endpoint to get all mac artifacts.",
1401
- "responses": {
1402
- "200": {
1403
- "description": "Successful operation",
1404
- "content": {
1405
- "application/json": {
1406
- "schema": {
1407
- "type": "object",
1408
- "properties": {
1409
- "artifacts": {
1410
- "type": "array",
1411
- "items": {
1412
- "type": "object",
1413
- "description": "Artifact details"
1414
- }
1415
- }
1416
- }
1417
- }
1418
- }
1419
- }
1420
- },
1421
- "default": {
1422
- "description": "Unexpected error",
1423
- "content": {
1424
- "application/json": {
1425
- "schema": {
1426
- "$ref": "#/components/schemas/Error"
1427
- }
1428
- }
1429
- }
1430
- }
1431
- },
1432
- "operationId": "getAllMacArtifacts",
1433
- "tags": [
1434
- "Velociraptor"
1435
- ]
1436
- }
1437
- },
1438
- "/velociraptor/artifacts/collection": {
1439
- "post": {
1440
- "summary": "Create a new artifact collection",
1441
- "description": "Endpoint to create a new artifact collection.",
1442
- "requestBody": {
1443
- "description": "Artifact collection details",
1444
- "content": {
1445
- "application/json": {
1446
- "schema": {
1447
- "type": "object",
1448
- "properties": {
1449
- "artifact_name": {
1450
- "type": "string",
1451
- "description": "The name of the artifact collection."
1452
- },
1453
- "client_name": {
1454
- "type": "string",
1455
- "description": "The name of the client to collect the artifact for."
1456
- }
1457
- }
1458
- }
1459
- }
1460
- }
1461
- },
1462
- "responses": {
1463
- "200": {
1464
- "description": "Successful operation",
1465
- "content": {
1466
- "application/json": {
1467
- "schema": {
1468
- "type": "object",
1469
- "properties": {
1470
- "collection": {
1471
- "type": "object",
1472
- "description": "Artifact collection details"
1473
- }
984
+ "summary": "Get health of the Wazuh-Indexer nodes",
985
+ "description": "Endpoint to get health.",
986
+ "responses": {
987
+ "200": {
988
+ "description": "Successful operation",
989
+ "content": {
990
+ "application/json": {
991
+ "schema": {
992
+ "type": "object",
993
+ "properties": {
994
+ "cluster_name": {
995
+ "type": "string"
996
+ },
997
+ "status": {
998
+ "type": "string"
999
+ },
1000
+ "number_of_nodes": {
1001
+ "type": "integer"
1002
+ },
1003
+ "number_of_data_nodes": {
1004
+ "type": "integer"
1005
+ },
1006
+ "active_primary_shards": {
1007
+ "type": "integer"
1008
+ },
1009
+ "active_shards": {
1010
+ "type": "integer"
1011
+ },
1012
+ "relocating_shards": {
1013
+ "type": "integer"
1014
+ },
1015
+ "initializing_shards": {
1016
+ "type": "integer"
1017
+ },
1018
+ "unassigned_shards": {
1019
+ "type": "integer"
1020
+ },
1021
+ "delayed_unassigned_shards": {
1022
+ "type": "integer"
1023
+ },
1024
+ "number_of_pending_tasks": {
1025
+ "type": "integer"
1026
+ },
1027
+ "number_of_in_flight_fetch": {
1028
+ "type": "integer"
1029
+ },
1030
+ "task_max_waiting_in_queue_millis": {
1031
+ "type": "integer"
1032
+ },
1033
+ "active_shards_percent_as_number": {
1034
+ "type": "integer"
1035
+ }
1036
+ }
1037
+ }
1038
+ }
1039
+ }
1040
+ },
1041
+ "default": {
1042
+ "description": "Unexpected error",
1043
+ "content": {
1044
+ "application/json": {
1045
+ "schema": {
1046
+ "$ref": "#/components/schemas/Error"
1047
+ }
1048
+ }
1049
}
1475
- }
1050
}
1477
- }
1051
},
1479
- "default": {
1480
- "description": "Unexpected error",
1481
- "content": {
1482
- "application/json": {
1483
- "schema": {
1484
- "$ref": "#/components/schemas/Error"
1485
- }
1486
- }
1487
- }
1488
- }
1489
- },
1490
- "operationId": "createArtifactCollection",
1491
- "tags": [
1492
- "Velociraptor"
1493
- ]
1052
+ "operationId": "getHealth",
1053
+ "tags": ["Wazuh-Indexer"]
1054
}
1495
- },
1496
- "/dfir_iris/cases": {
1055
+ },
1056
+ "/wazuh_indexer/shards": {
1057
"get": {
1498
- "summary": "Get all cases",
1499
- "description": "Endpoint to get all cases.",
1500
- "responses": {
1501
- "200": {
1502
- "description": "Successful operation",
1503
- "content": {
1504
- "application/json": {
1505
- "schema": {
1506
- "type": "object",
1507
- "properties": {
1508
- "cases": {
1509
- "type": "array",
1510
- "items": {
1511
- "type": "object",
1512
- "description": "Case details"
1513
- }
1514
- }
1515
- }
1516
- }
1058
+ "summary": "Get shards from Wazuh-Indexer",
1059
+ "description": "Endpoint to get shards from Wazuh-Indexer.",
1060
+ "responses": {
1061
+ "200": {
1062
+ "description": "Successful operation",
1063
+ "content": {
1064
+ "application/json": {
1065
+ "schema": {
1066
+ "type": "object",
1067
+ "properties": {
1068
+ "shards": {
1069
+ "type": "array",
1070
+ "items": {
1071
+ "type": "object",
1072
+ "description": "Shard details"
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+ },
1080
+ "default": {
1081
+ "description": "Unexpected error",
1082
+ "content": {
1083
+ "application/json": {
1084
+ "schema": {
1085
+ "$ref": "#/components/schemas/Error"
1086
+ }
1087
+ }
1088
+ }
1089
}
1518
- }
1090
},
1520
- "default": {
1521
- "description": "Unexpected error",
1522
- "content": {
1523
- "application/json": {
1524
- "schema": {
1525
- "$ref": "#/components/schemas/Error"
1526
- }
1527
- }
1528
- }
1529
- }
1530
- },
1531
- "operationId": "getAllCases",
1532
- "tags": [
1533
- "DFIR Iris"
1534
- ]
1091
+ "operationId": "getShards",
1092
+ "tags": ["Wazuh-Indexer"]
1093
}
1536
- },
1537
- "/dfir_iris/cases/{case_id}": {
1094
+ },
1095
+ "/shuffle/workflows": {
1096
"get": {
1539
- "summary": "Get a case",
1540
- "description": "Endpoint to get a case.",
1541
- "parameters": [
1542
- {
1543
- "name": "case_id",
1544
- "in": "path",
1545
- "description": "The ID of the case.",
1546
- "required": true,
1547
- "schema": {
1548
- "type": "integer"
1549
- }
1550
- }
1551
- ],
1552
- "responses": {
1553
- "200": {
1554
- "description": "Successful operation",
1555
- "content": {
1556
- "application/json": {
1557
- "schema": {
1558
- "type": "object",
1559
- "properties": {
1560
- "case": {
1561
- "type": "object",
1562
- "description": "Case details"
1563
- }
1097
+ "summary": "Get all workflows",
1098
+ "description": "Endpoint to get all workflows.",
1099
+ "responses": {
1100
+ "200": {
1101
+ "description": "Successful operation",
1102
+ "content": {
1103
+ "application/json": {
1104
+ "schema": {
1105
+ "type": "object",
1106
+ "properties": {
1107
+ "workflows": {
1108
+ "type": "array",
1109
+ "items": {
1110
+ "type": "object",
1111
+ "description": "Workflow details"
1112
+ }
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+ }
1118
+ },
1119
+ "default": {
1120
+ "description": "Unexpected error",
1121
+ "content": {
1122
+ "application/json": {
1123
+ "schema": {
1124
+ "$ref": "#/components/schemas/Error"
1125
+ }
1126
+ }
1127
}
1565
- }
1566
- }
1567
- }
1568
- },
1569
- "404": {
1570
- "description": "Case not found",
1571
- "content": {
1572
- "application/json": {
1573
- "schema": {
1574
- "$ref": "#/components/schemas/Error"
1575
- }
1128
}
1577
- }
1129
},
1579
- "default": {
1580
- "description": "Unexpected error",
1581
- "content": {
1582
- "application/json": {
1583
- "schema": {
1584
- "$ref": "#/components/schemas/Error"
1585
- }
1586
- }
1587
- }
1588
- }
1589
- },
1590
- "operationId": "getCase",
1591
- "tags": [
1592
- "DFIR Iris"
1593
- ]
1130
+ "operationId": "getAllWorkflows",
1131
+ "tags": ["Shuffle"]
1132
}
1595
- },
1596
- "/dfir_iris/cases/{case_id}/notes": {
1133
+ },
1134
+ "/shuffle/workflows/executions": {
1135
"get": {
1598
- "summary": "Get all notes for a case",
1599
- "description": "Endpoint to get all notes for a case.",
1600
- "parameters": [
1601
- {
1602
- "name": "case_id",
1603
- "in": "path",
1604
- "description": "The ID of the case.",
1605
- "required": true,
1606
- "schema": {
1607
- "type": "integer"
1608
- }
1609
- }
1610
- ],
1611
- "responses": {
1612
- "200": {
1613
- "description": "Successful operation",
1614
- "content": {
1615
- "application/json": {
1616
- "schema": {
1617
- "type": "object",
1618
- "properties": {
1619
- "notes": {
1620
- "type": "array",
1621
- "items": {
1622
- "type": "object",
1623
- "description": "Note details"
1624
- }
1625
- }
1626
- }
1627
- }
1628
- }
1629
- }
1630
- },
1631
- "404": {
1632
- "description": "Case not found",
1633
- "content": {
1634
- "application/json": {
1635
- "schema": {
1636
- "$ref": "#/components/schemas/Error"
1637
- }
1136
+ "summary": "Get all workflow executions",
1137
+ "description": "Endpoint to get all workflow executions.",
1138
+ "responses": {
1139
+ "200": {
1140
+ "description": "Successful operation",
1141
+ "content": {
1142
+ "application/json": {
1143
+ "schema": {
1144
+ "type": "object",
1145
+ "properties": {
1146
+ "executions": {
1147
+ "type": "array",
1148
+ "items": {
1149
+ "type": "object",
1150
+ "description": "Workflow execution details"
1151
+ }
1152
+ }
1153
+ }
1154
+ }
1155
+ }
1156
+ }
1157
+ },
1158
+ "default": {
1159
+ "description": "Unexpected error",
1160
+ "content": {
1161
+ "application/json": {
1162
+ "schema": {
1163
+ "$ref": "#/components/schemas/Error"
1164
+ }
1165
+ }
1166
+ }
1167
}
1639
- }
1168
},
1641
- "default": {
1642
- "description": "Unexpected error",
1643
- "content": {
1644
- "application/json": {
1645
- "schema": {
1646
- "$ref": "#/components/schemas/Error"
1647
- }
1648
- }
1649
- }
1650
- }
1651
- },
1652
- "operationId": "getAllNotesForCase",
1653
- "tags": [
1654
- "DFIR Iris"
1655
- ]
1169
+ "operationId": "getAllWorkflowExecutions",
1170
+ "tags": ["Shuffle"]
1171
}
1657
- },
1658
- "/dfir_iris/cases/{case_id}/note": {
1659
- "post": {
1660
- "summary": "Create a note for a case",
1661
- "description": "Endpoint to create a note for a case.",
1662
- "parameters": [
1663
- {
1664
- "name": "case_id",
1665
- "in": "path",
1666
- "description": "The ID of the case.",
1667
- "required": true,
1668
- "schema": {
1669
- "type": "integer"
1670
- }
1671
- }
1672
- ],
1673
- "requestBody": {
1674
- "description": "Note details",
1675
- "content": {
1676
- "application/json": {
1677
- "schema": {
1678
- "type": "object",
1679
- "properties": {
1680
- "note_title": {
1681
- "type": "string",
1682
- "description": "The title of the note"
1683
- },
1684
- "note_content": {
1685
- "type": "string",
1686
- "description": "The content of the note"
1687
- }
1688
- },
1689
- "description": "Note details"
1172
+ },
1173
+ "/shuffle/workflows/executions/{workflow_id}": {
1174
+ "get": {
1175
+ "summary": "Get workflow executions by workflow id",
1176
+ "description": "Endpoint to get workflow executions by workflow id.",
1177
+ "parameters": [
1178
+ {
1179
+ "name": "workflow_id",
1180
+ "in": "path",
1181
+ "description": "The workflow id",
1182
+ "required": true,
1183
+ "schema": {
1184
+ "type": "string"
1185
+ }
1186
}
1691
- }
1692
- }
1693
- },
1694
- "responses": {
1695
- "200": {
1696
- "description": "Successful operation",
1697
- "content": {
1698
- "application/json": {
1699
- "schema": {
1700
- "type": "object",
1701
- "properties": {
1702
- "note": {
1703
- "type": "object",
1704
- "properties": {
1705
- "title": {
1706
- "type": "string",
1707
- "description": "The title of the note"
1708
- },
1709
- "content": {
1710
- "type": "string",
1711
- "description": "The content of the note"
1712
- }
1713
- },
1714
- "description": "Note details"
1715
- }
1716
- }
1717
- }
1187
+ ],
1188
+ "responses": {
1189
+ "200": {
1190
+ "description": "Successful operation",
1191
+ "content": {
1192
+ "application/json": {
1193
+ "schema": {
1194
+ "type": "object",
1195
+ "properties": {
1196
+ "executions": {
1197
+ "type": "array",
1198
+ "items": {
1199
+ "type": "object",
1200
+ "description": "Workflow execution details"
1201
+ }
1202
+ }
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+ },
1208
+ "default": {
1209
+ "description": "Unexpected error",
1210
+ "content": {
1211
+ "application/json": {
1212
+ "schema": {
1213
+ "$ref": "#/components/schemas/Error"
1214
+ }
1215
+ }
1216
+ }
1217
}
1719
- }
1218
},
1721
- "404": {
1722
- "description": "Case not found",
1723
- "content": {
1724
- "application/json": {
1725
- "schema": {
1726
- "$ref": "#/components/schemas/Error"
1727
- }
1219
+ "operationId": "getWorkflowExecutionsByWorkflowId",
1220
+ "tags": ["Shuffle"]
1221
+ }
1222
+ },
1223
+ "/velociraptor/artifacts": {
1224
+ "get": {
1225
+ "summary": "Get all artifacts",
1226
+ "description": "Endpoint to get all artifacts.",
1227
+ "responses": {
1228
+ "200": {
1229
+ "description": "Successful operation",
1230
+ "content": {
1231
+ "application/json": {
1232
+ "schema": {
1233
+ "type": "object",
1234
+ "properties": {
1235
+ "artifacts": {
1236
+ "type": "array",
1237
+ "items": {
1238
+ "type": "object",
1239
+ "description": "Artifact details"
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ },
1247
+ "default": {
1248
+ "description": "Unexpected error",
1249
+ "content": {
1250
+ "application/json": {
1251
+ "schema": {
1252
+ "$ref": "#/components/schemas/Error"
1253
+ }
1254
+ }
1255
+ }
1256
}
1729
- }
1257
},
1731
- "default": {
1732
- "description": "Unexpected error",
1733
- "content": {
1734
- "application/json": {
1735
- "schema": {
1736
- "$ref": "#/components/schemas/Error"
1737
- }
1738
- }
1739
- }
1740
- }
1741
- },
1742
- "operationId": "createNoteForCase",
1743
- "tags": [
1744
- "DFIR Iris"
1745
- ]
1258
+ "operationId": "getAllArtifacts",
1259
+ "tags": ["Velociraptor"]
1260
}
1747
- },
1748
- "/dfir_iris/cases/{case_id}/assets": {
1261
+ },
1262
+ "/velociraptor/artifacts/linux": {
1263
"get": {
1750
- "summary": "Get all assets for a case",
1751
- "description": "Endpoint to get all assets for a case.",
1752
- "parameters": [
1753
- {
1754
- "name": "case_id",
1755
- "in": "path",
1756
- "description": "The ID of the case.",
1757
- "required": true,
1758
- "schema": {
1759
- "type": "integer"
1760
- }
1761
- }
1762
- ],
1763
- "responses": {
1764
- "200": {
1765
- "description": "Successful operation",
1766
- "content": {
1767
- "application/json": {
1768
- "schema": {
1769
- "type": "object",
1770
- "properties": {
1771
- "assets": {
1772
- "type": "array",
1773
- "items": {
1774
- "type": "object",
1775
- "description": "Asset details"
1776
- }
1777
- }
1778
- }
1779
- }
1264
+ "summary": "Get all linux artifacts",
1265
+ "description": "Endpoint to get all linux artifacts.",
1266
+ "responses": {
1267
+ "200": {
1268
+ "description": "Successful operation",
1269
+ "content": {
1270
+ "application/json": {
1271
+ "schema": {
1272
+ "type": "object",
1273
+ "properties": {
1274
+ "artifacts": {
1275
+ "type": "array",
1276
+ "items": {
1277
+ "type": "object",
1278
+ "description": "Artifact details"
1279
+ }
1280
+ }
1281
+ }
1282
+ }
1283
+ }
1284
+ }
1285
+ },
1286
+ "default": {
1287
+ "description": "Unexpected error",
1288
+ "content": {
1289
+ "application/json": {
1290
+ "schema": {
1291
+ "$ref": "#/components/schemas/Error"
1292
+ }
1293
+ }
1294
+ }
1295
}
1781
- }
1296
},
1783
- "404": {
1784
- "description": "Case not found",
1785
- "content": {
1786
- "application/json": {
1787
- "schema": {
1788
- "$ref": "#/components/schemas/Error"
1789
- }
1297
+ "operationId": "getAllLinuxArtifacts",
1298
+ "tags": ["Velociraptor"]
1299
+ }
1300
+ },
1301
+ "/velociraptor/artifacts/windows": {
1302
+ "get": {
1303
+ "summary": "Get all windows artifacts",
1304
+ "description": "Endpoint to get all windows artifacts.",
1305
+ "responses": {
1306
+ "200": {
1307
+ "description": "Successful operation",
1308
+ "content": {
1309
+ "application/json": {
1310
+ "schema": {
1311
+ "type": "object",
1312
+ "properties": {
1313
+ "artifacts": {
1314
+ "type": "array",
1315
+ "items": {
1316
+ "type": "object",
1317
+ "description": "Artifact details"
1318
+ }
1319
+ }
1320
+ }
1321
+ }
1322
+ }
1323
+ }
1324
+ },
1325
+ "default": {
1326
+ "description": "Unexpected error",
1327
+ "content": {
1328
+ "application/json": {
1329
+ "schema": {
1330
+ "$ref": "#/components/schemas/Error"
1331
+ }
1332
+ }
1333
+ }
1334
}
1791
- }
1335
},
1793
- "default": {
1794
- "description": "Unexpected error",
1795
- "content": {
1796
- "application/json": {
1797
- "schema": {
1798
- "$ref": "#/components/schemas/Error"
1799
- }
1800
- }
1801
- }
1802
- }
1803
- },
1804
- "operationId": "getAllAssetsForCase",
1805
- "tags": [
1806
- "DFIR Iris"
1807
- ]
1336
+ "operationId": "getAllWindowsArtifacts",
1337
+ "tags": ["Velociraptor"]
1338
}
1809
- },
1810
- "/dfir_iris/alerts": {
1339
+ },
1340
+ "/velociraptor/artifacts/mac": {
1341
"get": {
1812
- "summary": "Get all alerts",
1813
- "description": "Endpoint to get all alerts.",
1814
- "parameters": [
1815
- {
1816
- "name": "limit",
1817
- "in": "query",
1818
- "description": "The maximum number of alerts to return.",
1819
- "required": false,
1820
- "schema": {
1821
- "type": "integer"
1822
- }
1823
- },
1824
- {
1825
- "name": "offset",
1826
- "in": "query",
1827
- "description": "The offset to start returning alerts from.",
1828
- "required": false,
1829
- "schema": {
1830
- "type": "integer"
1831
- }
1832
- },
1833
- {
1834
- "name": "sort",
1835
- "in": "query",
1836
- "description": "The field to sort alerts by.",
1837
- "required": false,
1838
- "schema": {
1839
- "type": "string"
1840
- }
1841
- },
1842
- {
1843
- "name": "order",
1844
- "in": "query",
1845
- "description": "The order to sort alerts by.",
1846
- "required": false,
1847
- "schema": {
1848
- "type": "string"
1849
- }
1850
- },
1851
- {
1852
- "name": "filter",
1853
- "in": "query",
1854
- "description": "The filter to apply to the alerts.",
1855
- "required": false,
1856
- "schema": {
1857
- "type": "string"
1858
- }
1859
- }
1860
- ],
1861
- "responses": {
1862
- "200": {
1863
- "description": "Successful operation",
1864
- "content": {
1865
- "application/json": {
1866
- "schema": {
1867
- "type": "object",
1868
- "properties": {
1869
- "alerts": {
1870
- "type": "array",
1871
- "items": {
1872
- "type": "object",
1873
- "description": "Alert details"
1874
- }
1875
- }
1876
- }
1877
- }
1342
+ "summary": "Get all mac artifacts",
1343
+ "description": "Endpoint to get all mac artifacts.",
1344
+ "responses": {
1345
+ "200": {
1346
+ "description": "Successful operation",
1347
+ "content": {
1348
+ "application/json": {
1349
+ "schema": {
1350
+ "type": "object",
1351
+ "properties": {
1352
+ "artifacts": {
1353
+ "type": "array",
1354
+ "items": {
1355
+ "type": "object",
1356
+ "description": "Artifact details"
1357
+ }
1358
+ }
1359
+ }
1360
+ }
1361
+ }
1362
+ }
1363
+ },
1364
+ "default": {
1365
+ "description": "Unexpected error",
1366
+ "content": {
1367
+ "application/json": {
1368
+ "schema": {
1369
+ "$ref": "#/components/schemas/Error"
1370
+ }
1371
+ }
1372
+ }
1373
}
1879
- }
1374
},
1881
- "default": {
1882
- "description": "Unexpected error",
1883
- "content": {
1884
- "application/json": {
1885
- "schema": {
1886
- "$ref": "#/components/schemas/Error"
1887
- }
1888
- }
1889
- }
1890
- }
1891
- },
1892
- "operationId": "getAllAlerts",
1893
- "tags": [
1894
- "DFIR Iris"
1895
- ]
1375
+ "operationId": "getAllMacArtifacts",
1376
+ "tags": ["Velociraptor"]
1377
}
1897
- }
1378
+ },
1379
+ "/velociraptor/artifacts/collection": {
1380
+ "post": {
1381
+ "summary": "Create a new artifact collection",
1382
+ "description": "Endpoint to create a new artifact collection.",
1383
+ "requestBody": {
1384
+ "description": "Artifact collection details",
1385
+ "content": {
1386
+ "application/json": {
1387
+ "schema": {
1388
+ "type": "object",
1389
+ "properties": {
1390
+ "artifact_name": {
1391
+ "type": "string",
1392
+ "description": "The name of the artifact collection."
1393
+ },
1394
+ "client_name": {
1395
+ "type": "string",
1396
+ "description": "The name of the client to collect the artifact for."
1397
+ }
1398
+ }
1399
+ }
1400
+ }
1401
+ }
1402
+ },
1403
+ "responses": {
1404
+ "200": {
1405
+ "description": "Successful operation",
1406
+ "content": {
1407
+ "application/json": {
1408
+ "schema": {
1409
+ "type": "object",
1410
+ "properties": {
1411
+ "collection": {
1412
+ "type": "object",
1413
+ "description": "Artifact collection details"
1414
+ }
1415
+ }
1416
+ }
1417
+ }
1418
+ }
1419
+ },
1420
+ "default": {
1421
+ "description": "Unexpected error",
1422
+ "content": {
1423
+ "application/json": {
1424
+ "schema": {
1425
+ "$ref": "#/components/schemas/Error"
1426
+ }
1427
+ }
1428
+ }
1429
+ }
1430
+ },
1431
+ "operationId": "createArtifactCollection",
1432
+ "tags": ["Velociraptor"]
1433
+ }
1434
+ },
1435
+ "/dfir_iris/cases": {
1436
+ "get": {
1437
+ "summary": "Get all cases",
1438
+ "description": "Endpoint to get all cases.",
1439
+ "responses": {
1440
+ "200": {
1441
+ "description": "Successful operation",
1442
+ "content": {
1443
+ "application/json": {
1444
+ "schema": {
1445
+ "type": "object",
1446
+ "properties": {
1447
+ "cases": {
1448
+ "type": "array",
1449
+ "items": {
1450
+ "type": "object",
1451
+ "description": "Case details"
1452
+ }
1453
+ }
1454
+ }
1455
+ }
1456
+ }
1457
+ }
1458
+ },
1459
+ "default": {
1460
+ "description": "Unexpected error",
1461
+ "content": {
1462
+ "application/json": {
1463
+ "schema": {
1464
+ "$ref": "#/components/schemas/Error"
1465
+ }
1466
+ }
1467
+ }
1468
+ }
1469
+ },
1470
+ "operationId": "getAllCases",
1471
+ "tags": ["DFIR Iris"]
1472
+ }
1473
+ },
1474
+ "/dfir_iris/cases/{case_id}": {
1475
+ "get": {
1476
+ "summary": "Get a case",
1477
+ "description": "Endpoint to get a case.",
1478
+ "parameters": [
1479
+ {
1480
+ "name": "case_id",
1481
+ "in": "path",
1482
+ "description": "The ID of the case.",
1483
+ "required": true,
1484
+ "schema": {
1485
+ "type": "integer"
1486
+ }
1487
+ }
1488
+ ],
1489
+ "responses": {
1490
+ "200": {
1491
+ "description": "Successful operation",
1492
+ "content": {
1493
+ "application/json": {
1494
+ "schema": {
1495
+ "type": "object",
1496
+ "properties": {
1497
+ "case": {
1498
+ "type": "object",
1499
+ "description": "Case details"
1500
+ }
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1505
+ },
1506
+ "404": {
1507
+ "description": "Case not found",
1508
+ "content": {
1509
+ "application/json": {
1510
+ "schema": {
1511
+ "$ref": "#/components/schemas/Error"
1512
+ }
1513
+ }
1514
+ }
1515
+ },
1516
+ "default": {
1517
+ "description": "Unexpected error",
1518
+ "content": {
1519
+ "application/json": {
1520
+ "schema": {
1521
+ "$ref": "#/components/schemas/Error"
1522
+ }
1523
+ }
1524
+ }
1525
+ }
1526
+ },
1527
+ "operationId": "getCase",
1528
+ "tags": ["DFIR Iris"]
1529
+ }
1530
+ },
1531
+ "/dfir_iris/cases/{case_id}/notes": {
1532
+ "get": {
1533
+ "summary": "Get all notes for a case",
1534
+ "description": "Endpoint to get all notes for a case.",
1535
+ "parameters": [
1536
+ {
1537
+ "name": "case_id",
1538
+ "in": "path",
1539
+ "description": "The ID of the case.",
1540
+ "required": true,
1541
+ "schema": {
1542
+ "type": "integer"
1543
+ }
1544
+ }
1545
+ ],
1546
+ "responses": {
1547
+ "200": {
1548
+ "description": "Successful operation",
1549
+ "content": {
1550
+ "application/json": {
1551
+ "schema": {
1552
+ "type": "object",
1553
+ "properties": {
1554
+ "notes": {
1555
+ "type": "array",
1556
+ "items": {
1557
+ "type": "object",
1558
+ "description": "Note details"
1559
+ }
1560
+ }
1561
+ }
1562
+ }
1563
+ }
1564
+ }
1565
+ },
1566
+ "404": {
1567
+ "description": "Case not found",
1568
+ "content": {
1569
+ "application/json": {
1570
+ "schema": {
1571
+ "$ref": "#/components/schemas/Error"
1572
+ }
1573
+ }
1574
+ }
1575
+ },
1576
+ "default": {
1577
+ "description": "Unexpected error",
1578
+ "content": {
1579
+ "application/json": {
1580
+ "schema": {
1581
+ "$ref": "#/components/schemas/Error"
1582
+ }
1583
+ }
1584
+ }
1585
+ }
1586
+ },
1587
+ "operationId": "getAllNotesForCase",
1588
+ "tags": ["DFIR Iris"]
1589
+ }
1590
+ },
1591
+ "/dfir_iris/cases/{case_id}/note": {
1592
+ "post": {
1593
+ "summary": "Create a note for a case",
1594
+ "description": "Endpoint to create a note for a case.",
1595
+ "parameters": [
1596
+ {
1597
+ "name": "case_id",
1598
+ "in": "path",
1599
+ "description": "The ID of the case.",
1600
+ "required": true,
1601
+ "schema": {
1602
+ "type": "integer"
1603
+ }
1604
+ }
1605
+ ],
1606
+ "requestBody": {
1607
+ "description": "Note details",
1608
+ "content": {
1609
+ "application/json": {
1610
+ "schema": {
1611
+ "type": "object",
1612
+ "properties": {
1613
+ "note_title": {
1614
+ "type": "string",
1615
+ "description": "The title of the note"
1616
+ },
1617
+ "note_content": {
1618
+ "type": "string",
1619
+ "description": "The content of the note"
1620
+ }
1621
+ },
1622
+ "description": "Note details"
1623
+ }
1624
+ }
1625
+ }
1626
+ },
1627
+ "responses": {
1628
+ "200": {
1629
+ "description": "Successful operation",
1630
+ "content": {
1631
+ "application/json": {
1632
+ "schema": {
1633
+ "type": "object",
1634
+ "properties": {
1635
+ "note": {
1636
+ "type": "object",
1637
+ "properties": {
1638
+ "title": {
1639
+ "type": "string",
1640
+ "description": "The title of the note"
1641
+ },
1642
+ "content": {
1643
+ "type": "string",
1644
+ "description": "The content of the note"
1645
+ }
1646
+ },
1647
+ "description": "Note details"
1648
+ }
1649
+ }
1650
+ }
1651
+ }
1652
+ }
1653
+ },
1654
+ "404": {
1655
+ "description": "Case not found",
1656
+ "content": {
1657
+ "application/json": {
1658
+ "schema": {
1659
+ "$ref": "#/components/schemas/Error"
1660
+ }
1661
+ }
1662
+ }
1663
+ },
1664
+ "default": {
1665
+ "description": "Unexpected error",
1666
+ "content": {
1667
+ "application/json": {
1668
+ "schema": {
1669
+ "$ref": "#/components/schemas/Error"
1670
+ }
1671
+ }
1672
+ }
1673
+ }
1674
+ },
1675
+ "operationId": "createNoteForCase",
1676
+ "tags": ["DFIR Iris"]
1677
+ }
1678
+ },
1679
+ "/dfir_iris/cases/{case_id}/assets": {
1680
+ "get": {
1681
+ "summary": "Get all assets for a case",
1682
+ "description": "Endpoint to get all assets for a case.",
1683
+ "parameters": [
1684
+ {
1685
+ "name": "case_id",
1686
+ "in": "path",
1687
+ "description": "The ID of the case.",
1688
+ "required": true,
1689
+ "schema": {
1690
+ "type": "integer"
1691
+ }
1692
+ }
1693
+ ],
1694
+ "responses": {
1695
+ "200": {
1696
+ "description": "Successful operation",
1697
+ "content": {
1698
+ "application/json": {
1699
+ "schema": {
1700
+ "type": "object",
1701
+ "properties": {
1702
+ "assets": {
1703
+ "type": "array",
1704
+ "items": {
1705
+ "type": "object",
1706
+ "description": "Asset details"
1707
+ }
1708
+ }
1709
+ }
1710
+ }
1711
+ }
1712
+ }
1713
+ },
1714
+ "404": {
1715
+ "description": "Case not found",
1716
+ "content": {
1717
+ "application/json": {
1718
+ "schema": {
1719
+ "$ref": "#/components/schemas/Error"
1720
+ }
1721
+ }
1722
+ }
1723
+ },
1724
+ "default": {
1725
+ "description": "Unexpected error",
1726
+ "content": {
1727
+ "application/json": {
1728
+ "schema": {
1729
+ "$ref": "#/components/schemas/Error"
1730
+ }
1731
+ }
1732
+ }
1733
+ }
1734
+ },
1735
+ "operationId": "getAllAssetsForCase",
1736
+ "tags": ["DFIR Iris"]
1737
+ }
1738
+ },
1739
+ "/dfir_iris/alerts": {
1740
+ "get": {
1741
+ "summary": "Get all alerts",
1742
+ "description": "Endpoint to get all alerts.",
1743
+ "parameters": [
1744
+ {
1745
+ "name": "limit",
1746
+ "in": "query",
1747
+ "description": "The maximum number of alerts to return.",
1748
+ "required": false,
1749
+ "schema": {
1750
+ "type": "integer"
1751
+ }
1752
+ },
1753
+ {
1754
+ "name": "offset",
1755
+ "in": "query",
1756
+ "description": "The offset to start returning alerts from.",
1757
+ "required": false,
1758
+ "schema": {
1759
+ "type": "integer"
1760
+ }
1761
+ },
1762
+ {
1763
+ "name": "sort",
1764
+ "in": "query",
1765
+ "description": "The field to sort alerts by.",
1766
+ "required": false,
1767
+ "schema": {
1768
+ "type": "string"
1769
+ }
1770
+ },
1771
+ {
1772
+ "name": "order",
1773
+ "in": "query",
1774
+ "description": "The order to sort alerts by.",
1775
+ "required": false,
1776
+ "schema": {
1777
+ "type": "string"
1778
+ }
1779
+ },
1780
+ {
1781
+ "name": "filter",
1782
+ "in": "query",
1783
+ "description": "The filter to apply to the alerts.",
1784
+ "required": false,
1785
+ "schema": {
1786
+ "type": "string"
1787
+ }
1788
+ }
1789
+ ],
1790
+ "responses": {
1791
+ "200": {
1792
+ "description": "Successful operation",
1793
+ "content": {
1794
+ "application/json": {
1795
+ "schema": {
1796
+ "type": "object",
1797
+ "properties": {
1798
+ "alerts": {
1799
+ "type": "array",
1800
+ "items": {
1801
+ "type": "object",
1802
+ "description": "Alert details"
1803
+ }
1804
+ }
1805
+ }
1806
+ }
1807
+ }
1808
+ }
1809
+ },
1810
+ "default": {
1811
+ "description": "Unexpected error",
1812
+ "content": {
1813
+ "application/json": {
1814
+ "schema": {
1815
+ "$ref": "#/components/schemas/Error"
1816
+ }
1817
+ }
1818
+ }
1819
+ }
1820
+ },
1821
+ "operationId": "getAllAlerts",
1822
+ "tags": ["DFIR Iris"]
1823
+ }
1824
+ }
1825
},
1826
"components": {
1827
"schemas": {
@@ -1931,15 +1858,8 @@
1858
"description": "The operating system of the agent."
1859
}
1860
},
1934
- "required": [
1935
- "agent_id",
1936
- "hostname",
1937
- "id",
1938
- "ip_address",
1939
- "last_seen",
1940
- "os"
1941
- ]
1861
+ "required": ["agent_id", "hostname", "id", "ip_address", "last_seen", "os"]
1862
}
1863
}
1944
- }
1864
+ }
1865
}
backend/copilot.py
+3
-4
@@ -1,9 +1,8 @@
1
-from flask import Flask
2
-from app import db
3
-from app import app
4
-
1
+# from flask import Flask
2
from loguru import logger
3
4
+from app import app
5
+from app import db
6
7
logger.add(
8
"debug.log",
backend/requirements.txt
new
+108
@@ -0,0 +1,108 @@
1
+aiohttp==3.8.4
2
+aiosignal==1.3.1
3
+alembic==1.11.1
4
+antlr4-python3-runtime==4.9.3
5
+appdirs==1.4.4
6
+arrow==1.2.3
7
+async-timeout==4.0.2
8
+attrs==21.4.0
9
+blinker==1.6.2
10
+blueprint==3.4.2
11
+cattrs==23.1.2
12
+certifi==2023.5.7
13
+cffi==1.15.1
14
+charset-normalizer==3.2.0
15
+click==8.1.4
16
+colorama==0.4.6
17
+colour==0.1.5
18
+cpe==1.2.1
19
+cryptography==41.0.1
20
+cybox==2.1.0.21
21
+deepdiff==6.3.1
22
+drawsvg==2.2.0
23
+elasticsearch7==7.10.1
24
+environs==9.5.0
25
+et-xmlfile==1.1.0
26
+Flask==2.3.2
27
+Flask-Cors==4.0.0
28
+flask-marshmallow==0.15.0
29
+Flask-Migrate==4.0.4
30
+Flask-SQLAlchemy==3.0.5
31
+flask-swagger-ui==4.11.1
32
+fqdn==1.5.1
33
+frozenlist==1.3.3
34
+greenlet==2.0.2
35
+grpcio==1.56.0
36
+grpcio-tools==1.56.0
37
+idna==3.4
38
+isoduration==20.11.0
39
+itsdangerous==2.1.2
40
+Jinja2==3.1.2
41
+jsonpointer==2.4
42
+jsonschema==4.17.3
43
+loguru==0.7.0
44
+lxml==4.9.3
45
+maec==4.1.0.17
46
+Mako==1.2.4
47
+Markdown==3.4.3
48
+markdown-it-py==3.0.0
49
+MarkupSafe==2.1.3
50
+marshmallow==3.19.0
51
+marshmallow-sqlalchemy==0.29.0
52
+mdurl==0.1.2
53
+mitreattack-python==2.0.14
54
+mixbox==1.0.5
55
+multidict==6.0.4
56
+netaddr==0.8.0
57
+numpy==1.25.1
58
+openai==0.27.8
59
+openpyxl==3.1.2
60
+ordered-set==4.1.0
61
+packaging==23.1
62
+pandas==2.0.3
63
+pika==1.3.2
64
+Pillow==10.0.0
65
+platformdirs==3.8.1
66
+pluralizer==1.2.0
67
+pooch==1.7.0
68
+protobuf==4.23.4
69
+psycopg2-binary==2.9.6
70
+pycountry==22.3.5
71
+pycparser==2.21
72
+Pygments==2.15.1
73
+pyrsistent==0.19.3
74
+python-dateutil==2.8.2
75
+python-dotenv==1.0.0
76
+pytz==2023.3
77
+pyvelociraptor==0.1.8
78
+PyYAML==6.0
79
+requests==2.31.0
80
+requests-cache==1.1.0
81
+rfc3339-validator==0.1.4
82
+rfc3986-validator==0.1.1
83
+rich==13.4.2
84
+simplejson==3.19.1
85
+six==1.16.0
86
+SQLAlchemy==2.0.18
87
+stix==1.2.0.11
88
+stix2==3.0.1
89
+stix2-elevator==4.1.7
90
+stix2-patterns==2.0.0
91
+stix2-validator==3.1.3
92
+stixmarx==1.0.8
93
+tabulate==0.9.0
94
+taxii2-client==2.3.0
95
+tqdm==4.65.0
96
+typer==0.9.0
97
+typing_extensions==4.7.1
98
+tzdata==2023.3
99
+uri-template==1.3.0
100
+url-normalize==1.4.3
101
+urllib3==1.26.16
102
+weakrefmethod==1.0.3
103
+webcolors==1.13
104
+Werkzeug==2.3.6
105
+win32-setctime==1.1.0
106
+XlsxWriter==3.1.2
107
+xmltodict==0.13.0
108
+yarl==1.9.2
backend/settings.py
+2
-1
@@ -20,6 +20,7 @@ DEBUG = env.bool("FLASK_DEBUG", default=False)
20
SECRET_KEY = env.str("SECRET_KEY", "not-a-secret")
21
SQLALCHEMY_DATABASE_URI = env.str("SQLALCHEMY_DATABASE_URI", f"sqlite:///{db_path}")
22
SQLALCHEMY_TRACK_MODIFICATIONS = env.bool(
23
- "SQLALCHEMY_TRACK_MODIFICATIONS", default=False
23
+ "SQLALCHEMY_TRACK_MODIFICATIONS",
24
+ default=False,
25
)
26
UPLOAD_FOLDER = env.str("UPLOAD_FOLDER", str(Path.home() / "Desktop/copilot_uploads"))