@cryptotaxi247 / CoPilot / commits / b2bb1fb6

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"))