From 1b3c6f437c8b9c88934c9be8ebd4442eb8456181 Mon Sep 17 00:00:00 2001 From: taylor_socfortress <111797488+taylorwalton@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:58:18 -0600 Subject: [PATCH] Mkdocs continued (#7) * wazuh manager mkdocs * rest of services mkdocs --- backend/app/routes/dfir_iris.py | 24 +- backend/app/routes/rules.py | 60 +- backend/app/routes/shuffle.py | 19 +- backend/app/routes/wazuhindexer.py | 60 +- backend/app/services/DFIR_IRIS/alerts.py | 16 +- backend/app/services/DFIR_IRIS/assets.py | 20 +- backend/app/services/DFIR_IRIS/notes.py | 57 +- backend/app/services/DFIR_IRIS/universal.py | 36 +- backend/app/services/Shuffle/__init__.py | 0 backend/app/services/Shuffle/universal.py | 2 +- backend/app/services/Shuffle/workflows.py | 65 +- backend/app/services/WazuhIndexer/cluster.py | 57 +- backend/app/services/WazuhIndexer/index.py | 32 +- backend/app/services/WazuhManager/__init__.py | 0 backend/app/services/WazuhManager/agent.py | 41 +- .../services/WazuhManager/disabled_rule.py | 120 +- .../app/services/WazuhManager/enabled_rule.py | 97 +- .../app/services/WazuhManager/universal.py | 37 +- .../services/WazuhManager/vulnerability.py | 25 +- backend/docs/alerts.md | 10 - backend/docs/cases.md | 15 - backend/docs/dfiriris.md | 53 + backend/docs/shuffle.md | 15 + .../docs/{artifacts.md => velociraptor.md} | 2 +- backend/docs/wazuhindexer.md | 25 + backend/docs/wazuhmanager.md | 22 + backend/mkdocs.yml | 8 +- backend/site/404.html | 764 +- backend/site/agents/index.html | 4682 +++--- backend/site/alerts/index.html | 1785 +- backend/site/artifacts/index.html | 3353 ++-- backend/site/assets/_mkdocstrings.css | 37 +- .../assets/javascripts/bundle.220ee61c.min.js | 6358 +------- .../javascripts/lunr/min/lunr.ar.min.js | 235 +- .../javascripts/lunr/min/lunr.da.min.js | 205 +- .../javascripts/lunr/min/lunr.de.min.js | 242 +- .../javascripts/lunr/min/lunr.du.min.js | 289 +- .../javascripts/lunr/min/lunr.es.min.js | 528 +- .../javascripts/lunr/min/lunr.fi.min.js | 409 +- .../javascripts/lunr/min/lunr.fr.min.js | 536 +- .../javascripts/lunr/min/lunr.hi.min.js | 51 +- .../javascripts/lunr/min/lunr.hu.min.js | 474 +- .../javascripts/lunr/min/lunr.hy.min.js | 38 +- .../javascripts/lunr/min/lunr.it.min.js | 516 +- .../javascripts/lunr/min/lunr.ja.min.js | 78 +- .../javascripts/lunr/min/lunr.jp.min.js | 2 +- .../javascripts/lunr/min/lunr.kn.min.js | 51 +- .../javascripts/lunr/min/lunr.ko.min.js | 38 +- .../javascripts/lunr/min/lunr.multi.min.js | 34 +- .../javascripts/lunr/min/lunr.nl.min.js | 286 +- .../javascripts/lunr/min/lunr.no.min.js | 185 +- .../javascripts/lunr/min/lunr.pt.min.js | 464 +- .../javascripts/lunr/min/lunr.ro.min.js | 509 +- .../javascripts/lunr/min/lunr.ru.min.js | 291 +- .../javascripts/lunr/min/lunr.sa.min.js | 51 +- .../lunr/min/lunr.stemmer.support.min.js | 192 +- .../javascripts/lunr/min/lunr.sv.min.js | 198 +- .../javascripts/lunr/min/lunr.ta.min.js | 51 +- .../javascripts/lunr/min/lunr.te.min.js | 51 +- .../javascripts/lunr/min/lunr.th.min.js | 39 +- .../javascripts/lunr/min/lunr.tr.min.js | 702 +- .../javascripts/lunr/min/lunr.vi.min.js | 23 +- .../javascripts/lunr/min/lunr.zh.min.js | 65 +- .../site/assets/javascripts/lunr/tinyseg.js | 1625 +- .../site/assets/javascripts/lunr/wordcut.js | 13514 ++++++++-------- .../workers/search.74e28a9f.min.js | 1903 +-- .../assets/stylesheets/main.26e3688c.min.css | 4847 +----- .../stylesheets/main.26e3688c.min.css.map | 2 +- .../stylesheets/palette.ecc896b0.min.css | 404 +- .../stylesheets/palette.ecc896b0.min.css.map | 2 +- backend/site/cases/index.html | 3002 ++-- backend/site/connectors/index.html | 6108 ++++--- backend/site/graylog/index.html | 4482 +++-- backend/site/index.html | 1656 +- backend/{docs => site}/mkdocs.yml | 0 backend/site/objects.inv | Bin 1329 -> 1386 bytes backend/site/sitemap.xml | 2 +- backend/site/sitemap.xml.gz | Bin 127 -> 127 bytes backend/site/wazuhmanager/index.html | 1969 +++ 79 files changed, 22244 insertions(+), 42002 deletions(-) create mode 100644 backend/app/services/Shuffle/__init__.py create mode 100644 backend/app/services/WazuhManager/__init__.py delete mode 100644 backend/docs/alerts.md delete mode 100644 backend/docs/cases.md create mode 100644 backend/docs/dfiriris.md create mode 100644 backend/docs/shuffle.md rename backend/docs/{artifacts.md => velociraptor.md} (91%) create mode 100644 backend/docs/wazuhindexer.md create mode 100644 backend/docs/wazuhmanager.md rename backend/{docs => site}/mkdocs.yml (100%) create mode 100644 backend/site/wazuhmanager/index.html diff --git a/backend/app/routes/dfir_iris.py b/backend/app/routes/dfir_iris.py index f6dc494f..7b796fe9 100644 --- a/backend/app/routes/dfir_iris.py +++ b/backend/app/routes/dfir_iris.py @@ -12,10 +12,10 @@ @bp.route("/dfir_iris/cases", methods=["GET"]) def get_cases(): """ - Endpoint to retrieve all the cases from DFIR IRIS. + Handle GET requests at the "/cases" endpoint. Retrieve all cases from DFIR IRIS. Returns: - json: A JSON response containing the list of cases. + Response: A Flask Response object carrying a JSON representation of the list of cases. """ service = CasesService() cases = service.list_cases() @@ -25,13 +25,13 @@ def get_cases(): @bp.route("/dfir_iris/cases/", methods=["GET"]) def get_case(case_id: str): """ - Endpoint to retrieve a specific case from DFIR IRIS. + Handle GET requests at the "/cases/" endpoint. Retrieve a specific case from DFIR IRIS. Args: case_id (str): The ID of the case to retrieve. Returns: - json: A JSON response containing the case data. + Response: A Flask Response object carrying a JSON representation of the case data. """ service = CasesService() case = service.get_case(case_id=case_id) @@ -41,13 +41,13 @@ def get_case(case_id: str): @bp.route("/dfir_iris/cases//notes", methods=["GET"]) def get_case_notes(case_id: int): """ - Endpoint to retrieve notes of a specific case from DFIR IRIS. + Handle GET requests at the "/cases//notes" endpoint. Retrieve notes of a specific case from DFIR IRIS. Args: case_id (str): The ID of the case to retrieve notes from. Returns: - json: A JSON response containing the list of notes for the case. + Response: A Flask Response object carrying a JSON representation of the list of notes for the case. """ notes_service = NotesService() notes = notes_service.get_case_notes(search_term="%", cid=case_id) @@ -57,13 +57,13 @@ def get_case_notes(case_id: int): @bp.route("/dfir_iris/cases//note", methods=["POST"]) def create_case_note(case_id: str): """ - Endpoint to create a note for a specific case in DFIR IRIS. + Handle POST requests at the "/cases//note" endpoint. Create a note for a specific case in DFIR IRIS. Args: case_id (str): The ID of the case to create a note for. Returns: - json: A JSON response containing the result of the note creation operation. + Response: A Flask Response object carrying a JSON representation of the result of the note creation operation. """ note_title = request.json["note_title"] note_content = request.json["note_content"] @@ -79,13 +79,13 @@ def create_case_note(case_id: str): @bp.route("/dfir_iris/cases//assets", methods=["GET"]) def get_case_assets(case_id: str): """ - Endpoint to retrieve assets of a specific case from DFIR IRIS. + Handle GET requests at the "/cases//assets" endpoint. Retrieve assets of a specific case from DFIR IRIS. Args: case_id (str): The ID of the case to retrieve assets from. Returns: - json: A JSON response containing the list of assets for the case. + Response: A Flask Response object carrying a JSON representation of the list of assets for the case. """ asset_service = AssetsService() assets = asset_service.get_case_assets(cid=case_id) @@ -95,10 +95,10 @@ def get_case_assets(case_id: str): @bp.route("/dfir_iris/alerts", methods=["GET"]) def get_alerts(): """ - Endpoint to retrieve all alerts from DFIR IRIS. + Handle GET requests at the "/alerts" endpoint. Retrieve all alerts from DFIR IRIS. Returns: - json: A JSON response containing the list of alerts. + Response: A Flask Response object carrying a JSON representation of the list of alerts. """ service = AlertsService() alerts = service.list_alerts() diff --git a/backend/app/routes/rules.py b/backend/app/routes/rules.py index 23e7ba5b..bf8cb303 100644 --- a/backend/app/routes/rules.py +++ b/backend/app/routes/rules.py @@ -2,10 +2,9 @@ from flask import Blueprint from flask import request from loguru import logger +from typing import Any +from typing import Dict -# from app.models.connectors import Connector -# from app.models.connectors import WazuhManagerConnector -# from app.models.rules import DisabledRules from app.services.WazuhManager.disabled_rule import DisableRuleService from app.services.WazuhManager.enabled_rule import EnableRuleService from app.services.WazuhManager.universal import UniversalService @@ -14,47 +13,50 @@ bp = Blueprint("rules", __name__) - @bp.route("/rule/disable", methods=["POST"]) -def disable_rule(): +def disable_rule() -> str: """ - Endpoint to disable a rule. + Flask route to disable a rule in Wazuh. - Args: - id (str): The id of the rule to be disabled. + This endpoint accepts a POST request with a JSON body containing the rule to be disabled. Returns: - json: A JSON response containing the updated rule information. + str: A JSON string response containing the updated rule information. The actual content of the response depends on the implementation of `DisableRuleService.disable_rule`. + + Example Request Body: + { + "rule_id": "200222", + "reason": "string", + "length_of_time": 1 + } """ logger.info("Received request to disable rule") - data = request.get_json() - # wazuh_manager_connector = WazuhManagerConnector("Wazuh-Manager") - # wazuh_manager_service = WazuhManagerService(wazuh_manager_connector) - # result = wazuh_manager_service.disable_rule(data) - # Create instance of UniversalService - universal_service = UniversalService() - disable_service = DisableRuleService(universal_service) - result = disable_service.disable_rule(data) + data: Dict[str, Any] = request.get_json() + universal_service: UniversalService = UniversalService() + disable_service: DisableRuleService = DisableRuleService(universal_service) + result: str = disable_service.disable_rule(data) return result @bp.route("/rule/enable", methods=["POST"]) -def enable_rule(): +def enable_rule() -> str: """ - Endpoint to enable a rule. + Flask route to enable a rule in Wazuh. - Args: - id (str): The id of the rule to be enabled. + This endpoint accepts a POST request with a JSON body containing the rule to be enabled. Returns: - json: A JSON response containing the updated rule information. + str: A JSON string response containing the updated rule information. The actual content of the response depends on the implementation of `EnableRuleService.enable_rule`. + + Example Request Body: + { + "rule_id": "100001" + } """ logger.info("Received request to enable rule") - data = request.get_json() - # wazuh_manager_connector = WazuhManagerConnector("Wazuh-Manager") - # wazuh_manager_service = WazuhManagerService(wazuh_manager_connector) - # result = wazuh_manager_service.enable_rule(data) - universal_service = UniversalService() - enable_service = EnableRuleService(universal_service) - result = enable_service.enable_rule(data) + data: Dict[str, Any] = request.get_json() + universal_service: UniversalService = UniversalService() + enable_service: EnableRuleService = EnableRuleService(universal_service) + result: str = enable_service.enable_rule(data) return result + diff --git a/backend/app/routes/shuffle.py b/backend/app/routes/shuffle.py index 91ab3bc7..7efaaa38 100644 --- a/backend/app/routes/shuffle.py +++ b/backend/app/routes/shuffle.py @@ -15,12 +15,12 @@ @bp.route("/shuffle/workflows", methods=["GET"]) -def get_workflows(): +def get_workflows() -> jsonify: """ Endpoint to list all available Shuffle workflows. Returns: - json: A JSON response containing the list of all configured Workflows. + jsonify: A JSON response containing the list of all configured Workflows in Shuffle. """ service = WorkflowsService() workflows = service.collect_workflows() @@ -28,12 +28,14 @@ def get_workflows(): @bp.route("/shuffle/workflows/executions", methods=["GET"]) -def get_workflows_executions(): +def get_workflows_executions() -> jsonify: """ Endpoint to list all available Shuffle workflow execution status. + This endpoint retrieves the status of the most recent execution for each workflow in Shuffle. + Returns: - json: A JSON response containing the list of all configured workflows last execution status. + jsonify: A JSON response containing the list of all configured workflows and their last execution status. """ service = WorkflowsService() workflow_details = service.collect_workflow_details() @@ -48,12 +50,17 @@ def get_workflows_executions(): @bp.route("/shuffle/workflows/executions/", methods=["GET"]) -def get_workflow_executions(workflow_id): +def get_workflow_executions(workflow_id: str) -> jsonify: """ Endpoint to list execution status of a specified Shuffle workflow. + This endpoint retrieves the status of the most recent execution for a specific workflow in Shuffle. + + Args: + workflow_id (str): The ID of the workflow to retrieve the execution status for. + Returns: - json: A JSON response containing the last execution status of the specified workflow. + jsonify: A JSON response containing the last execution status of the specified workflow. """ service = WorkflowsService() workflow_details = service.collect_workflow_executions_status(workflow_id) diff --git a/backend/app/routes/wazuhindexer.py b/backend/app/routes/wazuhindexer.py index 421c5033..7640de73 100644 --- a/backend/app/routes/wazuhindexer.py +++ b/backend/app/routes/wazuhindexer.py @@ -1,37 +1,25 @@ from flask import Blueprint -# from app.models.connectors import Connector -# from app.models.connectors import WazuhManagerConnector -# from app.services.agents.agents import AgentService -# from app.services.agents.agents import AgentSyncService -# from app.services.WazuhIndexer.alerts import AlertsService from app.services.WazuhIndexer.cluster import ClusterService from app.services.WazuhIndexer.index import IndexService -# from flask import jsonify -# from flask import request -# from loguru import logger - - bp = Blueprint("wazuh_indexer", __name__) @bp.route("/wazuh_indexer/indices", methods=["GET"]) def get_indices_summary(): """ - Endpoint to list all available indices and collect. - { - "index": index["index"], - "health": index["health"], - "docs_count": index["docs.count"], - "store_size": index["store.size"], - "replica_count": index["rep"], - }, - It processes each alert to verify the connection and returns the results. + HTTP GET endpoint to list all available indices and collect relevant information for each. + + This includes: + - Index name + - Index health status + - Document count in the index + - Size of the index + - Number of replicas for the index Returns: - json: A JSON response containing the list of all available indices along with their connection verification - status. + json: A JSON response containing a list of all available indices along with their respective details. """ service = IndexService() indices = service.collect_indices_summary() @@ -41,19 +29,17 @@ def get_indices_summary(): @bp.route("/wazuh_indexer/allocation", methods=["GET"]) def get_node_allocation(): """ - Endpoint to list all available indices allocation. - Returns: - { - "disk_used": index["disk.used"], - "disk_available": index["disk.avail"], - "disk_total": index["disk.total"], - "disk_percent": index["disk.percent"], - "node": index["node"], - }, + HTTP GET endpoint to list all available indices allocation. + + This includes: + - Disk space used by the index + - Available disk space + - Total disk space + - Disk usage percentage + - Node on which the index resides Returns: - json: A JSON response containing the list of all available alerts along with their connection verification - status. + json: A JSON response containing a list of all available indices along with their respective allocation details. """ service = ClusterService() indices = service.collect_node_allocation() @@ -63,11 +49,10 @@ def get_node_allocation(): @bp.route("/wazuh_indexer/health", methods=["GET"]) def get_cluster_health(): """ - Endpoint to collect Wazuh-Indexer cluster health. + HTTP GET endpoint to collect Wazuh-Indexer cluster health information. Returns: - json: A JSON response containing the list of all available alerts along with their connection verification - status. + json: A JSON response containing health information for the Wazuh-Indexer cluster. """ service = ClusterService() indices = service.collect_cluster_health() @@ -77,11 +62,10 @@ def get_cluster_health(): @bp.route("/wazuh_indexer/shards", methods=["GET"]) def get_shards(): """ - Endpoint to collect Wazuh-Indexer shards. + HTTP GET endpoint to collect information about Wazuh-Indexer shards. Returns: - json: A JSON response containing the list of all available alerts along with their connection verification - status. + json: A JSON response containing information about the shards in the Wazuh-Indexer. """ service = ClusterService() indices = service.collect_shards() diff --git a/backend/app/services/DFIR_IRIS/alerts.py b/backend/app/services/DFIR_IRIS/alerts.py index ed38901c..67a687ff 100644 --- a/backend/app/services/DFIR_IRIS/alerts.py +++ b/backend/app/services/DFIR_IRIS/alerts.py @@ -12,10 +12,15 @@ class AlertsService: """ - A service class that encapsulates the logic for pulling alerts from DFIR-IRIS. + A service class that encapsulates the logic for pulling alerts from DFIR-IRIS. It creates a DFIR-IRIS session upon + initialization and uses it to fetch alerts. """ def __init__(self): + """ + Initializes the AlertsService by creating a UniversalService object for "DFIR-IRIS" and establishing a session. + If the session creation is unsuccessful, an error is logged and the iris_session attribute is set to None. + """ self.universal_service = UniversalService("DFIR-IRIS") session_result = self.universal_service.create_session() @@ -27,10 +32,15 @@ def __init__(self): def list_alerts(self) -> Dict[str, object]: """ - Lists all alerts from DFIR-IRIS + List all alerts from DFIR-IRIS. If the iris_session attribute is None, this indicates that the session creation + was unsuccessful, and a dictionary with "success" set to False is returned. Otherwise, it attempts to fetch and + parse the alerts data. Returns: - dict: A dictionary containing the success status, a message and potentially the cases. + dict: A dictionary containing the success status, a message, and potentially the fetched alerts. The + "success" key is a boolean indicating whether the operation was successful. The "message" key is a string + providing details about the operation. The "results" key, included when "success" is True, contains the + fetched alerts data. """ if self.iris_session is None: return { diff --git a/backend/app/services/DFIR_IRIS/assets.py b/backend/app/services/DFIR_IRIS/assets.py index f2fb3beb..3acd8e72 100644 --- a/backend/app/services/DFIR_IRIS/assets.py +++ b/backend/app/services/DFIR_IRIS/assets.py @@ -13,10 +13,15 @@ class AssetsService: """ - A service class that encapsulates the logic for pulling case assets from DFIR-IRIS. + A service class that encapsulates the logic for pulling case assets from DFIR-IRIS. It creates a DFIR-IRIS session upon + initialization and uses it to fetch case assets. """ def __init__(self): + """ + Initializes the AssetsService by creating a UniversalService object for "DFIR-IRIS" and establishing a session. + If the session creation is unsuccessful, an error is logged and the iris_session attribute is set to None. + """ self.universal_service = UniversalService("DFIR-IRIS") session_result = self.universal_service.create_session() @@ -28,13 +33,18 @@ def __init__(self): def get_case_assets(self, cid: int) -> Dict[str, object]: """ - Gets a case's assets from DFIR-IRIS + Retrieves the assets of a specific case from DFIR-IRIS. If the iris_session attribute is None, this indicates + that the session creation was unsuccessful, and a dictionary with "success" set to False is returned. Otherwise, + it attempts to fetch and parse the assets data for the case specified by the `cid` parameter. - ARGS: - cid: The case ID to search for + Args: + cid (int): The ID of the case for which to retrieve assets. Returns: - dict: A dictionary containing the success status, a message and potentially the notes of a given case. + dict: A dictionary containing the success status, a message, and potentially the fetched assets. The + "success" key is a boolean indicating whether the operation was successful. The "message" key is a string + providing details about the operation. If "success" is True, the dictionary also contains the "data" key + with the fetched assets. """ if self.iris_session is None: return { diff --git a/backend/app/services/DFIR_IRIS/notes.py b/backend/app/services/DFIR_IRIS/notes.py index 80c05bbc..1dd4c542 100644 --- a/backend/app/services/DFIR_IRIS/notes.py +++ b/backend/app/services/DFIR_IRIS/notes.py @@ -13,10 +13,16 @@ class NotesService: """ - A service class that encapsulates the logic for pulling case notes from DFIR-IRIS. + A service class that encapsulates the logic for pulling and managing case notes from DFIR-IRIS. This class handles + fetching and creating case notes. It creates a DFIR-IRIS session upon initialization and uses it to interact with + the DFIR-IRIS case notes. """ def __init__(self): + """ + Initializes the NotesService by creating a UniversalService object for "DFIR-IRIS" and establishing a session. + If the session creation is unsuccessful, an error is logged and the iris_session attribute is set to None. + """ self.universal_service = UniversalService("DFIR-IRIS") session_result = self.universal_service.create_session() @@ -28,14 +34,19 @@ def __init__(self): def get_case_notes(self, search_term: str, cid: int) -> Dict[str, object]: """ - Gets a case's notes from DFIR-IRIS and return the ID and Title + Retrieves the notes of a specific case from DFIR-IRIS. If the iris_session attribute is None, this indicates + that the session creation was unsuccessful, and a dictionary with "success" set to False is returned. Otherwise, + it attempts to fetch and parse the notes data for the case specified by the `cid` parameter. - ARGS: - cid: The case ID to search for - search_term: The search term to use + Args: + search_term (str): The search term to use when fetching case notes. + cid (int): The ID of the case for which to retrieve notes. Returns: - dict: A dictionary containing the success status, a message and potentially the notes of a given case. + dict: A dictionary containing the success status, a message, and potentially the fetched notes. The + "success" key is a boolean indicating whether the operation was successful. The "message" key is a string + providing details about the operation. If "success" is True, the dictionary also contains the "data" key + with the fetched notes. """ if self.iris_session is None: return { @@ -73,14 +84,20 @@ def get_case_notes(self, search_term: str, cid: int) -> Dict[str, object]: def _get_case_note_details(self, note_id: int, cid: int) -> Dict[str, object]: """ - Gets a case's notes from DFIR-IRIS and returns the note details such as the content + Retrieves the details of a specific note of a specific case from DFIR-IRIS. If the iris_session attribute is None, + this indicates that the session creation was unsuccessful, and a dictionary with "success" set to False is + returned. Otherwise, it attempts to fetch and parse the note data for the note specified by the `note_id` + parameter and the case specified by the `cid` parameter. - ARGS: - cid: The case ID to search for - note_id: The note ID to search for + Args: + note_id (int): The ID of the note for which to retrieve details. + cid (int): The ID of the case for which to retrieve the note details. Returns: - dict: A dictionary containing the success status, a message and potentially the notes of a given case. + dict: A dictionary containing the success status, a message, and potentially the fetched note details. The + "success" key is a boolean indicating whether the operation was successful. The "message" key is a string + providing details about the operation. If "success" is True, the dictionary also contains the "notes" key + with the fetched note details. """ if self.iris_session is None: return { @@ -116,15 +133,21 @@ def create_case_note( note_content: str, ) -> Dict[str, object]: """ - Creates a case note in DFIR-IRIS + Creates a note for a specific case in DFIR-IRIS. If the iris_session attribute is None, this indicates that + the session creation was unsuccessful, and a dictionary with "success" set to False is returned. Otherwise, + it attempts to create a note with the specified `note_title` and `note_content` for the case specified by the + `cid` parameter. - ARGS: - cid: The case ID to search for - title: The title of the note - content: The content of the note + Args: + cid (int): The ID of the case for which to create a note. + note_title (str): The title of the note to create. + note_content (str): The content of the note to create. Returns: - dict: A dictionary containing the success status, a message and potentially the notes of a given case. + dict: A dictionary containing the success status, a message, and potentially the created note. The + "success" key is a boolean indicating whether the operation was successful. The "message" key is a string + providing details about the operation. If "success" is True, the dictionary also contains the "notes" key + with the created note. """ if self.iris_session is None: return { diff --git a/backend/app/services/DFIR_IRIS/universal.py b/backend/app/services/DFIR_IRIS/universal.py index 9aaa843c..d2667db5 100644 --- a/backend/app/services/DFIR_IRIS/universal.py +++ b/backend/app/services/DFIR_IRIS/universal.py @@ -1,4 +1,5 @@ from typing import Optional +from typing import Tuple, Dict, Callable, Union # from dfir_iris_client.case import Case from dfir_iris_client.helper.utils import assert_api_resp @@ -18,15 +19,22 @@ class UniversalService: """ - A service class that encapsulates the logic for polling messages from DFIR-IRIS. + A service class that encapsulates the logic for interfacing with DFIR-IRIS. This class handles tasks like creating a session, + fetching and parsing data, and retrieving connector details. """ def __init__(self, connector_name: str) -> None: + """ + Initializes the UniversalService by collecting DFIR-IRIS details associated with the specified connector name. + + Args: + connector_name (str): The name of the DFIR-IRIS connector. + """ self.connector_url, self.connector_api_key = self.collect_iris_details( connector_name, ) - def collect_iris_details(self, connector_name: str): + def collect_iris_details(self, connector_name: str) -> Tuple[Optional[str], Optional[str]]: """ Collects the details of the DFIR-IRIS connector. @@ -34,7 +42,7 @@ def collect_iris_details(self, connector_name: str): connector_name (str): The name of the DFIR-IRIS connector. Returns: - tuple: A tuple containing the connection URL, and api key. + tuple: A tuple containing the connection URL and API key. If the connection is not successful, both elements of the tuple are None. """ connector_instance = connector_factory.create(connector_name, connector_name) connection_successful = connector_instance.verify_connection() @@ -47,15 +55,15 @@ def collect_iris_details(self, connector_name: str): else: return None, None - def create_session(self) -> Optional[ClientSession]: + def create_session(self) -> Dict[str, Union[bool, Optional[ClientSession], str]]: """ - Create a session with DFIR-IRIS. + Creates a session with DFIR-IRIS. - This method creates a session with DFIR-IRIS and returns the session object. - If a session cannot be established, an error is logged and None is returned. + This method creates a session with DFIR-IRIS and returns a dictionary with a success status and the session object. + If a session cannot be established, an error is logged and a dictionary with "success" set to False and an error message is returned. Returns: - session: A session object for DFIR-IRIS. + dict: A dictionary containing the success status and either the session object or an error message. """ try: logger.info("Creating session with DFIR-IRIS.") @@ -76,17 +84,17 @@ def create_session(self) -> Optional[ClientSession]: "message": "Connection to DFIR-IRIS unsuccessful.", } - def fetch_and_parse_data(self, session, action, *args): + def fetch_and_parse_data(self, session: ClientSession, action: Callable, *args) -> Dict[str, Union[bool, Optional[Dict]]]: """ - General method to fetch and parse data from DFIR-IRIS. + Fetches and parses data from DFIR-IRIS using a specified action. Args: - session: ClientSession object. - action: callable, the action to be performed (e.g., list_cases or get_case) - args: arguments for the action callable + session (ClientSession): The DFIR-IRIS session object. + action (Callable): The function to execute to fetch data from DFIR-IRIS. This function should accept *args. + args: The arguments to pass to the action function. Returns: - dict: A dictionary containing the data and a success status. + dict: A dictionary containing the success status and either the fetched data or None if the operation was unsuccessful. """ try: logger.info(f"Executing {action.__name__}... on args: {args}") diff --git a/backend/app/services/Shuffle/__init__.py b/backend/app/services/Shuffle/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app/services/Shuffle/universal.py b/backend/app/services/Shuffle/universal.py index 1da44cea..280ecb3a 100644 --- a/backend/app/services/Shuffle/universal.py +++ b/backend/app/services/Shuffle/universal.py @@ -16,7 +16,7 @@ class UniversalService: """ - A service class that encapsulates the logic for polling messages from the Wazuh-Indexer. + A service class that encapsulates the logic for polling messages from Shuffle. """ def __init__(self) -> None: diff --git a/backend/app/services/Shuffle/workflows.py b/backend/app/services/Shuffle/workflows.py index 4d96db52..7eb09df7 100644 --- a/backend/app/services/Shuffle/workflows.py +++ b/backend/app/services/Shuffle/workflows.py @@ -8,7 +8,24 @@ class WorkflowsService: """ - A service class that encapsulates the logic for pulling workflows from Shuffle. + A service class that encapsulates the logic for retrieving workflow information from Shuffle. + + Attributes: + connector_url (str): The URL of the Shuffle instance. + connector_api_key (str): The API key used for the Shuffle instance. + session (requests.Session): A requests session for making HTTP requests. + + Methods: + _collect_shuffle_details: Collects the details of the Shuffle connector. + _are_details_collected: Checks whether the details for the Shuffle connector were successfully collected. + _send_request: Sends a GET request to a provided URL. + collect_workflows: Collects the workflows from Shuffle. + _handle_request_error: Handles any errors that occur during a request. + _collect_workflows: Collects the workflows from Shuffle. + collect_workflow_details: Collects the workflow ID and workflow name from Shuffle. + _collect_workflow_details: Collects the workflow ID and workflow name from Shuffle. + collect_workflow_executions_status: Collects the execution status of a Shuffle Workflow by its ID. + _collect_workflow_executions_status: Collects the execution status of a Shuffle Workflow by its ID. """ def __init__(self): @@ -19,15 +36,38 @@ def __init__(self): ) def _collect_shuffle_details(self): + """ + Collects the details of the Shuffle connector. + + The details are collected from a universal service which pulls connector details from a database. + + Returns: + tuple: A tuple containing the connection URL and API key of the Shuffle connector. + """ ( self.connector_url, self.connector_api_key, ) = UniversalService().collect_shuffle_details("Shuffle") def _are_details_collected(self) -> bool: + """ + Checks whether the details for the Shuffle connector were successfully collected. + + Returns: + bool: True if all details were collected, False otherwise. + """ return all([self.connector_url, self.connector_api_key]) def _send_request(self, url: str): + """ + Sends a GET request to a provided URL. + + Args: + url (str): The URL to send the GET request to. + + Returns: + requests.Response: The response from the GET request. + """ return self.session.get( url, verify=False, @@ -38,7 +78,7 @@ def collect_workflows(self) -> Dict[str, object]: Collects the workflows from Shuffle. Returns: - dict: A dictionary containing the success status, a message and potentially the workflows. + dict: A dictionary containing the success status, a message, and potentially the workflows. """ if not self._are_details_collected(): return { @@ -56,7 +96,16 @@ def collect_workflows(self) -> Dict[str, object]: "workflows": workflows["workflows"], } - def _handle_request_error(self, err): + def _handle_request_error(self, err: Exception) -> Dict[str, object]: + """ + Handles any errors that occur during a request. + + Args: + err (Exception): The exception that occurred. + + Returns: + dict: A dictionary containing the success status and an error message. + """ logger.error(f"Failed to collect workflows from Shuffle: {err}") return { "message": "Failed to collect workflows from Shuffle", @@ -68,7 +117,7 @@ def _collect_workflows(self) -> Dict[str, object]: Collects the workflows from Shuffle. Returns: - dict: A dictionary containing the success status, a message and potentially the workflows. + dict: A dictionary containing the success status, a message, and potentially the workflows. """ try: response = self._send_request(f"{self.connector_url}/api/v1/workflows") @@ -87,7 +136,7 @@ def collect_workflow_details(self) -> Dict[str, object]: Collects the workflow ID and workflow name from Shuffle. Returns: - dict: A dictionary containing the success status, a message and potentially the workflow IDs. + dict: A dictionary containing the success status, a message, and potentially the workflow IDs. """ if not self._are_details_collected(): return { @@ -110,7 +159,7 @@ def _collect_workflow_details(self) -> Dict[str, object]: Collects the workflow ID and workflow name from Shuffle. Returns: - dict: A dictionary containing the success status, a message and potentially the workflow IDs. + dict: A dictionary containing the success status, a message, and potentially the workflow IDs. """ try: response = self._send_request(f"{self.connector_url}/api/v1/workflows") @@ -136,7 +185,7 @@ def collect_workflow_executions_status(self, workflow_id: str) -> Dict[str, obje Collects the execution status of a Shuffle Workflow by its ID. Returns: - dict: A dictionary containing the success status, a message and potentially the workflow execution status. + dict: A dictionary containing the success status, a message, and potentially the workflow execution status. """ if not self._are_details_collected(): return { @@ -162,7 +211,7 @@ def _collect_workflow_executions_status( Collects the execution status of a Shuffle Workflow by its ID. Returns: - dict: A dictionary containing the success status, a message and potentially the workflow execution status. + dict: A dictionary containing the success status, a message, and potentially the workflow execution status. """ try: response = self._send_request( diff --git a/backend/app/services/WazuhIndexer/cluster.py b/backend/app/services/WazuhIndexer/cluster.py index e4d9ee5c..bed2fbcb 100644 --- a/backend/app/services/WazuhIndexer/cluster.py +++ b/backend/app/services/WazuhIndexer/cluster.py @@ -13,10 +13,16 @@ class ClusterService: """ def __init__(self): + """ + Initialize the ClusterService with the details of the Wazuh-Indexer and initialize the Elasticsearch client. + """ self._collect_wazuhindexer_details() self._initialize_es_client() def _collect_wazuhindexer_details(self): + """ + Collect details of the Wazuh-Indexer. These details include the connector URL, username, and password. + """ ( self.connector_url, self.connector_username, @@ -24,6 +30,9 @@ def _collect_wazuhindexer_details(self): ) = UniversalService().collect_wazuhindexer_details("Wazuh-Indexer") def _initialize_es_client(self): + """ + Initialize the Elasticsearch client with the details of the Wazuh-Indexer. + """ self.es = Elasticsearch( [self.connector_url], http_auth=(self.connector_username, self.connector_password), @@ -34,16 +43,22 @@ def _initialize_es_client(self): ) def _are_details_collected(self) -> bool: + """ + Check whether the details of the Wazuh-Indexer have been collected. + + Returns: + bool: True if all details have been collected, False otherwise. + """ return all( [self.connector_url, self.connector_username, self.connector_password], ) def collect_node_allocation(self) -> Dict[str, object]: """ - Collects the node allocation from the Wazuh-Indexer. + Collect node allocation details from the Wazuh-Indexer's Elasticsearch cluster. Returns: - dict: A dictionary containing the success status, a message and potentially the index allocation. + dict: A dictionary containing success status, a message, and potentially the node allocation details. """ if not self._are_details_collected(): return { @@ -63,10 +78,10 @@ def collect_node_allocation(self) -> Dict[str, object]: def _collect_node_allocation(self) -> Dict[str, object]: """ - Collects the node allocation from the Wazuh-Indexer. + Collect node allocation details from the Wazuh-Indexer's Elasticsearch cluster. Returns: - dict: A dictionary containing the success status, a message and potentially the index allocation. + dict: A dictionary containing success status, a message, and potentially the node allocation details. """ try: node_allocation = self.es.cat.allocation(format="json") @@ -81,6 +96,15 @@ def _collect_node_allocation(self) -> Dict[str, object]: return {"message": "Failed to collect node allocation", "success": False} def _format_node_allocation(self, node_allocation): + """ + Format the node allocation details into a list of dictionaries. Each dictionary contains disk used, disk available, total disk, disk usage percentage, and node name. + + Args: + node_allocation: Node allocation details from Elasticsearch. + + Returns: + list: A list of dictionaries containing formatted node allocation details. + """ return [ { "disk_used": node["disk.used"], @@ -94,10 +118,10 @@ def _format_node_allocation(self, node_allocation): def collect_cluster_health(self) -> Dict[str, object]: """ - Collects the cluster health from the Wazuh-Indexer. + Collect health details of the Elasticsearch cluster from the Wazuh-Indexer. Returns: - dict: A dictionary containing the success status, a message and potentially the cluster health. + dict: A dictionary containing success status, a message, and potentially the cluster health details. """ if not self._are_details_collected(): return { @@ -117,10 +141,10 @@ def collect_cluster_health(self) -> Dict[str, object]: def _collect_cluster_health(self) -> Dict[str, object]: """ - Collects the cluster health from the Wazuh-Indexer. + Collect health details of the Elasticsearch cluster from the Wazuh-Indexer. Returns: - dict: A dictionary containing the success status, a message and potentially the cluster health. + dict: A dictionary containing success status, a message, and potentially the cluster health details. """ try: cluster_health = self.es.cluster.health() @@ -135,10 +159,10 @@ def _collect_cluster_health(self) -> Dict[str, object]: def collect_shards(self) -> Dict[str, object]: """ - Collects the shards from the Wazuh-Indexer. + Collect shard details from the Wazuh-Indexer's Elasticsearch cluster. Returns: - dict: A dictionary containing the success status, a message and potentially the shards. + dict: A dictionary containing success status, a message, and potentially the shard details. """ if not self._are_details_collected(): return { @@ -158,10 +182,10 @@ def collect_shards(self) -> Dict[str, object]: def _collect_shards(self) -> Dict[str, object]: """ - Collects the shards from the Wazuh-Indexer. + Collect shard details from the Wazuh-Indexer's Elasticsearch cluster. Returns: - dict: A dictionary containing the success status, a message and potentially the shards. + dict: A dictionary containing success status, a message, and potentially the shard details. """ try: shards = self.es.cat.shards(format="json") @@ -176,6 +200,15 @@ def _collect_shards(self) -> Dict[str, object]: return {"message": "Failed to collect shards", "success": False} def _format_shards(self, shards): + """ + Format the shard details into a list of dictionaries. Each dictionary contains index name, shard number, shard state, shard size, and node name. + + Args: + shards: Shard details from Elasticsearch. + + Returns: + list: A list of dictionaries containing formatted shard details. + """ return [ { "index": shard["index"], diff --git a/backend/app/services/WazuhIndexer/index.py b/backend/app/services/WazuhIndexer/index.py index da529ca3..eec9a6f0 100644 --- a/backend/app/services/WazuhIndexer/index.py +++ b/backend/app/services/WazuhIndexer/index.py @@ -13,10 +13,16 @@ class IndexService: """ def __init__(self): + """ + Initialize the IndexService with the details of the Wazuh-Indexer and initialize the Elasticsearch client. + """ self._collect_wazuhindexer_details() self._initialize_es_client() def _collect_wazuhindexer_details(self): + """ + Collect details of the Wazuh-Indexer. These details include the connector URL, username, and password. + """ ( self.connector_url, self.connector_username, @@ -24,6 +30,9 @@ def _collect_wazuhindexer_details(self): ) = UniversalService().collect_wazuhindexer_details("Wazuh-Indexer") def _initialize_es_client(self): + """ + Initialize the Elasticsearch client with the details of the Wazuh-Indexer. + """ self.es = Elasticsearch( [self.connector_url], http_auth=(self.connector_username, self.connector_password), @@ -34,16 +43,22 @@ def _initialize_es_client(self): ) def _are_details_collected(self) -> bool: + """ + Check whether the details of the Wazuh-Indexer have been collected. + + Returns: + bool: True if all details have been collected, False otherwise. + """ return all( [self.connector_url, self.connector_username, self.connector_password], ) def collect_indices_summary(self) -> Dict[str, object]: """ - Collects summary information for each index from the Wazuh-Indexer. + Collect summary information for each index from the Wazuh-Indexer's Elasticsearch cluster. Returns: - dict: A dictionary containing the success status, a message, and potentially the indices. + dict: A dictionary containing success status, a message, and potentially the indices summary. """ if not self._are_details_collected(): return { @@ -64,6 +79,15 @@ def collect_indices_summary(self) -> Dict[str, object]: } def _format_indices_summary(self, indices: Dict[str, object]) -> Dict[str, object]: + """ + Format the indices summary into a list of dictionaries. Each dictionary contains index name, health status, document count, store size, and replica count. + + Args: + indices (dict): Indices summary from Elasticsearch. + + Returns: + list: A list of dictionaries containing formatted indices summary. + """ return [ { "index": index["index"], @@ -77,10 +101,10 @@ def _format_indices_summary(self, indices: Dict[str, object]) -> Dict[str, objec def _collect_indices(self) -> Dict[str, object]: """ - Collects the indices from the Wazuh-Indexer. + Collect indices from the Wazuh-Indexer's Elasticsearch cluster. Returns: - dict: A dictionary containing the success status, a message and potentially the indices. + dict: A dictionary containing success status, a message, and potentially the indices. """ try: indices = self.es.cat.indices(format="json") diff --git a/backend/app/services/WazuhManager/__init__.py b/backend/app/services/WazuhManager/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app/services/WazuhManager/agent.py b/backend/app/services/WazuhManager/agent.py index 67260e14..caf09f05 100644 --- a/backend/app/services/WazuhManager/agent.py +++ b/backend/app/services/WazuhManager/agent.py @@ -11,13 +11,18 @@ class WazuhHttpRequests: """ - Class to handle HTTP requests to the Wazuh API. + A class to handle HTTP requests to the Wazuh API. + + This class is initialized with the URL for the Wazuh connector and an authentication token. + It provides a method to make HTTP DELETE requests to the specified Wazuh API endpoint. """ def __init__(self, connector_url: str, wazuh_auth_token: str) -> None: """ + Initializes a WazuhHttpRequests instance. + Args: - connector_url (str): The URL of the Wazuh Manager. + connector_url (str): The URL for the Wazuh connector. wazuh_auth_token (str): The Wazuh API authentication token. """ self.connector_url = connector_url @@ -30,14 +35,15 @@ def delete_request( params: Optional[Dict[str, str]] = None, ) -> Dict[str, bool]: """ - Function to handle DELETE requests. + Makes an HTTP DELETE request to a specified Wazuh API endpoint. Args: endpoint (str): The endpoint to make a DELETE request to. params (Optional[Dict[str, str]]): Any parameters to pass in the DELETE request. Returns: - Dict[str, bool]: A dictionary indicating the success of the operation. + Dict[str, bool]: A dictionary indicating the success of the operation. If the request was successful, + `{"agentDeleted": True}` is returned. If it failed, `{"agentDeleted": False}` is returned. """ try: response = requests.delete( @@ -57,13 +63,18 @@ def delete_request( class WazuhManagerAgentService: """ - A service class that encapsulates the logic for handling agent related operations in Wazuh Manager. + A service class that encapsulates the logic for handling agent-related operations in the Wazuh Manager. + + This class uses the UniversalService to get authentication tokens and URLs, and WazuhHttpRequests to make HTTP requests. + It provides methods to collect all agents from Wazuh Manager and to delete a specific agent. """ def __init__(self, universal_service: UniversalService) -> None: """ + Initializes a WazuhManagerAgentService instance. + Args: - universal_service (UniversalService): The UniversalService instance to use. + universal_service (UniversalService): An instance of UniversalService. """ self.universal_service = universal_service self.auth_token = universal_service.get_auth_token() @@ -74,10 +85,11 @@ def __init__(self, universal_service: UniversalService) -> None: def collect_agents(self) -> Optional[List[Dict[str, str]]]: """ - Collect all agents from Wazuh Manager. + Collects all agents from the Wazuh Manager. Returns: - Optional[List[Dict[str, str]]]: A list of dictionaries containing agent data, or None on failure. + Optional[List[Dict[str, str]]]: A list of dictionaries where each dictionary contains data about an agent. + If the operation fails, it returns None. """ logger.info("Collecting Wazuh Agents") try: @@ -93,10 +105,10 @@ def collect_agents(self) -> Optional[List[Dict[str, str]]]: def _get_agent_data(self) -> Optional[Dict[str, Any]]: """ - Get agent data from Wazuh Manager. + Retrieves agent data from the Wazuh Manager. Returns: - Optional[Dict[str, Any]]: A dictionary containing agent data, or None on failure. + Optional[Dict[str, Any]]: A dictionary containing data about agents. If the operation fails, it returns None. """ headers = {"Authorization": f"Bearer {self.auth_token}"} limit = 1000 @@ -112,13 +124,13 @@ def _get_agent_data(self) -> Optional[Dict[str, Any]]: def _build_agent_list(self, agent_data: Dict[str, Any]) -> List[Dict[str, str]]: """ - Build a list of agent data dictionaries. + Builds a list of dictionaries with agent data. Args: agent_data (Dict[str, Any]): The raw agent data. Returns: - List[Dict[str, str]]: A list of dictionaries containing agent data. + List[Dict[str, str]]: A list of dictionaries where each dictionary contains data about an agent. """ wazuh_agents_list = [] for agent in agent_data: @@ -138,13 +150,14 @@ def _build_agent_list(self, agent_data: Dict[str, Any]) -> List[Dict[str, str]]: def delete_agent(self, agent_id: str) -> Dict[str, bool]: """ - Delete an agent from Wazuh Manager. + Deletes an agent from the Wazuh Manager. Args: agent_id (str): The id of the agent to be deleted. Returns: - Dict[str, bool]: A dictionary indicating the success of the operation. + Dict[str, bool]: A dictionary indicating the success of the operation. If the operation was successful, + `{"agentDeleted": True}` is returned. If it failed, `{"agentDeleted": False}` is returned. """ params = { "purge": True, diff --git a/backend/app/services/WazuhManager/disabled_rule.py b/backend/app/services/WazuhManager/disabled_rule.py index a9ecdd09..65120355 100644 --- a/backend/app/services/WazuhManager/disabled_rule.py +++ b/backend/app/services/WazuhManager/disabled_rule.py @@ -21,11 +21,18 @@ class WazuhHttpRequests: """ - Class to handle HTTP requests to the Wazuh API. + This class helps to send HTTP requests to the Wazuh API. + + Attributes: + connector_url (str): The URL of the Wazuh Manager. + wazuh_auth_token (str): The Wazuh API authentication token. + headers (Dict[str, str]): The headers for HTTP requests. """ def __init__(self, connector_url: str, wazuh_auth_token: str) -> None: """ + Initialize a WazuhHttpRequests instance. + Args: connector_url (str): The URL of the Wazuh Manager. wazuh_auth_token (str): The Wazuh API authentication token. @@ -40,14 +47,14 @@ def get_request( params: Optional[Dict[str, str]] = None, ) -> Dict[str, Union[str, bool]]: """ - Function to handle GET requests. + Send a GET request to the specified endpoint. Args: - endpoint (str): The endpoint to make a GET request to. - params (Optional[Dict[str, str]]): Any parameters to pass in the GET request. + endpoint (str): The endpoint to send the GET request to. + params (Optional[Dict[str, str]]): Additional parameters to include in the request. Returns: - Dict[str, Union[str, bool]]: A dictionary with the requested data or error message. + Dict[str, Union[str, bool]]: The response from the endpoint. It contains the data from the response or an error message. """ try: response = requests.get( @@ -73,15 +80,15 @@ def put_request( params: Optional[Dict[str, str]] = None, ) -> Dict[str, bool]: """ - Function to handle PUT requests. + Send a PUT request to the specified endpoint. Args: - endpoint (str): The endpoint to make a PUT request to. - data (str): Data to be updated on the PUT request. - params (Optional[Dict[str, str]]): Any parameters to pass in the PUT request. + endpoint (str): The endpoint to send the PUT request to. + data (str): The data to be updated in the PUT request. + params (Optional[Dict[str, str]]): Additional parameters to include in the request. Returns: - Dict[str, bool]: A dictionary indicating the success of the operation. + Dict[str, bool]: A dictionary indicating the success or failure of the operation. """ try: headers = self.headers.copy() @@ -104,11 +111,18 @@ def put_request( class DisableRuleService: """ - A service class that encapsulates the logic for handling rule disabling related operations in Wazuh Manager. + This class handles rule disabling related operations in Wazuh Manager. + + Attributes: + universal_service (UniversalService): An instance of UniversalService. + auth_token (str): The Wazuh API authentication token. + wazuh_http_requests (WazuhHttpRequests): An instance of WazuhHttpRequests. """ def __init__(self, universal_service: UniversalService) -> None: """ + Initialize a DisableRuleService instance. + Args: universal_service (UniversalService): The UniversalService instance to use. """ @@ -123,6 +137,15 @@ def disable_rule( self, request: Dict[str, Union[str, int]], ) -> Dict[str, Union[str, bool]]: + """ + Disable a rule in Wazuh Manager. + + Args: + request (Dict[str, Union[str, int]]): The request to disable a rule. It should contain 'rule_id', 'reason', and 'length_of_time'. + + Returns: + Dict[str, Union[str, bool]]: A dictionary indicating the success or failure of the operation. + """ try: self._validate_request(request) rule_id = request["rule_id"] @@ -150,7 +173,16 @@ def disable_rule( logger.error(str(e)) return {"message": str(e), "success": False} - def _validate_request(self, request: Dict[str, Union[str, int]]): + def _validate_request(self, request: Dict[str, Union[str, int]]) -> None: + """ + Validate the request to disable a rule. + + Args: + request (Dict[str, Union[str, int]]): The request to disable a rule. + + Raises: + ValueError: If any of the required fields ('rule_id', 'reason', 'length_of_time') are missing in the request. + """ logger.info(f"Validating disable rule request: {request}") if "rule_id" not in request: raise ValueError("Request missing rule_id") @@ -161,6 +193,18 @@ def _validate_request(self, request: Dict[str, Union[str, int]]): request["length_of_time"] = int(request["length_of_time"]) def _fetch_filename(self, rule_id: str) -> str: + """ + Fetch the filename from the Wazuh-Manager that contains the rule to be disabled. + + Args: + rule_id (str): The id of the rule to be disabled. + + Returns: + str: The filename that contains the rule. + + Raises: + ValueError: If the filename could not be fetched successfully. + """ filename_data = self.wazuh_http_requests.get_request( "rules", {"rule_ids": rule_id}, @@ -173,6 +217,18 @@ def _fetch_file_content( self, filename: str, ) -> Union[Dict[str, str], List[Dict[str, str]]]: + """ + Fetch the content of the file that contains the rule to be disabled. + + Args: + filename (str): The filename that contains the rule. + + Returns: + Union[Dict[str, str], List[Dict[str, str]]]: The content of the file. + + Raises: + ValueError: If the file content could not be fetched successfully. + """ file_content_data = self.wazuh_http_requests.get_request( f"rules/files/{filename}", ) @@ -185,6 +241,16 @@ def _set_level_1( file_content: Union[Dict[str, str], List[Dict[str, str]]], rule_id: str, ) -> Tuple[str, Union[Dict[str, str], List[Dict[str, str]]]]: + """ + Set the level of the rule to 1. + + Args: + file_content (Union[Dict[str, str], List[Dict[str, str]]]): The content of the file that contains the rule. + rule_id (str): The id of the rule to be disabled. + + Returns: + Tuple[str, Union[Dict[str, str], List[Dict[str, str]]]]: A tuple containing the previous level of the rule and the updated file content. + """ logger.info( f"Setting rule {rule_id} level to 1 for file_content: {file_content}", ) @@ -208,6 +274,15 @@ def _convert_to_xml( self, updated_file_content: Union[Dict[str, str], List[Dict[str, str]]], ) -> str: + """ + Convert the updated file content to XML format. + + Args: + updated_file_content (Union[Dict[str, str], List[Dict[str, str]]]): The updated file content. + + Returns: + str: The updated file content in XML format. + """ logger.info(f"Received updated_file_content: {updated_file_content}") xml_content_list = [] for group in updated_file_content: @@ -228,7 +303,16 @@ def _store_disabled_rule_info( previous_level: str, reason: str, length_of_time: str, - ): + ) -> None: + """ + Store information about the disabled rule in the database. + + Args: + rule_id (str): The id of the rule. + previous_level (str): The previous level of the rule before it was disabled. + reason (str): The reason for disabling the rule. + length_of_time (str): The length of time for which the rule will be disabled. + """ disabled_rule = DisabledRules( rule_id=rule_id, previous_level=previous_level, @@ -240,6 +324,16 @@ def _store_disabled_rule_info( db.session.commit() def _upload_updated_rule(self, filename: str, xml_content: str): + """ + Upload the updated rule to the Wazuh Manager. + + Args: + filename (str): The filename that contains the rule. + xml_content (str): The updated rule in XML format. + + Raises: + ValueError: If the updated rule could not be uploaded successfully. + """ response = self.wazuh_http_requests.put_request( f"rules/files/{filename}", xml_content, diff --git a/backend/app/services/WazuhManager/enabled_rule.py b/backend/app/services/WazuhManager/enabled_rule.py index 3ac5f771..6a8b17f2 100644 --- a/backend/app/services/WazuhManager/enabled_rule.py +++ b/backend/app/services/WazuhManager/enabled_rule.py @@ -22,11 +22,18 @@ class WazuhHttpRequests: """ - Class to handle HTTP requests to the Wazuh API. + A class to handle HTTP requests to the Wazuh API. + + Attributes: + connector_url (str): The URL of the Wazuh Manager. + wazuh_auth_token (str): The Wazuh API authentication token. + headers (dict): The headers for the HTTP requests. """ def __init__(self, connector_url: str, wazuh_auth_token: str) -> None: """ + Initialize a WazuhHttpRequests instance. + Args: connector_url (str): The URL of the Wazuh Manager. wazuh_auth_token (str): The Wazuh API authentication token. @@ -41,14 +48,14 @@ def get_request( params: Optional[Dict[str, str]] = None, ) -> Dict[str, Union[str, bool]]: """ - Function to handle GET requests. + Send a GET request to the given endpoint. Args: - endpoint (str): The endpoint to make a GET request to. - params (Optional[Dict[str, str]]): Any parameters to pass in the GET request. + endpoint (str): The endpoint to send a GET request to. + params (Optional[Dict[str, str]]): Additional parameters to include in the request. Returns: - Dict[str, Union[str, bool]]: A dictionary with the requested data or error message. + Dict[str, Union[str, bool]]: A dictionary containing the requested data or an error message. """ try: logger.info(f"GET request to {endpoint}") @@ -76,12 +83,12 @@ def put_request( params: Optional[Dict[str, str]] = None, ) -> Dict[str, bool]: """ - Function to handle PUT requests. + Send a PUT request to the given endpoint. Args: - endpoint (str): The endpoint to make a PUT request to. - data (str): Data to be updated on the PUT request. - params (Optional[Dict[str, str]]): Any parameters to pass in the PUT request. + endpoint (str): The endpoint to send a PUT request to. + data (str): The data to be updated in the PUT request. + params (Optional[Dict[str, str]]): Additional parameters to include in the request. Returns: Dict[str, bool]: A dictionary indicating the success of the operation. @@ -107,13 +114,20 @@ def put_request( class EnableRuleService: """ - A service class that encapsulates the logic for handling rule enabling related operations in Wazuh Manager. + A service class to manage rule enabling operations in Wazuh Manager. + + Attributes: + universal_service (UniversalService): An instance of the UniversalService. + auth_token (str): The Wazuh API authentication token. + wazuh_http_requests (WazuhHttpRequests): An instance of WazuhHttpRequests. """ def __init__(self, universal_service: UniversalService) -> None: """ + Initialize an EnableRuleService instance. + Args: - universal_service (UniversalService): The UniversalService instance to use. + universal_service (UniversalService): An instance of the UniversalService. """ self.universal_service = universal_service self.auth_token = universal_service.get_auth_token() @@ -124,13 +138,13 @@ def __init__(self, universal_service: UniversalService) -> None: def enable_rule(self, request: Dict[str, str]) -> Dict[str, Union[str, bool]]: """ - Enable a rule in the Wazuh Manager. + Enable a rule in Wazuh Manager. Args: - request (Dict[str, str]): The request to enable a rule in Wazuh Manager. + request (Dict[str, str]): A request to enable a rule. Returns: - Dict[str, Union[str, bool]]: A dictionary containing status of the operation. + Dict[str, Union[str, bool]]: A dictionary indicating the success of the operation. """ try: self._validate_request(request) @@ -157,16 +171,16 @@ def enable_rule(self, request: Dict[str, str]) -> Dict[str, Union[str, bool]]: def _validate_request(self, request: Dict[str, str]) -> str: """ - Validate the request to enable a rule in Wazuh Manager and return rule_id. + Validate a request to enable a rule. Args: - request (Dict[str, str]): The request to enable a rule in Wazuh Manager. + request (Dict[str, str]): A request to enable a rule. Raises: - ValueError: If the request is missing rule_id. + ValueError: If 'rule_id' is not in the request. Returns: - str: rule_id. + str: The rule id from the request. """ logger.info(f"Validating enable rule request: {request}") if "rule_id" not in request: @@ -175,16 +189,16 @@ def _validate_request(self, request: Dict[str, str]) -> str: def _fetch_filename(self, rule_id: str) -> str: """ - Get the filename of the rule to be enabled. + Fetch the filename containing the rule to be enabled. Args: rule_id (str): The id of the rule to be enabled. Raises: - RuntimeError: If the filename cannot be obtained. + ValueError: If the filename could not be fetched. Returns: - str: The filename of the rule to be enabled. + str: The filename containing the rule. """ filename_data = self.wazuh_http_requests.get_request( "rules", @@ -196,16 +210,16 @@ def _fetch_filename(self, rule_id: str) -> str: def _fetch_file_content(self, filename: str) -> Any: """ - Get the content of the rule file. + Fetch the content of the file containing the rule to be enabled. Args: - filename (str): The filename of the rule to be enabled. + filename (str): The filename containing the rule to be enabled. Raises: - RuntimeError: If the file content cannot be obtained. + ValueError: If the file content could not be fetched. Returns: - Any: The content of the rule file. + Any: The content of the file. """ file_content_data = self.wazuh_http_requests.get_request( f"rules/files/{filename}", @@ -216,10 +230,10 @@ def _fetch_file_content(self, filename: str) -> Any: def _get_previous_level(self, rule_id: str) -> str: """ - Get the previous level of the rule from the `disabled_rules` table. + Fetch the previous level of a rule from the `disabled_rules` table. Args: - rule_id (str):The rule id to be enabled. + rule_id (str): The id of the rule. Raises: ValueError: If the rule was not previously disabled. @@ -239,15 +253,15 @@ def _set_level_previous( previous_level: str, ) -> Any: """ - Set the level of the rule to be enabled to the previous level. + Set the level of a rule to its previous level in the file content. Args: - file_content (Any): The content of the rule to be enabled. - rule_id (str): The id of the rule to be enabled. - previous_level (str): The previous level of the rule to be enabled. + file_content (Any): The content of the file containing the rule. + rule_id (str): The id of the rule. + previous_level (str): The previous level of the rule. Returns: - Any: The content of the rule with the level set to the previous level. + Any: The updated file content with the level of the rule set to the previous level. """ logger.info( f"Setting rule {rule_id} level to {previous_level} for file_content: {file_content}", @@ -273,16 +287,13 @@ def _set_level_previous( def _json_to_xml(self, file_content: Any) -> str: """ - Convert the rule content from JSON to XML. + Convert file content from JSON to XML format. Args: - file_content (Any): The content of the rule to be enabled. - - Raises: - Exception: If the JSON to XML conversion fails. + file_content (Any): The content of the file containing the rule. Returns: - str: The content of the rule to be enabled in XML format. + str: The file content in XML format. """ logger.info(f"Converting file_content to XML: {file_content}") @@ -307,10 +318,10 @@ def _json_to_xml(self, file_content: Any) -> str: def _delete_rule_from_db(self, rule_id: str): """ - Delete the rule from the `disabled_rules` table. + Delete a rule from the `disabled_rules` table. Args: - rule_id (str): The rule id to be deleted. + rule_id (str): The id of the rule to be deleted. """ disabled_rule = DisabledRules.query.filter_by(rule_id=rule_id).first() db.session.delete(disabled_rule) @@ -318,11 +329,11 @@ def _delete_rule_from_db(self, rule_id: str): def _put_updated_rule(self, filename: str, xml_content: str): """ - PUT the updated rule to the Wazuh Manager. + Upload the updated rule to the Wazuh Manager. Args: - filename (str): The filename of the rule. - xml_content (str): The XML content of the rule. + filename (str): The filename containing the rule. + xml_content (str): The updated rule in XML format. Raises: RuntimeError: If the PUT operation fails. diff --git a/backend/app/services/WazuhManager/universal.py b/backend/app/services/WazuhManager/universal.py index b3868eef..789ec1f7 100644 --- a/backend/app/services/WazuhManager/universal.py +++ b/backend/app/services/WazuhManager/universal.py @@ -1,5 +1,9 @@ import requests from loguru import logger +from typing import Optional +from typing import Tuple +from typing import Dict +from typing import Union from app.models.connectors import Connector from app.models.connectors import connector_factory @@ -7,17 +11,36 @@ class UniversalService: """ - A service class that encapsulates the logic for polling messages from the Wazuh-Manager API. + A service class to manage operations with the Wazuh-Manager API. + + Attributes: + connector_url (str): The URL of the Wazuh Manager. + connector_username (str): The username for the Wazuh Manager. + connector_password (str): The password for the Wazuh Manager. """ def __init__(self) -> None: + """ + Initialize an UniversalService instance. + + The Wazuh-Manager details are collected upon initialization. + """ ( self.connector_url, self.connector_username, self.connector_password, ) = self.collect_wazuhmanager_details("Wazuh-Manager") - def collect_wazuhmanager_details(self, connector_name: str): + def collect_wazuhmanager_details(self, connector_name: str) -> Tuple[Optional[str], Optional[str], Optional[str]]: + """ + Collect the details of the Wazuh Manager. + + Args: + connector_name (str): The name of the connector, in this case "Wazuh-Manager". + + Returns: + Tuple[Optional[str], Optional[str], Optional[str]]: The URL, username, and password of the Wazuh Manager. + """ connector_instance = connector_factory.create(connector_name, connector_name) if connector_instance.verify_connection(): connection_details = Connector.get_connector_info_from_db(connector_name) @@ -30,12 +53,12 @@ def collect_wazuhmanager_details(self, connector_name: str): logger.error(f"Connection to {connector_name} failed.") return None, None, None - def get_auth_token(self): + def get_auth_token(self) -> Optional[str]: """ - Gets the authentication token from the Wazuh-Manager API. + Get the authentication token from the Wazuh-Manager API. Returns: - str: The authentication token. + Optional[str]: The authentication token. If the request fails, returns None. """ try: response = requests.get( @@ -51,12 +74,12 @@ def get_auth_token(self): logger.info(f"Authentication token: {auth_token}") return auth_token - def restart_service(self): + def restart_service(self) -> Dict[str, Union[str, bool]]: """ Restart the Wazuh Manager service. Returns: - json: A JSON response containing the updated agent information. + Dict[str, Union[str, bool]]: A dictionary containing the status of the operation. """ headers = {"Authorization": f"Bearer {self.get_auth_token()}"} try: diff --git a/backend/app/services/WazuhManager/vulnerability.py b/backend/app/services/WazuhManager/vulnerability.py index 2294b1b0..a7bb43f5 100644 --- a/backend/app/services/WazuhManager/vulnerability.py +++ b/backend/app/services/WazuhManager/vulnerability.py @@ -12,10 +12,18 @@ class WazuhHttpRequests: """ Class to handle HTTP requests to the Wazuh API. + + Attributes: + connector_url (str): The URL of the Wazuh Manager. + wazuh_auth_token (str): The Wazuh API authentication token. + headers (Dict[str, str]): Headers to use for the HTTP requests. """ + def __init__(self, connector_url: str, wazuh_auth_token: str) -> None: """ + Initialize a WazuhHttpRequests instance. + Args: connector_url (str): The URL of the Wazuh Manager. wazuh_auth_token (str): The Wazuh API authentication token. @@ -30,7 +38,7 @@ def get_request( params: Optional[Dict[str, Any]] = None, ) -> Optional[Dict[str, Any]]: """ - Function to handle GET requests. + Send a GET request to the specified endpoint. Args: endpoint (str): The endpoint to make a GET request to. @@ -58,11 +66,18 @@ def get_request( class VulnerabilityService: """ - A service class that encapsulates the logic for pulling API data from Wazuh Manager. + Class to manage operations with the Wazuh Manager API related to vulnerabilities. + + Attributes: + universal_service (UniversalService): The UniversalService instance to use. + auth_token (str): The Wazuh API authentication token. + wazuh_http_requests (WazuhHttpRequests): A WazuhHttpRequests instance to handle HTTP requests. """ def __init__(self, universal_service: UniversalService) -> None: """ + Initialize a VulnerabilityService instance. + Args: universal_service (UniversalService): The UniversalService instance to use. """ @@ -75,7 +90,7 @@ def __init__(self, universal_service: UniversalService) -> None: def agent_vulnerabilities(self, agent_id: str) -> List[Dict[str, Any]]: """ - Get the vulnerabilities of an agent from Wazuh Manager. + Retrieve and process vulnerabilities for a specified agent. Args: agent_id (str): The id of the agent to get vulnerabilities for. @@ -98,10 +113,10 @@ def _process_agent_vulnerabilities( response: Dict[str, Any], ) -> List[Dict[str, Any]]: """ - Process the vulnerabilities of an agent from Wazuh Manager. + Process the raw vulnerabilities data for an agent. Args: - response (dict): The response from Wazuh Manager containing agent vulnerabilities. + response (Dict[str, Any]): The raw vulnerabilities data from Wazuh Manager. Returns: List[Dict[str, Any]]: A list of processed vulnerabilities. diff --git a/backend/docs/alerts.md b/backend/docs/alerts.md deleted file mode 100644 index 1b8706f9..00000000 --- a/backend/docs/alerts.md +++ /dev/null @@ -1,10 +0,0 @@ -## Alerts Overview - -### Alert Routes - -::: app.routes.alerts -
- -### Alert Services - -::: app.services.WazuhIndexer.alerts diff --git a/backend/docs/cases.md b/backend/docs/cases.md deleted file mode 100644 index ce85b257..00000000 --- a/backend/docs/cases.md +++ /dev/null @@ -1,15 +0,0 @@ -## Cases Overview - -### Cases Model - -::: app.models.cases -
- -### Cases Routes - -::: app.routes.dfir_iris -
- -### Cases Services - -::: app.services.DFIR_IRIS.cases diff --git a/backend/docs/dfiriris.md b/backend/docs/dfiriris.md new file mode 100644 index 00000000..84703bf1 --- /dev/null +++ b/backend/docs/dfiriris.md @@ -0,0 +1,53 @@ +## Dfir-Iris Overview + +### Cases Model + +::: app.models.cases +
+ +### Cases Routes + +::: app.routes.dfir_iris.get_cases +::: app.routes.dfir_iris.get_case +
+ +### Notes Routes + +::: app.routes.dfir_iris.get_case_notes +::: app.routes.dfir_iris.create_case_note +
+ +### Assets Routes + +::: app.routes.dfir_iris.get_case_assets +
+ +### Alerts Routes + +::: app.routes.dfir_iris.get_alerts +
+ +### Alerts Services + +::: app.services.DFIR_IRIS.alerts +
+ +### Cases Services + +::: app.services.DFIR_IRIS.cases +
+ +### Assets Services + +::: app.services.DFIR_IRIS.assets +
+ +### Notes Services + +::: app.services.DFIR_IRIS.notes +
+ +### Universal Services + +::: app.services.DFIR_IRIS.universal +
diff --git a/backend/docs/shuffle.md b/backend/docs/shuffle.md new file mode 100644 index 00000000..bca835ed --- /dev/null +++ b/backend/docs/shuffle.md @@ -0,0 +1,15 @@ +## Shuffle Overview + +### Shuffle Routes + +::: app.routes.shuffle +
+ +### Workflows Services + +::: app.services.Shuffle.workflows +
+ +### Universal Services + +::: app.services.Shuffle.universal diff --git a/backend/docs/artifacts.md b/backend/docs/velociraptor.md similarity index 91% rename from backend/docs/artifacts.md rename to backend/docs/velociraptor.md index 9602376c..6505b604 100644 --- a/backend/docs/artifacts.md +++ b/backend/docs/velociraptor.md @@ -1,4 +1,4 @@ -## Artifacts Overview +## Velociraptor Overview ### Artifacts Model diff --git a/backend/docs/wazuhindexer.md b/backend/docs/wazuhindexer.md new file mode 100644 index 00000000..c3610972 --- /dev/null +++ b/backend/docs/wazuhindexer.md @@ -0,0 +1,25 @@ +## Wazuh-Indexer Overview + +### Alert Routes +::: app.routes.alerts +
+ +### Wazuh-Indexer Routes +::: app.routes.wazuhindexer +
+ +### Alert Services + +::: app.services.WazuhIndexer.alerts + +### Cluster Services + +::: app.services.WazuhIndexer.cluster + +### Index Services + +::: app.services.WazuhIndexer.index + +### Universal Services + +::: app.services.WazuhIndexer.universal diff --git a/backend/docs/wazuhmanager.md b/backend/docs/wazuhmanager.md new file mode 100644 index 00000000..3217a17a --- /dev/null +++ b/backend/docs/wazuhmanager.md @@ -0,0 +1,22 @@ +## Wazuh-Manager Overview + +### Wazuh-Manager Model + +::: app.models.rules +::: app.models.agents +
+ +### Wazuh-Manager Routes + +::: app.routes.agents.sync_agents +::: app.routes.agents.delete_agent +::: app.routes.agents.get_agent_vulnerabilities +
+ +### Wazuh-Manager Services +::: app.services.WazuhManager.agent +::: app.services.WazuhManager.disabled_rule +::: app.services.WazuhManager.enabled_rule +::: app.services.WazuhManager.universal +::: app.services.WazuhManager.vulnerability + diff --git a/backend/mkdocs.yml b/backend/mkdocs.yml index cd11a7af..1813028d 100644 --- a/backend/mkdocs.yml +++ b/backend/mkdocs.yml @@ -35,10 +35,12 @@ nav: - Home: index.md - Connectors: connectors.md - Agents: agents.md - - Alerts: alerts.md - - Artifacts: artifacts.md - - Cases: cases.md + - Wazuh-Indexer: wazuhindexer.md + - Dfir-Iris: dfiriris.md - Graylog: graylog.md + - Wazuh-Manager: wazuhmanager.md + - Velociraptor: velociraptor.md + - Shuffle: shuffle.md markdown_extensions: - pymdownx.highlight: diff --git a/backend/site/404.html b/backend/site/404.html index 1c867e2e..876e22d8 100644 --- a/backend/site/404.html +++ b/backend/site/404.html @@ -1,314 +1,464 @@ + - - - - - - - + + + + + + + + + + + + + + SOCFortress CoPilot - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - -
-
- -
- -
- -
- - -
-
-
-
-
- -
-
-
- -
-
-

404 - Not found

-
-
- - -
- - -
- - + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ +

404 - Not found

+ +
+
+ + + - - - - - +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/agents/index.html b/backend/site/agents/index.html index ed87e4da..0265839d 100644 --- a/backend/site/agents/index.html +++ b/backend/site/agents/index.html @@ -1,566 +1,764 @@ + - - - + + + + + + + + + + + + + + + + + + + Agents - SOCFortress CoPilot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + - - +

Agents

- - +

Agents Overview

+

Agent Model

- Agents - SOCFortress CoPilot - - - - - - - - - - - - +
- - - - - - - -
- -
- -
- -
- - -
-
- - -
-
-

Agents

- -

Agents Overview

-

Agent Model

- -
- -
-
-
-

- AgentMetadata -

- -
-

- Bases: - db.Model -

- -

- Class for agent metadata which stores the agent ID, IP address, - hostname, OS, last seen timestamp, and boolean for critical asset. - This class inherits from SQLAlchemy's Model class. -

- -
- Source code in app\models\agents.py -
- - -
-
-
14
+
+  
+ + + +
+ + + + + + + + +
+ + + +

+ AgentMetadata + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for agent metadata which stores the agent ID, IP address, hostname, OS, last seen timestamp, +and boolean for critical asset. This class inherits from SQLAlchemy's Model class.

+ + +
+ Source code in app\models\agents.py +
- - -
14
 15
 16
 17
@@ -627,12 +825,7 @@ 

78 79 80 -81

-
-
-
-
class AgentMetadata(db.Model):
+81
class AgentMetadata(db.Model):
     """
     Class for agent metadata which stores the agent ID, IP address, hostname, OS, last seen timestamp,
     and boolean for critical asset. This class inherits from SQLAlchemy's Model class.
@@ -700,56 +893,44 @@ 

""" db.session.add(self) db.session.commit() -

-
-
-
-
- -
-
-

- __init__(agent_id, - ip_address, os, hostname, - critical_asset, - last_seen) -

- -
-

Initialize a new instance of the AgentMetadata class.

-

- :param agent_id: Unique ID for the agent. :param - ip_address: IP address of the agent. :param os: - Operating system of the agent. :param hostname: Hostname - of the agent. :param critical_asset: Boolean value - indicating if the agent is a critical asset. :param - last_seen: Timestamp of when the agent was last seen. -

- -
- - Source code in app\models\agents.py - -
- - -
-
-
28
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(agent_id, ip_address, os, hostname, critical_asset, last_seen) + +

+ + +
+ +

Initialize a new instance of the AgentMetadata class.

+

:param agent_id: Unique ID for the agent. +:param ip_address: IP address of the agent. +:param os: Operating system of the agent. +:param hostname: Hostname of the agent. +:param critical_asset: Boolean value indicating if the agent is a critical asset. +:param last_seen: Timestamp of when the agent was last seen.

+ +
+ Source code in app\models\agents.py +
- - -
28
 29
 30
 31
@@ -773,12 +954,7 @@ 

49 50 51 -52

-
-
-
-
def __init__(
+52
def __init__(
     self,
     agent_id: str,
     ip_address: str,
@@ -803,249 +979,177 @@ 

self.hostname = hostname self.critical_asset = critical_asset self.last_seen = last_seen -

-
-
-
-
-
-
- -
-

- __repr__() -

- -
-

- Returns a string representation of the AgentMetadata - instance. -

-

:return: A string representation of the agent ID.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
54
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the AgentMetadata instance.

+

:return: A string representation of the agent ID.

+ +
+ Source code in app\models\agents.py +
- - -
54
 55
 56
 57
 58
 59
-60
-
-
-
-
def __repr__(self) -> str:
+60
def __repr__(self) -> str:
     """
     Returns a string representation of the AgentMetadata instance.
 
     :return: A string representation of the agent ID.
     """
     return f"<AgentMetadata {self.agent_id}>"
-
-
-
-
-
-
-
- -
-

- commit_wazuh_agent_to_db() -

- -
-

Commits the agent to the database.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
76
+
+
+
+ +
+ +
+ + + +

+commit_wazuh_agent_to_db() + +

+ + +
+ +

Commits the agent to the database.

+ +
+ Source code in app\models\agents.py +
- - -
76
 77
 78
 79
 80
-81
-
-
-
-
def commit_wazuh_agent_to_db(self):
+81
def commit_wazuh_agent_to_db(self):
     """
     Commits the agent to the database.
     """
     db.session.add(self)
     db.session.commit()
-
-
-
-
-
-
-
- -
-

- mark_as_critical() -

- -
-

Marks the agent as a critical asset.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
62
+
+
+
+ +
+ +
+ + + +

+mark_as_critical() + +

+ + +
+ +

Marks the agent as a critical asset.

+ +
+ Source code in app\models\agents.py +
- - -
62
 63
 64
 65
 66
-67
-
-
-
-
def mark_as_critical(self):
+67
def mark_as_critical(self):
     """
     Marks the agent as a critical asset.
     """
     self.critical_asset = True
     db.session.commit()
-
-
-
-
-
-
-
- -
-

- mark_as_non_critical() -

- -
-

Marks the agent as a non-critical asset.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
69
+
+
+
+ +
+ +
+ + + +

+mark_as_non_critical() + +

+ + +
+ +

Marks the agent as a non-critical asset.

+ +
+ Source code in app\models\agents.py +
- - -
69
 70
 71
 72
 73
-74
-
-
-
-
def mark_as_non_critical(self):
+74
def mark_as_non_critical(self):
     """
     Marks the agent as a non-critical asset.
     """
     self.critical_asset = False
     db.session.commit()
-
-
-
-
-
-
-
-
-
-
- -
-

- AgentMetadataSchema -

- -
-

- Bases: - ma.Schema -

- -

- Schema for serializing and deserializing instances of the - AgentMetadata class. -

- -
- Source code in app\models\agents.py -
- - -
-
-
 84
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ AgentMetadataSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the AgentMetadata class.

+ + +
+ Source code in app\models\agents.py +
- - -
 84
  85
  86
  87
@@ -1063,12 +1167,7 @@ 

99 100 101 -102

-
-
-
-
class AgentMetadataSchema(ma.Schema):
+102
class AgentMetadataSchema(ma.Schema):
     """
     Schema for serializing and deserializing instances of the AgentMetadata class.
     """
@@ -1087,39 +1186,40 @@ 

"critical_asset", "last_seen", ) -

-
-
-
-
- -
-
-

- Meta -

- -
-

- Meta class defines the fields to be - serialized/deserialized. -

- -
- - Source code in app\models\agents.py - -
- - -
-
-
 89
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\agents.py +
- - -
 89
  90
  91
  92
@@ -1132,12 +1232,7 @@ 

99 100 101 -102

-
-
-
-
class Meta:
+102
class Meta:
     """
     Meta class defines the fields to be serialized/deserialized.
     """
@@ -1151,104 +1246,134 @@ 

"critical_asset", "last_seen", ) -

-
-
-
-
- -
-
-
-
-
-
-
-
-
-


-

Agent Routes

- -
- -
-
-
-

- delete_agent(agent_id) -

- -
-

Endpoint to delete an agent.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

The ID of the agent to be deleted.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response indicating whether the deletion - was successful. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
81
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + +
+ +
+ +


+

Agent Routes

+ + +
+ + + +
+ + + +
+ + + + + + + + + +
+ + + +

+delete_agent(agent_id) + +

+ + +
+ +

Endpoint to delete an agent.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to be deleted.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response indicating whether the deletion was successful.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
81
 82
 83
 84
@@ -1264,12 +1389,7 @@ 

94 95 96 -97

-
-
-
-
@bp.route("/agents/<agent_id>/delete", methods=["POST"])
+97
@bp.route("/agents/<agent_id>/delete", methods=["POST"])
 def delete_agent(agent_id: str) -> Any:
     """
     Endpoint to delete an agent.
@@ -1286,90 +1406,79 @@ 

agent_service.delete_agent(agent_id=agent_id) return result -

-
-
-
-
-
-
- -
-

- get_agent(agent_id) -

- -
-

Endpoint to get the details of a specific agent.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

The ID of the agent to retrieve.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response containing the details of the - agent. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
27
+
+
+
+ +
+ +
+ + + +

+get_agent(agent_id) + +

+ + +
+ +

Endpoint to get the details of a specific agent.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to retrieve.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the details of the agent.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
27
 28
 29
 30
@@ -1380,12 +1489,7 @@ 

35 36 37 -38

-
-
-
-
@bp.route("/agents/<agent_id>", methods=["GET"])
+38
@bp.route("/agents/<agent_id>", methods=["GET"])
 def get_agent(agent_id: str) -> Any:
     """
     Endpoint to get the details of a specific agent.
@@ -1397,97 +1501,79 @@ 

service = AgentService() agent = service.get_agent(agent_id=agent_id) return agent -

-
-
-
-
-
-
- -
-

- get_agent_vulnerabilities(agent_id) -

- -
-

Endpoint to get the vulnerabilities of a specific agent.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

- The ID of the agent whose vulnerabilities are to - be fetched. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response containing the vulnerabilities - of the agent. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
100
+
+
+
+ +
+ +
+ + + +

+get_agent_vulnerabilities(agent_id) + +

+ + +
+ +

Endpoint to get the vulnerabilities of a specific agent.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent whose vulnerabilities are to be fetched.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the vulnerabilities of the agent.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
100
 101
 102
 103
@@ -1502,12 +1588,7 @@ 

112 113 114 -115

-
-
-
-
@bp.route("/agents/<agent_id>/vulnerabilities", methods=["GET"])
+115
@bp.route("/agents/<agent_id>/vulnerabilities", methods=["GET"])
 def get_agent_vulnerabilities(agent_id: str) -> Any:
     """
     Endpoint to get the vulnerabilities of a specific agent.
@@ -1523,65 +1604,51 @@ 

agent_id=agent_id, ) return agent_vulnerabilities -

-
-
-
-
-
-
- -
-

- get_agents() -

- -
-

- Endpoint to get a list of all agents. It processes each agent and - returns the results. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response containing the list of all - available agents along with their connection - verification status. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
15
+
+
+
+ +
+ +
+ + + +

+get_agents() + +

+ + +
+ +

Endpoint to get a list of all agents. It processes each agent and returns the results.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the list of all available agents along with their connection verification status.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
15
 16
 17
 18
@@ -1590,12 +1657,7 @@ 

21 22 23 -24

-
-
-
-
@bp.route("/agents", methods=["GET"])
+24
@bp.route("/agents", methods=["GET"])
 def get_agents() -> Any:
     """
     Endpoint to get a list of all agents. It processes each agent and returns the results.
@@ -1605,90 +1667,79 @@ 

service = AgentService() agents = service.get_all_agents() return agents -

-
-
-
-
-
-
- -
-

- mark_as_critical(agent_id) -

- -
-

Endpoint to mark an agent as critical.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

The ID of the agent to mark as critical.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response containing the updated agent - information after being marked as critical. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
41
+
+
+
+ +
+ +
+ + + +

+mark_as_critical(agent_id) + +

+ + +
+ +

Endpoint to mark an agent as critical.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to mark as critical.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the updated agent information after being marked as critical.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
41
 42
 43
 44
@@ -1699,12 +1750,7 @@ 

49 50 51 -52

-
-
-
-
@bp.route("/agents/<agent_id>/critical", methods=["POST"])
+52
@bp.route("/agents/<agent_id>/critical", methods=["POST"])
 def mark_as_critical(agent_id: str) -> Any:
     """
     Endpoint to mark an agent as critical.
@@ -1716,61 +1762,51 @@ 

service = AgentService() result = service.mark_agent_as_critical(agent_id=agent_id) return result -

-
-
-
-
-
-
- -
-

- sync_agents() -

- -
-

Endpoint to synchronize all agents.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response containing the updated - information of all synchronized agents. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
69
+
+
+
+ +
+ +
+ + + +

+sync_agents() + +

+ + +
+ +

Endpoint to synchronize all agents.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the updated information of all synchronized agents.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
69
 70
 71
 72
@@ -1779,12 +1815,7 @@ 

75 76 77 -78

-
-
-
-
@bp.route("/agents/sync", methods=["POST"])
+78
@bp.route("/agents/sync", methods=["POST"])
 def sync_agents() -> Any:
     """
     Endpoint to synchronize all agents.
@@ -1794,90 +1825,79 @@ 

service = AgentSyncService() result = service.sync_agents() return jsonify(result) -

-
-
-
-
-
-
- -
-

- unmark_agent_critical(agent_id) -

- -
-

Endpoint to unmark an agent as critical.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

The ID of the agent to unmark as critical.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json - Any - -
-

- A JSON response containing the updated agent - information after being unmarked as critical. -

-
-
- -
- Source code in app\routes\agents.py -
- - -
-
-
55
+
+
+
+ +
+ +
+ + + +

+unmark_agent_critical(agent_id) + +

+ + +
+ +

Endpoint to unmark an agent as critical.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to unmark as critical.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the updated agent information after being unmarked as critical.

+
+
+ +
+ Source code in app\routes\agents.py +
- - -
55
 56
 57
 58
@@ -1888,12 +1908,7 @@ 

63 64 65 -66

-
-
-
-
@bp.route("/agents/<agent_id>/noncritical", methods=["POST"])
+66
@bp.route("/agents/<agent_id>/noncritical", methods=["POST"])
 def unmark_agent_critical(agent_id: str) -> Any:
     """
     Endpoint to unmark an agent as critical.
@@ -1905,43 +1920,59 @@ 

service = AgentService() result = service.mark_agent_as_non_critical(agent_id=agent_id) return result -

-
-
-
-
-
-
-
-
-
-


-

Agent Services

- -
- -
-
-
-

- AgentService -

- -
-

A service class that encapsulates the logic for managing agents.

- -
- - Source code in app\services\agents\agents.py - -
- - -
-
-
 21
+
+
+
+ +
+ + + +
+ +
+ +


+

Agent Services

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ AgentService + + +

+ + +
+ + +

A service class that encapsulates the logic for managing agents.

+ + +
+ Source code in app\services\agents\agents.py +
- - -
 21
  22
  23
  24
@@ -2087,12 +2118,7 @@ 

164 165 166 -167

-
-
-
-
class AgentService:
+167
class AgentService:
     """
     A service class that encapsulates the logic for managing agents.
     """
@@ -2239,107 +2265,88 @@ 

"message": f"Failed to delete agent with ID {agent_id}", "success": False, } -

-
-
-
-
- -
-
-

- create_agent(agent) -

- -
-

Creates a new agent in the database.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent - dict - -
-

- A dictionary containing the - information of an agent. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Optional[AgentMetadata] - -
-

- The agent object if the agent was - successfully created, None - otherwise. -

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
102
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+create_agent(agent) + +

+ + +
+ +

Creates a new agent in the database.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent + dict + +
+

A dictionary containing the information of an agent.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Optional[AgentMetadata] + +
+

The agent object if the agent was successfully created, None otherwise.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
102
 103
 104
 105
@@ -2379,12 +2386,7 @@ 

139 140 141 -142

-
-
-
-
def create_agent(self, agent: Dict[str, str]) -> Optional[AgentMetadata]:
+142
def create_agent(self, agent: Dict[str, str]) -> Optional[AgentMetadata]:
     """
     Creates a new agent in the database.
 
@@ -2425,121 +2427,89 @@ 

except Exception as e: logger.error(f"Failed to create agent: {e}") return None -

-
-
-
-
-
-
- -
-

- delete_agent_db(agent_id) -

- -
-

- Deletes a specific agent from the database using its ID. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

The ID of the agent to delete.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool]] - -
-

- A dictionary representing a success - message if the operation was - successful, otherwise an error -

-
-
- Dict[str, - Union[str, bool]] - -
-

message.

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
144
+
+
+
+ +
+ +
+ + + +

+delete_agent_db(agent_id) + +

+ + +
+ +

Deletes a specific agent from the database using its ID.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to delete.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool]] + +
+

A dictionary representing a success message if the operation was successful, otherwise an error

+
+
+ Dict[str, Union[str, bool]] + +
+

message.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
144
 145
 146
 147
@@ -2562,12 +2532,7 @@ 

164 165 166 -167

-
-
-
-
def delete_agent_db(self, agent_id: str) -> Dict[str, Union[str, bool]]:
+167
def delete_agent_db(self, agent_id: str) -> Dict[str, Union[str, bool]]:
     """
     Deletes a specific agent from the database using its ID.
 
@@ -2591,123 +2556,89 @@ 

"message": f"Failed to delete agent with ID {agent_id}", "success": False, } -

-
-
-
-
-
-
- -
-

- get_agent(agent_id) -

- -
-

- Retrieves a specific agent from the database using its - ID. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

The ID of the agent to retrieve.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool]] - -
-

- A dictionary representing the - serialized data of the agent if - found, otherwise a message - indicating -

-
-
- Dict[str, - Union[str, bool]] - -
-

that the agent was not found.

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
36
+
+
+
+ +
+ +
+ + + +

+get_agent(agent_id) + +

+ + +
+ +

Retrieves a specific agent from the database using its ID.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to retrieve.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool]] + +
+

A dictionary representing the serialized data of the agent if found, otherwise a message indicating

+
+
+ Dict[str, Union[str, bool]] + +
+

that the agent was not found.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
36
 37
 38
 39
@@ -2721,12 +2652,7 @@ 

47 48 49 -50

-
-
-
-
def get_agent(self, agent_id: str) -> Dict[str, Union[str, bool]]:
+50
def get_agent(self, agent_id: str) -> Dict[str, Union[str, bool]]:
     """
     Retrieves a specific agent from the database using its ID.
 
@@ -2741,73 +2667,51 @@ 

if agent is None: return {"message": f"Agent with ID {agent_id} not found"} return agent_metadata_schema.dump(agent) -

-
-
-
-
-
-
- -
-

- get_all_agents() -

- -
-

Retrieves all agents from the database.

- -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- List[Dict[str, - Union[str, bool]]] - -
-

- List[dict]: A list of dictionaries - where each dictionary represents the - serialized data of an agent. -

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
26
+
+
+
+ +
+ +
+ + + +

+get_all_agents() + +

+ + +
+ +

Retrieves all agents from the database.

+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Dict[str, Union[str, bool]]] + +
+

List[dict]: A list of dictionaries where each dictionary represents the serialized data of an agent.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
26
 27
 28
 29
@@ -2815,12 +2719,7 @@ 

31 32 33 -34

-
-
-
-
def get_all_agents(self) -> List[Dict[str, Union[str, bool]]]:
+34
def get_all_agents(self) -> List[Dict[str, Union[str, bool]]]:
     """
     Retrieves all agents from the database.
 
@@ -2829,122 +2728,89 @@ 

""" agents = db.session.query(AgentMetadata).all() return agent_metadatas_schema.dump(agents) -

-
-
-
-
-
-
- -
-

- mark_agent_as_critical(agent_id) -

- -
-

Marks a specific agent as critical.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

- The ID of the agent to mark as - critical. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool]] - -
-

- A dictionary representing a success - message if the operation was - successful, otherwise an error -

-
-
- Dict[str, - Union[str, bool]] - -
-

message.

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
52
+
+
+
+ +
+ +
+ + + +

+mark_agent_as_critical(agent_id) + +

+ + +
+ +

Marks a specific agent as critical.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to mark as critical.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool]] + +
+

A dictionary representing a success message if the operation was successful, otherwise an error

+
+
+ Dict[str, Union[str, bool]] + +
+

message.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
52
 53
 54
 55
@@ -2967,12 +2833,7 @@ 

72 73 74 -75

-
-
-
-
def mark_agent_as_critical(self, agent_id: str) -> Dict[str, Union[str, bool]]:
+75
def mark_agent_as_critical(self, agent_id: str) -> Dict[str, Union[str, bool]]:
     """
     Marks a specific agent as critical.
 
@@ -2996,122 +2857,89 @@ 

"success": False, } return {"message": f"Agent {agent_id} marked as critical", "success": True} -

-
-
-
-
-
-
- -
-

- mark_agent_as_non_critical(agent_id) -

- -
-

Marks a specific agent as non-critical.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
agent_id - str - -
-

- The ID of the agent to mark as - non-critical. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool]] - -
-

- A dictionary representing a success - message if the operation was - successful, otherwise an error -

-
-
- Dict[str, - Union[str, bool]] - -
-

message.

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
 77
+
+
+
+ +
+ +
+ + + +

+mark_agent_as_non_critical(agent_id) + +

+ + +
+ +

Marks a specific agent as non-critical.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to mark as non-critical.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool]] + +
+

A dictionary representing a success message if the operation was successful, otherwise an error

+
+
+ Dict[str, Union[str, bool]] + +
+

message.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
 77
  78
  79
  80
@@ -3134,12 +2962,7 @@ 

97 98 99 -100

-
-
-
-
def mark_agent_as_non_critical(self, agent_id: str) -> Dict[str, Union[str, bool]]:
+100
def mark_agent_as_non_critical(self, agent_id: str) -> Dict[str, Union[str, bool]]:
     """
     Marks a specific agent as non-critical.
 
@@ -3163,38 +2986,38 @@ 

"success": False, } return {"message": f"Agent {agent_id} marked as non-critical", "success": True} -

-
-
-
-
-
-
-
-
-
- -
-

- AgentSyncService -

- -
-
- - Source code in app\services\agents\agents.py - -
- - -
-
-
170
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ AgentSyncService + + +

+ + +
+ + + +
+ Source code in app\services\agents\agents.py +
- - -
170
 171
 172
 173
@@ -3303,12 +3126,7 @@ 

276 277 278 -279

-
-
-
-
class AgentSyncService:
+279
class AgentSyncService:
     def __init__(self):
         self.agent_service = AgentService()
 
@@ -3418,80 +3236,60 @@ 

"success": True, "agents_added": agents_added_list, } -

-
-
-
-
- -
-
-

- collect_wazuh_agents(connection_url, - wazuh_auth_token) -

- -
-

- Collects the information of all agents from the Wazuh - API. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
list - Optional[List[Dict[str, str]]] - -
-

- A list containing the information of - all Wazuh agents. -

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
196
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+collect_wazuh_agents(connection_url, wazuh_auth_token) + +

+ + +
+ +

Collects the information of all agents from the Wazuh API.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
list + Optional[List[Dict[str, str]]] + +
+

A list containing the information of all Wazuh agents.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
196
 197
 198
 199
@@ -3531,12 +3329,7 @@ 

233 234 235 -236

-
-
-
-
def collect_wazuh_agents(
+236
def collect_wazuh_agents(
     self,
     connection_url: str,
     wazuh_auth_token: str,
@@ -3577,84 +3370,51 @@ 

except Exception as e: logger.error(f"Failed to collect Wazuh Agents: {e}") return None -

-
-
-
-
-
-
- -
-

- collect_wazuh_details(connector_name) -

- -
-

- Collects the information of all Wazuh API credentials - using the WazuhIndexerConnector class details. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
tuple - Tuple[Optional[str], - Optional[str], - Optional[str]] - -
-

- A tuple containing the connection - URL, username, and password. -

-
-
- -
- - Source code in - app\services\agents\agents.py - -
- - -
-
-
174
+
+
+
+ +
+ +
+ + + +

+collect_wazuh_details(connector_name) + +

+ + +
+ +

Collects the information of all Wazuh API credentials using the WazuhIndexerConnector class details.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
tuple + Tuple[Optional[str], Optional[str], Optional[str]] + +
+

A tuple containing the connection URL, username, and password.

+
+
+ +
+ Source code in app\services\agents\agents.py +
- - -
174
 175
 176
 177
@@ -3674,12 +3434,7 @@ 

191 192 193 -194

-
-
-
-
def collect_wazuh_details(
+194
def collect_wazuh_details(
     self,
     connector_name: str,
 ) -> Tuple[Optional[str], Optional[str], Optional[str]]:
@@ -3700,103 +3455,82 @@ 

) else: return None, None, None -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
+
+
+
- -
- - -
-
-
-
+
- - - - - + + +
+ +
+ +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/alerts/index.html b/backend/site/alerts/index.html index 152a916a..207fdea0 100644 --- a/backend/site/alerts/index.html +++ b/backend/site/alerts/index.html @@ -1,411 +1,622 @@ + - - - + + + + + + + + + + + + + + + + + + + Alerts - SOCFortress CoPilot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + - - +

Alerts

- - +

Alerts Overview

+

Alert Routes

- Alerts - SOCFortress CoPilot - - - - - - - - - - - - +
- - - - - - - -
- -
- -
- -
- - -
-
-
-
-
- -
-
-
- -
-
-

Alerts

- -

Alerts Overview

-

Alert Routes

- -
- -
-
-
-

- get_alerts() -

- -
-

Retrieves all alerts from the AlertsService.

-

- This endpoint retrieves all available alerts from the AlertsService. - It does this by creating an instance of the AlertsService class and - calling its collect_alerts method. The result is a list - of all alerts currently available. -

- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
jsonify - jsonify - -
-

- A JSON response containing a list of alerts. - Each item in the list is a dictionary - representing an alert, -

-
-
- jsonify - -
-

containing all its associated data.

-
-
- -
- Source code in app\routes\alerts.py -
- - -
-
-
 9
+
+  
+ + + +
+ + + + + + + + + +
+ + + +

+get_alerts() + +

+ + +
+ +

Retrieves all alerts from the AlertsService.

+

This endpoint retrieves all available alerts from the AlertsService. It does this by creating an instance of +the AlertsService class and calling its collect_alerts method. The result is a list of all alerts currently +available.

+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
jsonify + jsonify + +
+

A JSON response containing a list of alerts. Each item in the list is a dictionary representing an alert,

+
+
+ jsonify + +
+

containing all its associated data.

+
+
+ +
+ Source code in app\routes\alerts.py +
- - -
 9
 10
 11
 12
@@ -420,12 +631,7 @@ 

21 22 23 -24

-
-
-
-
@bp.route("/alerts", methods=["GET"])
+24
@bp.route("/alerts", methods=["GET"])
 def get_alerts() -> jsonify:
     """
     Retrieves all alerts from the AlertsService.
@@ -441,110 +647,116 @@ 

service = AlertsService() alerts = service.collect_alerts() return jsonify(alerts) -

-
-
-
-
-
-
-
-
-
-


-

Alert Services

- -
- -
-
-
-

- AlertsService -

- -
-

- A service class that encapsulates the logic for pulling alerts from - the Wazuh-Indexer. -

- -

Attributes:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
connector_url - str - -
-

The url to the Wazuh-Indexer.

-
-
connector_username - str - -
-

The username for the Wazuh-Indexer.

-
-
connector_password - str - -
-

The password for the Wazuh-Indexer.

-
-
es - Elasticsearch - -
-

The Elasticsearch client.

-
-
- -
- - Source code in app\services\WazuhIndexer\alerts.py - -
- - -
-
-
  9
+
+
+
+ +
+ + + +
+ +
+ +


+

Alert Services

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ AlertsService + + +

+ + +
+ + +

A service class that encapsulates the logic for pulling alerts from the Wazuh-Indexer.

+ +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
connector_url + str + +
+

The url to the Wazuh-Indexer.

+
+
connector_username + str + +
+

The username for the Wazuh-Indexer.

+
+
connector_password + str + +
+

The password for the Wazuh-Indexer.

+
+
es + Elasticsearch + +
+

The Elasticsearch client.

+
+
+ + +
+ Source code in app\services\WazuhIndexer\alerts.py +
- - -
  9
  10
  11
  12
@@ -679,12 +891,7 @@ 

Alert Services

141 142 143 -144
-
-
-
-
class AlertsService:
+144
class AlertsService:
     """
     A service class that encapsulates the logic for pulling alerts from the Wazuh-Indexer.
 
@@ -820,43 +1027,38 @@ 

Alert Services

}, "sort": [{"timestamp_utc": {"order": "desc"}}], } -
-
-
-
-
- -
-
-

- __init__() -

- -
-

- Initializes the service by collecting Wazuh-Indexer - details and creating an Elasticsearch client. -

- -
- - Source code in - app\services\WazuhIndexer\alerts.py - -
- - -
-
-
25
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__() + +

+ + +
+ +

Initializes the service by collecting Wazuh-Indexer details and creating an Elasticsearch client.

+ +
+ Source code in app\services\WazuhIndexer\alerts.py +
- - -
25
 26
 27
 28
@@ -872,12 +1074,7 @@ 

Alert Services

38 39 40 -41
-
-
-
-
def __init__(self):
+41
def __init__(self):
     """
     Initializes the service by collecting Wazuh-Indexer details and creating an Elasticsearch client.
     """
@@ -894,75 +1091,53 @@ 

Alert Services

max_retries=10, retry_on_timeout=False, ) -
-
-
-
-
-
-
- -
-

- collect_alerts() -

- -
-

- Collects the alerts from the Wazuh-Indexer where the - index name starts with "wazuh_" and is not in the - SKIP_INDEX_NAMES list. Returns the 10 previous alerts - based on the timestamp_utc field. -

- -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Dict[str, object] - -
-

- Dict[str, object]: A dictionary - containing success status and alerts - or an error message. -

-
-
- -
- - Source code in - app\services\WazuhIndexer\alerts.py - -
- - -
-
-
58
+
+
+
+ +
+ +
+ + + +

+collect_alerts() + +

+ + +
+ +

Collects the alerts from the Wazuh-Indexer where the index name starts with "wazuh_" +and is not in the SKIP_INDEX_NAMES list. +Returns the 10 previous alerts based on the timestamp_utc field.

+ +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Dict[str, object] + +
+

Dict[str, object]: A dictionary containing success status and alerts or an error message.

+
+
+ +
+ Source code in app\services\WazuhIndexer\alerts.py +
- - -
58
 59
 60
 61
@@ -1002,12 +1177,7 @@ 

Alert Services

95 96 97 -98
-
-
-
-
def collect_alerts(self) -> Dict[str, object]:
+98
def collect_alerts(self) -> Dict[str, object]:
     """
     Collects the alerts from the Wazuh-Indexer where the index name starts with "wazuh_"
     and is not in the SKIP_INDEX_NAMES list.
@@ -1048,100 +1218,79 @@ 

Alert Services

"success": True, "alerts_summary": alerts_summary, } -
-
-
-
-
-
-
- -
-

- is_index_skipped(index_name) -

- -
-

- Checks whether the given index name should be skipped. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
index_name - str - -
-

The name of the index.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
bool - bool - -
-

- True if the index should be skipped, - False otherwise. -

-
-
- -
- - Source code in - app\services\WazuhIndexer\alerts.py - -
- - -
-
-
43
+
+
+
+ +
+ +
+ + + +

+is_index_skipped(index_name) + +

+ + +
+ +

Checks whether the given index name should be skipped.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
index_name + str + +
+

The name of the index.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
bool + bool + +
+

True if the index should be skipped, False otherwise.

+
+
+ +
+ Source code in app\services\WazuhIndexer\alerts.py +
- - -
43
 44
 45
 46
@@ -1154,12 +1303,7 @@ 

Alert Services

53 54 55 -56
-
-
-
-
def is_index_skipped(self, index_name: str) -> bool:
+56
def is_index_skipped(self, index_name: str) -> bool:
     """
     Checks whether the given index name should be skipped.
 
@@ -1173,103 +1317,82 @@ 

Alert Services

if index_name.startswith(skipped): return True return False -
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
+
+
+
- -
- - -
-
-
-
+
+ + + +
+ +
+ +
- - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/artifacts/index.html b/backend/site/artifacts/index.html index c7a06561..2d462d8e 100644 --- a/backend/site/artifacts/index.html +++ b/backend/site/artifacts/index.html @@ -1,507 +1,702 @@ + - - - + + + + + + + + + + + + + + + + + + + Artifacts - SOCFortress CoPilot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + - - +

Artifacts

- - +

Artifacts Overview

+

Artifacts Model

- Artifacts - SOCFortress CoPilot - - - - - - - - - - - - +
- - - - - - - -
- -
- -
- -
- - -
-
- - -
-
-

Artifacts

- -

Artifacts Overview

-

Artifacts Model

- -
- -
-
-
-

- Artifact -

- -
-

- Bases: - db.Model -

- -

- Class for artifacts collected which stores the artifact name, - artifact results (json), and hostname. This class inherits from - SQLAlchemy's Model class. -

- -
- - Source code in app\models\artifacts.py - -
- - -
-
-
11
+
+  
+ + + +
+ + + + + + + + +
+ + + +

+ Artifact + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for artifacts collected which stores the artifact name, artifact results (json), and hostname. +This class inherits from SQLAlchemy's Model class.

+ + +
+ Source code in app\models\artifacts.py +
- - -
11
 12
 13
 14
@@ -530,12 +725,7 @@ 

37 38 39 -40

-
-
-
-
class Artifact(db.Model):
+40
class Artifact(db.Model):
     """
     Class for artifacts collected which stores the artifact name, artifact results (json), and hostname.
     This class inherits from SQLAlchemy's Model class.
@@ -565,49 +755,41 @@ 

:return: A string representation of the artifact name. """ return f"<Artifact {self.artifact_name}>" -

-
-
-
-
- -
-
-

- __init__(artifact_name, - artifact_results, hostname) -

- -
-

Initialize a new instance of the Artifact class.

-

- :param artifact_name: The name of the artifact. :param - artifact_results: The results of the artifact, stored as - a JSON string. :param hostname: The hostname where the - artifact was collected. -

- -
- - Source code in app\models\artifacts.py - -
- - -
-
-
22
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(artifact_name, artifact_results, hostname) + +

+ + +
+ +

Initialize a new instance of the Artifact class.

+

:param artifact_name: The name of the artifact. +:param artifact_results: The results of the artifact, stored as a JSON string. +:param hostname: The hostname where the artifact was collected.

+ +
+ Source code in app\models\artifacts.py +
- - -
22
 23
 24
 25
@@ -617,12 +799,7 @@ 

29 30 31 -32

-
-
-
-
def __init__(self, artifact_name: str, artifact_results: str, hostname: str):
+32
def __init__(self, artifact_name: str, artifact_results: str, hostname: str):
     """
     Initialize a new instance of the Artifact class.
 
@@ -633,106 +810,78 @@ 

self.artifact_name = artifact_name self.artifact_results = artifact_results self.hostname = hostname -

-
-
-
-
-
-
- -
-

- __repr__() -

- -
-

- Returns a string representation of the Artifact - instance. -

-

- :return: A string representation of the artifact name. -

- -
- - Source code in app\models\artifacts.py - -
- - -
-
-
34
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the Artifact instance.

+

:return: A string representation of the artifact name.

+ +
+ Source code in app\models\artifacts.py +
- - -
34
 35
 36
 37
 38
 39
-40
-
-
-
-
def __repr__(self) -> str:
+40
def __repr__(self) -> str:
     """
     Returns a string representation of the Artifact instance.
 
     :return: A string representation of the artifact name.
     """
     return f"<Artifact {self.artifact_name}>"
-
-
-
-
-
-
-
-
-
-
- -
-

- ArtifactSchema -

- -
-

- Bases: - ma.Schema -

- -

- Schema for serializing and deserializing instances of the Artifact - class. -

- -
- - Source code in app\models\artifacts.py - -
- - -
-
-
43
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ ArtifactSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the Artifact class.

+ + +
+ Source code in app\models\artifacts.py +
- - -
43
 44
 45
 46
@@ -747,12 +896,7 @@ 

55 56 57 -58

-
-
-
-
class ArtifactSchema(ma.Schema):
+58
class ArtifactSchema(ma.Schema):
     """
     Schema for serializing and deserializing instances of the Artifact class.
     """
@@ -768,39 +912,40 @@ 

"artifact_results", "hostname", ) -

-
-
-
-
- -
-
-

- Meta -

- -
-

- Meta class defines the fields to be - serialized/deserialized. -

- -
- - Source code in app\models\artifacts.py - -
- - -
-
-
48
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\artifacts.py +
- - -
48
 49
 50
 51
@@ -810,12 +955,7 @@ 

55 56 57 -58

-
-
-
-
class Meta:
+58
class Meta:
     """
     Meta class defines the fields to be serialized/deserialized.
     """
@@ -826,79 +966,106 @@ 

"artifact_results", "hostname", ) -

-
-
-
-
- -
-
-
-
-
-
-
-
-
-


-

Artifacts Routes

- -
- -
-
-
-

- collect_artifact() -

- -
-

- Endpoint to collect an artifact. It collects the artifact name and - client name from the request body and returns the results. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the result of the - artifact collection operation. -

-
-
- -
- - Source code in app\routes\velociraptor.py - -
- - -
-
-
 74
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + +
+ +
+ +


+

Artifacts Routes

+ + +
+ + + +
+ + + +
+ + + + + + + + + +
+ + + +

+collect_artifact() + +

+ + +
+ +

Endpoint to collect an artifact. +It collects the artifact name and client name from the request body and returns the results.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the result of the artifact collection operation.

+
+
+ +
+ Source code in app\routes\velociraptor.py +
- - -
 74
  75
  76
  77
@@ -931,12 +1098,7 @@ 

104 105 106 -107

-
-
-
-
@bp.route("/velociraptor/artifacts/collection", methods=["POST"])
+107
@bp.route("/velociraptor/artifacts/collection", methods=["POST"])
 def collect_artifact():
     """
     Endpoint to collect an artifact.
@@ -970,74 +1132,60 @@ 

artifact=artifact_name, ) return artifact_results -

-
-
-
-
-
-
- -
-

- get_artifacts() -

- -
-

- Endpoint to list all available artifacts. It processes each artifact - to verify the connection and returns the results. -

- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of all - available artifacts along with their connection - verification -

-
-
-
-

status.

-
-
- -
- - Source code in app\routes\velociraptor.py - -
- - -
-
-
11
+
+
+
+ +
+ +
+ + + +

+get_artifacts() + +

+ + +
+ +

Endpoint to list all available artifacts. +It processes each artifact to verify the connection and returns the results.

+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of all available artifacts along with their connection verification

+
+
+ +
+

status.

+
+
+ +
+ Source code in app\routes\velociraptor.py +
- - -
11
 12
 13
 14
@@ -1049,12 +1197,7 @@ 

20 21 22 -23

-
-
-
-
@bp.route("/velociraptor/artifacts", methods=["GET"])
+23
@bp.route("/velociraptor/artifacts", methods=["GET"])
 def get_artifacts():
     """
     Endpoint to list all available artifacts.
@@ -1067,79 +1210,61 @@ 

service = ArtifactsService() artifacts = service.collect_artifacts() return artifacts -

-
-
-
-
-
-
- -
-

- get_artifacts_linux() -

- -
-

- Endpoint to list all available Linux artifacts. It processes each - artifact to verify the connection and returns the results where the - name begins with Linux. -

- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of all - available Linux artifacts along with their - connection verification -

-
-
-
-

status.

-
-
- -
- - Source code in app\routes\velociraptor.py - -
- - -
-
-
26
+
+
+
+ +
+ +
+ + + +

+get_artifacts_linux() + +

+ + +
+ +

Endpoint to list all available Linux artifacts. +It processes each artifact to verify the connection and returns the results where the name +begins with Linux.

+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of all available Linux artifacts along with their connection verification

+
+
+ +
+

status.

+
+
+ +
+ Source code in app\routes\velociraptor.py +
- - -
26
 27
 28
 29
@@ -1152,12 +1277,7 @@ 

36 37 38 -39

-
-
-
-
@bp.route("/velociraptor/artifacts/linux", methods=["GET"])
+39
@bp.route("/velociraptor/artifacts/linux", methods=["GET"])
 def get_artifacts_linux():
     """
     Endpoint to list all available Linux artifacts.
@@ -1171,76 +1291,61 @@ 

service = ArtifactsService() linux_artifacts = service.collect_artifacts_linux() return linux_artifacts -

-
-
-
-
-
-
- -
-

- get_artifacts_mac() -

- -
-

- Endpoint to list all available MacOS artifacts. It processes each - artifact to verify the connection and returns the results where the - name begins with MacOS. -

- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of all - available MacOS artifacts along with their - connection verification -

-
-
-
-

status.

-
-
- -
- - Source code in app\routes\velociraptor.py - -
- - -
-
-
58
+
+
+
+ +
+ +
+ + + +

+get_artifacts_mac() + +

+ + +
+ +

Endpoint to list all available MacOS artifacts. +It processes each artifact to verify the connection and returns the results where the name +begins with MacOS.

+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of all available MacOS artifacts along with their connection verification

+
+
+ +
+

status.

+
+
+ +
+ Source code in app\routes\velociraptor.py +
- - -
58
 59
 60
 61
@@ -1253,12 +1358,7 @@ 

68 69 70 -71

-
-
-
-
@bp.route("/velociraptor/artifacts/mac", methods=["GET"])
+71
@bp.route("/velociraptor/artifacts/mac", methods=["GET"])
 def get_artifacts_mac():
     """
     Endpoint to list all available MacOS artifacts.
@@ -1272,79 +1372,61 @@ 

service = ArtifactsService() mac_artifacts = service.collect_artifacts_macos() return mac_artifacts -

-
-
-
-
-
-
- -
-

- get_artifacts_windows() -

- -
-

- Endpoint to list all available Windows artifacts. It processes each - artifact to verify the connection and returns the results where the - name begins with Windows. -

- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of all - available Windows artifacts along with their - connection verification -

-
-
-
-

status.

-
-
- -
- - Source code in app\routes\velociraptor.py - -
- - -
-
-
42
+
+
+
+ +
+ +
+ + + +

+get_artifacts_windows() + +

+ + +
+ +

Endpoint to list all available Windows artifacts. +It processes each artifact to verify the connection and returns the results where the name +begins with Windows.

+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of all available Windows artifacts along with their connection verification

+
+
+ +
+

status.

+
+
+ +
+ Source code in app\routes\velociraptor.py +
- - -
42
 43
 44
 45
@@ -1357,12 +1439,7 @@ 

52 53 54 -55

-
-
-
-
@bp.route("/velociraptor/artifacts/windows", methods=["GET"])
+55
@bp.route("/velociraptor/artifacts/windows", methods=["GET"])
 def get_artifacts_windows():
     """
     Endpoint to list all available Windows artifacts.
@@ -1376,50 +1453,59 @@ 

service = ArtifactsService() windows_artifacts = service.collect_artifacts_windows() return windows_artifacts -

-
-
-
-
-
-
-
-
-
-


-

Artifacts Services

- -
- -
-
-
-

- ArtifactsService -

- -
-

- A service class that encapsulates the logic for pulling artifacts - from Velociraptor. -

- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
  6
+
+
+
+ +
+ + + +
+ +
+ +


+

Artifacts Services

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ ArtifactsService + + +

+ + +
+ + +

A service class that encapsulates the logic for pulling artifacts from Velociraptor.

+ + +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
  6
   7
   8
   9
@@ -1551,12 +1637,7 @@ 

Artifacts Services135 136 137 -138

-
-
-
-
class ArtifactsService:
+138
class ArtifactsService:
     """
     A service class that encapsulates the logic for pulling artifacts from Velociraptor.
     """
@@ -1689,68 +1770,60 @@ 

Artifacts Services "message": "Failed to run artifact collection", "success": False, } -

-
-
-
-
- -
-
-

- collect_artifacts() -

- -
-

Collect the artifacts from Velociraptor.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A dictionary with the success - status, a message, and potentially - the artifacts. -

-
-
- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
39
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+collect_artifacts() + +

+ + +
+ +

Collect the artifacts from Velociraptor.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A dictionary with the success status, a message, and potentially the artifacts.

+
+
+ +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
39
 40
 41
 42
@@ -1758,12 +1831,7 @@ 

Artifacts Services44 45 46 -47

-
-
-
-
def collect_artifacts(self) -> dict:
+47
def collect_artifacts(self) -> dict:
     """
     Collect the artifacts from Velociraptor.
 
@@ -1772,84 +1840,58 @@ 

Artifacts Services """ query = self._create_query("SELECT name FROM artifact_definitions()") return self.universal_service.execute_query(query) -

-
-
-
-
-
-
- -
-

- collect_artifacts_linux() -

- -
-

- Collect the artifacts from Velociraptor that have a name - beginning with Linux. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A dictionary with the success - status, a message, and potentially - the artifacts. -

-
-
- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
75
+
+
+
+ +
+ +
+ + + +

+collect_artifacts_linux() + +

+ + +
+ +

Collect the artifacts from Velociraptor that have a name beginning with Linux.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A dictionary with the success status, a message, and potentially the artifacts.

+
+
+ +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
75
 76
 77
 78
 79
 80
 81
-82
-
-
-
-
def collect_artifacts_linux(self) -> dict:
+82
def collect_artifacts_linux(self) -> dict:
     """
     Collect the artifacts from Velociraptor that have a name beginning with `Linux`.
 
@@ -1857,84 +1899,58 @@ 

Artifacts Services dict: A dictionary with the success status, a message, and potentially the artifacts. """ return self.collect_artifacts_prefixed("Linux.") -

-
-
-
-
-
-
- -
-

- collect_artifacts_macos() -

- -
-

- Collect the artifacts from Velociraptor that have a name - beginning with MacOS. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A dictionary with the success - status, a message, and potentially - the artifacts. -

-
-
- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
 93
+
+
+
+ +
+ +
+ + + +

+collect_artifacts_macos() + +

+ + +
+ +

Collect the artifacts from Velociraptor that have a name beginning with MacOS.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A dictionary with the success status, a message, and potentially the artifacts.

+
+
+ +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
- -
-
def collect_artifacts_macos(self) -> dict:
+100
def collect_artifacts_macos(self) -> dict:
     """
     Collect the artifacts from Velociraptor that have a name beginning with `MacOS`.
 
@@ -1942,103 +1958,79 @@ 

Artifacts Services dict: A dictionary with the success status, a message, and potentially the artifacts. """ return self.collect_artifacts_prefixed("MacOS.") -

-
-
-
-
-
-
- -
-

- collect_artifacts_prefixed(prefix) -

- -
-

- Collect the artifacts from Velociraptor that have a name - beginning with a specific prefix. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
prefix - str - -
-

- The prefix to filter the artifacts. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A dictionary with the success - status, a message, and potentially - the artifacts. -

-
-
- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
49
+
+
+
+ +
+ +
+ + + +

+collect_artifacts_prefixed(prefix) + +

+ + +
+ +

Collect the artifacts from Velociraptor that have a name beginning with a specific prefix.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
prefix + str + +
+

The prefix to filter the artifacts.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A dictionary with the success status, a message, and potentially the artifacts.

+
+
+ +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
49
 50
 51
 52
@@ -2062,12 +2054,7 @@ 

Artifacts Services70 71 72 -73

-
-
-
-
def collect_artifacts_prefixed(self, prefix: str) -> dict:
+73
def collect_artifacts_prefixed(self, prefix: str) -> dict:
     """
     Collect the artifacts from Velociraptor that have a name beginning with a specific prefix.
 
@@ -2092,84 +2079,58 @@ 

Artifacts Services "message": f"Successfully collected {prefix} artifacts", "artifacts": filtered_artifacts, } -

-
-
-
-
-
-
- -
-

- collect_artifacts_windows() -

- -
-

- Collect the artifacts from Velociraptor that have a name - beginning with Windows. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A dictionary with the success - status, a message, and potentially - the artifacts. -

-
-
- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
84
+
+
+
+ +
+ +
+ + + +

+collect_artifacts_windows() + +

+ + +
+ +

Collect the artifacts from Velociraptor that have a name beginning with Windows.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A dictionary with the success status, a message, and potentially the artifacts.

+
+
+ +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
84
 85
 86
 87
 88
 89
 90
-91
-
-
-
-
def collect_artifacts_windows(self) -> dict:
+91
def collect_artifacts_windows(self) -> dict:
     """
     Collect the artifacts from Velociraptor that have a name beginning with `Windows`.
 
@@ -2177,113 +2138,93 @@ 

Artifacts Services dict: A dictionary with the success status, a message, and potentially the artifacts. """ return self.collect_artifacts_prefixed("Windows.") -

-
-
-
-
-
-
- -
-

- run_artifact_collection(client_id, artifact) -

- -
-

Run an artifact collection on a specific client.

- -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
client_id - str - -
-

The ID of the client.

-
-
- required -
artifact - str - -
-

The name of the artifact.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A dictionary with the success - status, a message, and potentially - the results. -

-
-
- -
- - Source code in - app\services\Velociraptor\artifacts.py - -
- - -
-
-
102
+
+
+
+ +
+ +
+ + + +

+run_artifact_collection(client_id, artifact) + +

+ + +
+ +

Run an artifact collection on a specific client.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
client_id + str + +
+

The ID of the client.

+
+
+ required +
artifact + str + +
+

The name of the artifact.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A dictionary with the success status, a message, and potentially the results.

+
+
+ +
+ Source code in app\services\Velociraptor\artifacts.py +
- - -
102
 103
 104
 105
@@ -2319,12 +2260,7 @@ 

Artifacts Services135 136 137 -138

-
-
-
-
def run_artifact_collection(self, client_id: str, artifact: str) -> dict:
+138
def run_artifact_collection(self, client_id: str, artifact: str) -> dict:
     """
     Run an artifact collection on a specific client.
 
@@ -2361,103 +2297,82 @@ 

Artifacts Services "message": "Failed to run artifact collection", "success": False, } -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
+
+
+
- -
- - -
-
-
-
+
+ + + +
+ +
- - - - - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/assets/_mkdocstrings.css b/backend/site/assets/_mkdocstrings.css index 79be9bf6..049a254b 100644 --- a/backend/site/assets/_mkdocstrings.css +++ b/backend/site/assets/_mkdocstrings.css @@ -1,28 +1,29 @@ + /* Avoid breaking parameter names, etc. in table cells. */ .doc-contents td code { - word-break: normal !important; + word-break: normal !important; } /* No line break before first paragraph of descriptions. */ .doc-md-description, -.doc-md-description > p:first-child { - display: inline; +.doc-md-description>p:first-child { + display: inline; } /* Max width for docstring sections tables. */ .doc .md-typeset__table, .doc .md-typeset__table table { - display: table !important; - width: 100%; + display: table !important; + width: 100%; } .doc .md-typeset__table tr { - display: table-row; + display: table-row; } /* Defaults in Spacy table style. */ .doc-param-default { - float: right; + float: right; } /* Keep headings consistent. */ @@ -32,32 +33,32 @@ h3.doc-heading, h4.doc-heading, h5.doc-heading, h6.doc-heading { - font-weight: 400; - line-height: 1.5; - color: inherit; - text-transform: none; + font-weight: 400; + line-height: 1.5; + color: inherit; + text-transform: none; } h1.doc-heading { - font-size: 1.6rem; + font-size: 1.6rem; } h2.doc-heading { - font-size: 1.2rem; + font-size: 1.2rem; } h3.doc-heading { - font-size: 1.15rem; + font-size: 1.15rem; } h4.doc-heading { - font-size: 1.1rem; + font-size: 1.10rem; } h5.doc-heading { - font-size: 1.05rem; + font-size: 1.05rem; } h6.doc-heading { - font-size: 1rem; -} + font-size: 1rem; +} \ No newline at end of file diff --git a/backend/site/assets/javascripts/bundle.220ee61c.min.js b/backend/site/assets/javascripts/bundle.220ee61c.min.js index ec02d635..116072a1 100644 --- a/backend/site/assets/javascripts/bundle.220ee61c.min.js +++ b/backend/site/assets/javascripts/bundle.220ee61c.min.js @@ -1,1217 +1,15 @@ -"use strict" -;(() => { - var Ci = Object.create - var gr = Object.defineProperty - var Ri = Object.getOwnPropertyDescriptor - var ki = Object.getOwnPropertyNames, - Ht = Object.getOwnPropertySymbols, - Hi = Object.getPrototypeOf, - yr = Object.prototype.hasOwnProperty, - nn = Object.prototype.propertyIsEnumerable - var rn = (e, t, r) => - t in e ? gr(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : (e[t] = r), - P = (e, t) => { - for (var r in t || (t = {})) yr.call(t, r) && rn(e, r, t[r]) - if (Ht) for (var r of Ht(t)) nn.call(t, r) && rn(e, r, t[r]) - return e - } - var on = (e, t) => { - var r = {} - for (var n in e) yr.call(e, n) && t.indexOf(n) < 0 && (r[n] = e[n]) - if (e != null && Ht) for (var n of Ht(e)) t.indexOf(n) < 0 && nn.call(e, n) && (r[n] = e[n]) - return r - } - var Pt = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports) - var Pi = (e, t, r, n) => { - if ((t && typeof t == "object") || typeof t == "function") - for (let o of ki(t)) - !yr.call(e, o) && o !== r && gr(e, o, { get: () => t[o], enumerable: !(n = Ri(t, o)) || n.enumerable }) - return e - } - var yt = (e, t, r) => ( - (r = e != null ? Ci(Hi(e)) : {}), - Pi(t || !e || !e.__esModule ? gr(r, "default", { value: e, enumerable: !0 }) : r, e) - ) - var sn = Pt((xr, an) => { - ;(function (e, t) { - typeof xr == "object" && typeof an != "undefined" - ? t() - : typeof define == "function" && define.amd - ? define(t) - : t() - })(xr, function () { - "use strict" - function e(r) { - var n = !0, - o = !1, - i = null, - s = { - text: !0, - search: !0, - url: !0, - tel: !0, - email: !0, - password: !0, - number: !0, - date: !0, - month: !0, - week: !0, - time: !0, - datetime: !0, - "datetime-local": !0 - } - function a(O) { - return !!( - O && - O !== document && - O.nodeName !== "HTML" && - O.nodeName !== "BODY" && - "classList" in O && - "contains" in O.classList - ) - } - function f(O) { - var Qe = O.type, - De = O.tagName - return !!( - (De === "INPUT" && s[Qe] && !O.readOnly) || - (De === "TEXTAREA" && !O.readOnly) || - O.isContentEditable - ) - } - function c(O) { - O.classList.contains("focus-visible") || - (O.classList.add("focus-visible"), O.setAttribute("data-focus-visible-added", "")) - } - function u(O) { - O.hasAttribute("data-focus-visible-added") && - (O.classList.remove("focus-visible"), O.removeAttribute("data-focus-visible-added")) - } - function p(O) { - O.metaKey || O.altKey || O.ctrlKey || (a(r.activeElement) && c(r.activeElement), (n = !0)) - } - function m(O) { - n = !1 - } - function d(O) { - a(O.target) && (n || f(O.target)) && c(O.target) - } - function h(O) { - a(O.target) && - (O.target.classList.contains("focus-visible") || - O.target.hasAttribute("data-focus-visible-added")) && - ((o = !0), - window.clearTimeout(i), - (i = window.setTimeout(function () { - o = !1 - }, 100)), - u(O.target)) - } - function v(O) { - document.visibilityState === "hidden" && (o && (n = !0), Y()) - } - function Y() { - document.addEventListener("mousemove", N), - document.addEventListener("mousedown", N), - document.addEventListener("mouseup", N), - document.addEventListener("pointermove", N), - document.addEventListener("pointerdown", N), - document.addEventListener("pointerup", N), - document.addEventListener("touchmove", N), - document.addEventListener("touchstart", N), - document.addEventListener("touchend", N) - } - function B() { - document.removeEventListener("mousemove", N), - document.removeEventListener("mousedown", N), - document.removeEventListener("mouseup", N), - document.removeEventListener("pointermove", N), - document.removeEventListener("pointerdown", N), - document.removeEventListener("pointerup", N), - document.removeEventListener("touchmove", N), - document.removeEventListener("touchstart", N), - document.removeEventListener("touchend", N) - } - function N(O) { - ;(O.target.nodeName && O.target.nodeName.toLowerCase() === "html") || ((n = !1), B()) - } - document.addEventListener("keydown", p, !0), - document.addEventListener("mousedown", m, !0), - document.addEventListener("pointerdown", m, !0), - document.addEventListener("touchstart", m, !0), - document.addEventListener("visibilitychange", v, !0), - Y(), - r.addEventListener("focus", d, !0), - r.addEventListener("blur", h, !0), - r.nodeType === Node.DOCUMENT_FRAGMENT_NODE && r.host - ? r.host.setAttribute("data-js-focus-visible", "") - : r.nodeType === Node.DOCUMENT_NODE && - (document.documentElement.classList.add("js-focus-visible"), - document.documentElement.setAttribute("data-js-focus-visible", "")) - } - if (typeof window != "undefined" && typeof document != "undefined") { - window.applyFocusVisiblePolyfill = e - var t - try { - t = new CustomEvent("focus-visible-polyfill-ready") - } catch (r) { - ;(t = document.createEvent("CustomEvent")), - t.initCustomEvent("focus-visible-polyfill-ready", !1, !1, {}) - } - window.dispatchEvent(t) - } - typeof document != "undefined" && e(document) - }) - }) - var cn = Pt(Er => { - ;(function (e) { - var t = function () { - try { - return !!Symbol.iterator - } catch (c) { - return !1 - } - }, - r = t(), - n = function (c) { - var u = { - next: function () { - var p = c.shift() - return { done: p === void 0, value: p } - } - } - return ( - r && - (u[Symbol.iterator] = function () { - return u - }), - u - ) - }, - o = function (c) { - return encodeURIComponent(c).replace(/%20/g, "+") - }, - i = function (c) { - return decodeURIComponent(String(c).replace(/\+/g, " ")) - }, - s = function () { - var c = function (p) { - Object.defineProperty(this, "_entries", { writable: !0, value: {} }) - var m = typeof p - if (m !== "undefined") - if (m === "string") p !== "" && this._fromString(p) - else if (p instanceof c) { - var d = this - p.forEach(function (B, N) { - d.append(N, B) - }) - } else if (p !== null && m === "object") - if (Object.prototype.toString.call(p) === "[object Array]") - for (var h = 0; h < p.length; h++) { - var v = p[h] - if ( - Object.prototype.toString.call(v) === "[object Array]" || - v.length !== 2 - ) - this.append(v[0], v[1]) - else - throw new TypeError( - "Expected [string, any] as entry at index " + - h + - " of URLSearchParams's input" - ) - } - else for (var Y in p) p.hasOwnProperty(Y) && this.append(Y, p[Y]) - else throw new TypeError("Unsupported input's type for URLSearchParams") - }, - u = c.prototype - ;(u.append = function (p, m) { - p in this._entries ? this._entries[p].push(String(m)) : (this._entries[p] = [String(m)]) - }), - (u.delete = function (p) { - delete this._entries[p] - }), - (u.get = function (p) { - return p in this._entries ? this._entries[p][0] : null - }), - (u.getAll = function (p) { - return p in this._entries ? this._entries[p].slice(0) : [] - }), - (u.has = function (p) { - return p in this._entries - }), - (u.set = function (p, m) { - this._entries[p] = [String(m)] - }), - (u.forEach = function (p, m) { - var d - for (var h in this._entries) - if (this._entries.hasOwnProperty(h)) { - d = this._entries[h] - for (var v = 0; v < d.length; v++) p.call(m, d[v], h, this) - } - }), - (u.keys = function () { - var p = [] - return ( - this.forEach(function (m, d) { - p.push(d) - }), - n(p) - ) - }), - (u.values = function () { - var p = [] - return ( - this.forEach(function (m) { - p.push(m) - }), - n(p) - ) - }), - (u.entries = function () { - var p = [] - return ( - this.forEach(function (m, d) { - p.push([d, m]) - }), - n(p) - ) - }), - r && (u[Symbol.iterator] = u.entries), - (u.toString = function () { - var p = [] - return ( - this.forEach(function (m, d) { - p.push(o(d) + "=" + o(m)) - }), - p.join("&") - ) - }), - (e.URLSearchParams = c) - }, - a = function () { - try { - var c = e.URLSearchParams - return ( - new c("?a=1").toString() === "a=1" && - typeof c.prototype.set == "function" && - typeof c.prototype.entries == "function" - ) - } catch (u) { - return !1 - } - } - a() || s() - var f = e.URLSearchParams.prototype - typeof f.sort != "function" && - (f.sort = function () { - var c = this, - u = [] - this.forEach(function (m, d) { - u.push([d, m]), c._entries || c.delete(d) - }), - u.sort(function (m, d) { - return m[0] < d[0] ? -1 : m[0] > d[0] ? 1 : 0 - }), - c._entries && (c._entries = {}) - for (var p = 0; p < u.length; p++) this.append(u[p][0], u[p][1]) - }), - typeof f._fromString != "function" && - Object.defineProperty(f, "_fromString", { - enumerable: !1, - configurable: !1, - writable: !1, - value: function (c) { - if (this._entries) this._entries = {} - else { - var u = [] - this.forEach(function (h, v) { - u.push(v) - }) - for (var p = 0; p < u.length; p++) this.delete(u[p]) - } - c = c.replace(/^\?/, "") - for (var m = c.split("&"), d, p = 0; p < m.length; p++) - (d = m[p].split("=")), this.append(i(d[0]), d.length > 1 ? i(d[1]) : "") - } - }) - })( - typeof global != "undefined" - ? global - : typeof window != "undefined" - ? window - : typeof self != "undefined" - ? self - : Er - ) - ;(function (e) { - var t = function () { - try { - var o = new e.URL("b", "http://a") - return (o.pathname = "c d"), o.href === "http://a/c%20d" && o.searchParams - } catch (i) { - return !1 - } - }, - r = function () { - var o = e.URL, - i = function (f, c) { - typeof f != "string" && (f = String(f)), c && typeof c != "string" && (c = String(c)) - var u = document, - p - if (c && (e.location === void 0 || c !== e.location.href)) { - ;(c = c.toLowerCase()), - (u = document.implementation.createHTMLDocument("")), - (p = u.createElement("base")), - (p.href = c), - u.head.appendChild(p) - try { - if (p.href.indexOf(c) !== 0) throw new Error(p.href) - } catch (O) { - throw new Error("URL unable to set base " + c + " due to " + O) - } - } - var m = u.createElement("a") - ;(m.href = f), p && (u.body.appendChild(m), (m.href = m.href)) - var d = u.createElement("input") - if ( - ((d.type = "url"), - (d.value = f), - m.protocol === ":" || !/:/.test(m.href) || (!d.checkValidity() && !c)) - ) - throw new TypeError("Invalid URL") - Object.defineProperty(this, "_anchorElement", { value: m }) - var h = new e.URLSearchParams(this.search), - v = !0, - Y = !0, - B = this - ;["append", "delete", "set"].forEach(function (O) { - var Qe = h[O] - h[O] = function () { - Qe.apply(h, arguments), v && ((Y = !1), (B.search = h.toString()), (Y = !0)) - } - }), - Object.defineProperty(this, "searchParams", { value: h, enumerable: !0 }) - var N = void 0 - Object.defineProperty(this, "_updateSearchParams", { - enumerable: !1, - configurable: !1, - writable: !1, - value: function () { - this.search !== N && - ((N = this.search), - Y && ((v = !1), this.searchParams._fromString(this.search), (v = !0))) - } - }) - }, - s = i.prototype, - a = function (f) { - Object.defineProperty(s, f, { - get: function () { - return this._anchorElement[f] - }, - set: function (c) { - this._anchorElement[f] = c - }, - enumerable: !0 - }) - } - ;["hash", "host", "hostname", "port", "protocol"].forEach(function (f) { - a(f) - }), - Object.defineProperty(s, "search", { - get: function () { - return this._anchorElement.search - }, - set: function (f) { - ;(this._anchorElement.search = f), this._updateSearchParams() - }, - enumerable: !0 - }), - Object.defineProperties(s, { - toString: { - get: function () { - var f = this - return function () { - return f.href - } - } - }, - href: { - get: function () { - return this._anchorElement.href.replace(/\?$/, "") - }, - set: function (f) { - ;(this._anchorElement.href = f), this._updateSearchParams() - }, - enumerable: !0 - }, - pathname: { - get: function () { - return this._anchorElement.pathname.replace(/(^\/?)/, "/") - }, - set: function (f) { - this._anchorElement.pathname = f - }, - enumerable: !0 - }, - origin: { - get: function () { - var f = { "http:": 80, "https:": 443, "ftp:": 21 }[this._anchorElement.protocol], - c = this._anchorElement.port != f && this._anchorElement.port !== "" - return ( - this._anchorElement.protocol + - "//" + - this._anchorElement.hostname + - (c ? ":" + this._anchorElement.port : "") - ) - }, - enumerable: !0 - }, - password: { - get: function () { - return "" - }, - set: function (f) {}, - enumerable: !0 - }, - username: { - get: function () { - return "" - }, - set: function (f) {}, - enumerable: !0 - } - }), - (i.createObjectURL = function (f) { - return o.createObjectURL.apply(o, arguments) - }), - (i.revokeObjectURL = function (f) { - return o.revokeObjectURL.apply(o, arguments) - }), - (e.URL = i) - } - if ((t() || r(), e.location !== void 0 && !("origin" in e.location))) { - var n = function () { - return ( - e.location.protocol + - "//" + - e.location.hostname + - (e.location.port ? ":" + e.location.port : "") - ) - } - try { - Object.defineProperty(e.location, "origin", { get: n, enumerable: !0 }) - } catch (o) { - setInterval(function () { - e.location.origin = n() - }, 100) - } - } - })( - typeof global != "undefined" - ? global - : typeof window != "undefined" - ? window - : typeof self != "undefined" - ? self - : Er - ) - }) - var qr = Pt((Mt, Nr) => { - /*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */ ;(function (t, r) { - typeof Mt == "object" && typeof Nr == "object" - ? (Nr.exports = r()) - : typeof define == "function" && define.amd - ? define([], r) - : typeof Mt == "object" - ? (Mt.ClipboardJS = r()) - : (t.ClipboardJS = r()) - })(Mt, function () { - return (function () { - var e = { - 686: function (n, o, i) { - "use strict" - i.d(o, { - default: function () { - return Ai - } - }) - var s = i(279), - a = i.n(s), - f = i(370), - c = i.n(f), - u = i(817), - p = i.n(u) - function m(j) { - try { - return document.execCommand(j) - } catch (T) { - return !1 - } - } - var d = function (T) { - var E = p()(T) - return m("cut"), E - }, - h = d - function v(j) { - var T = document.documentElement.getAttribute("dir") === "rtl", - E = document.createElement("textarea") - ;(E.style.fontSize = "12pt"), - (E.style.border = "0"), - (E.style.padding = "0"), - (E.style.margin = "0"), - (E.style.position = "absolute"), - (E.style[T ? "right" : "left"] = "-9999px") - var H = window.pageYOffset || document.documentElement.scrollTop - return ( - (E.style.top = "".concat(H, "px")), E.setAttribute("readonly", ""), (E.value = j), E - ) - } - var Y = function (T, E) { - var H = v(T) - E.container.appendChild(H) - var I = p()(H) - return m("copy"), H.remove(), I - }, - B = function (T) { - var E = - arguments.length > 1 && arguments[1] !== void 0 - ? arguments[1] - : { container: document.body }, - H = "" - return ( - typeof T == "string" - ? (H = Y(T, E)) - : T instanceof HTMLInputElement && - !["text", "search", "url", "tel", "password"].includes( - T == null ? void 0 : T.type - ) - ? (H = Y(T.value, E)) - : ((H = p()(T)), m("copy")), - H - ) - }, - N = B - function O(j) { - "@babel/helpers - typeof" - return ( - typeof Symbol == "function" && typeof Symbol.iterator == "symbol" - ? (O = function (E) { - return typeof E - }) - : (O = function (E) { - return E && - typeof Symbol == "function" && - E.constructor === Symbol && - E !== Symbol.prototype - ? "symbol" - : typeof E - }), - O(j) - ) - } - var Qe = function () { - var T = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, - E = T.action, - H = E === void 0 ? "copy" : E, - I = T.container, - q = T.target, - Me = T.text - if (H !== "copy" && H !== "cut") - throw new Error('Invalid "action" value, use either "copy" or "cut"') - if (q !== void 0) - if (q && O(q) === "object" && q.nodeType === 1) { - if (H === "copy" && q.hasAttribute("disabled")) - throw new Error( - 'Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute' - ) - if ( - H === "cut" && - (q.hasAttribute("readonly") || q.hasAttribute("disabled")) - ) - throw new Error( - `Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes` - ) - } else throw new Error('Invalid "target" value, use a valid Element') - if (Me) return N(Me, { container: I }) - if (q) return H === "cut" ? h(q) : N(q, { container: I }) - }, - De = Qe - function $e(j) { - "@babel/helpers - typeof" - return ( - typeof Symbol == "function" && typeof Symbol.iterator == "symbol" - ? ($e = function (E) { - return typeof E - }) - : ($e = function (E) { - return E && - typeof Symbol == "function" && - E.constructor === Symbol && - E !== Symbol.prototype - ? "symbol" - : typeof E - }), - $e(j) - ) - } - function Ei(j, T) { - if (!(j instanceof T)) throw new TypeError("Cannot call a class as a function") - } - function tn(j, T) { - for (var E = 0; E < T.length; E++) { - var H = T[E] - ;(H.enumerable = H.enumerable || !1), - (H.configurable = !0), - "value" in H && (H.writable = !0), - Object.defineProperty(j, H.key, H) - } - } - function wi(j, T, E) { - return T && tn(j.prototype, T), E && tn(j, E), j - } - function Si(j, T) { - if (typeof T != "function" && T !== null) - throw new TypeError("Super expression must either be null or a function") - ;(j.prototype = Object.create(T && T.prototype, { - constructor: { value: j, writable: !0, configurable: !0 } - })), - T && br(j, T) - } - function br(j, T) { - return ( - (br = - Object.setPrototypeOf || - function (H, I) { - return (H.__proto__ = I), H - }), - br(j, T) - ) - } - function Oi(j) { - var T = Mi() - return function () { - var H = Rt(j), - I - if (T) { - var q = Rt(this).constructor - I = Reflect.construct(H, arguments, q) - } else I = H.apply(this, arguments) - return Ti(this, I) - } - } - function Ti(j, T) { - return T && ($e(T) === "object" || typeof T == "function") ? T : _i(j) - } - function _i(j) { - if (j === void 0) - throw new ReferenceError( - "this hasn't been initialised - super() hasn't been called" - ) - return j - } - function Mi() { - if (typeof Reflect == "undefined" || !Reflect.construct || Reflect.construct.sham) - return !1 - if (typeof Proxy == "function") return !0 - try { - return Date.prototype.toString.call(Reflect.construct(Date, [], function () {})), !0 - } catch (j) { - return !1 - } - } - function Rt(j) { - return ( - (Rt = Object.setPrototypeOf - ? Object.getPrototypeOf - : function (E) { - return E.__proto__ || Object.getPrototypeOf(E) - }), - Rt(j) - ) - } - function vr(j, T) { - var E = "data-clipboard-".concat(j) - if (T.hasAttribute(E)) return T.getAttribute(E) - } - var Li = (function (j) { - Si(E, j) - var T = Oi(E) - function E(H, I) { - var q - return Ei(this, E), (q = T.call(this)), q.resolveOptions(I), q.listenClick(H), q - } - return ( - wi( - E, - [ - { - key: "resolveOptions", - value: function () { - var I = - arguments.length > 0 && arguments[0] !== void 0 - ? arguments[0] - : {} - ;(this.action = - typeof I.action == "function" - ? I.action - : this.defaultAction), - (this.target = - typeof I.target == "function" - ? I.target - : this.defaultTarget), - (this.text = - typeof I.text == "function" - ? I.text - : this.defaultText), - (this.container = - $e(I.container) === "object" - ? I.container - : document.body) - } - }, - { - key: "listenClick", - value: function (I) { - var q = this - this.listener = c()(I, "click", function (Me) { - return q.onClick(Me) - }) - } - }, - { - key: "onClick", - value: function (I) { - var q = I.delegateTarget || I.currentTarget, - Me = this.action(q) || "copy", - kt = De({ - action: Me, - container: this.container, - target: this.target(q), - text: this.text(q) - }) - this.emit(kt ? "success" : "error", { - action: Me, - text: kt, - trigger: q, - clearSelection: function () { - q && q.focus(), window.getSelection().removeAllRanges() - } - }) - } - }, - { - key: "defaultAction", - value: function (I) { - return vr("action", I) - } - }, - { - key: "defaultTarget", - value: function (I) { - var q = vr("target", I) - if (q) return document.querySelector(q) - } - }, - { - key: "defaultText", - value: function (I) { - return vr("text", I) - } - }, - { - key: "destroy", - value: function () { - this.listener.destroy() - } - } - ], - [ - { - key: "copy", - value: function (I) { - var q = - arguments.length > 1 && arguments[1] !== void 0 - ? arguments[1] - : { container: document.body } - return N(I, q) - } - }, - { - key: "cut", - value: function (I) { - return h(I) - } - }, - { - key: "isSupported", - value: function () { - var I = - arguments.length > 0 && arguments[0] !== void 0 - ? arguments[0] - : ["copy", "cut"], - q = typeof I == "string" ? [I] : I, - Me = !!document.queryCommandSupported - return ( - q.forEach(function (kt) { - Me = Me && !!document.queryCommandSupported(kt) - }), - Me - ) - } - } - ] - ), - E - ) - })(a()), - Ai = Li - }, - 828: function (n) { - var o = 9 - if (typeof Element != "undefined" && !Element.prototype.matches) { - var i = Element.prototype - i.matches = - i.matchesSelector || - i.mozMatchesSelector || - i.msMatchesSelector || - i.oMatchesSelector || - i.webkitMatchesSelector - } - function s(a, f) { - for (; a && a.nodeType !== o; ) { - if (typeof a.matches == "function" && a.matches(f)) return a - a = a.parentNode - } - } - n.exports = s - }, - 438: function (n, o, i) { - var s = i(828) - function a(u, p, m, d, h) { - var v = c.apply(this, arguments) - return ( - u.addEventListener(m, v, h), - { - destroy: function () { - u.removeEventListener(m, v, h) - } - } - ) - } - function f(u, p, m, d, h) { - return typeof u.addEventListener == "function" - ? a.apply(null, arguments) - : typeof m == "function" - ? a.bind(null, document).apply(null, arguments) - : (typeof u == "string" && (u = document.querySelectorAll(u)), - Array.prototype.map.call(u, function (v) { - return a(v, p, m, d, h) - })) - } - function c(u, p, m, d) { - return function (h) { - ;(h.delegateTarget = s(h.target, p)), h.delegateTarget && d.call(u, h) - } - } - n.exports = f - }, - 879: function (n, o) { - ;(o.node = function (i) { - return i !== void 0 && i instanceof HTMLElement && i.nodeType === 1 - }), - (o.nodeList = function (i) { - var s = Object.prototype.toString.call(i) - return ( - i !== void 0 && - (s === "[object NodeList]" || s === "[object HTMLCollection]") && - "length" in i && - (i.length === 0 || o.node(i[0])) - ) - }), - (o.string = function (i) { - return typeof i == "string" || i instanceof String - }), - (o.fn = function (i) { - var s = Object.prototype.toString.call(i) - return s === "[object Function]" - }) - }, - 370: function (n, o, i) { - var s = i(879), - a = i(438) - function f(m, d, h) { - if (!m && !d && !h) throw new Error("Missing required arguments") - if (!s.string(d)) throw new TypeError("Second argument must be a String") - if (!s.fn(h)) throw new TypeError("Third argument must be a Function") - if (s.node(m)) return c(m, d, h) - if (s.nodeList(m)) return u(m, d, h) - if (s.string(m)) return p(m, d, h) - throw new TypeError( - "First argument must be a String, HTMLElement, HTMLCollection, or NodeList" - ) - } - function c(m, d, h) { - return ( - m.addEventListener(d, h), - { - destroy: function () { - m.removeEventListener(d, h) - } - } - ) - } - function u(m, d, h) { - return ( - Array.prototype.forEach.call(m, function (v) { - v.addEventListener(d, h) - }), - { - destroy: function () { - Array.prototype.forEach.call(m, function (v) { - v.removeEventListener(d, h) - }) - } - } - ) - } - function p(m, d, h) { - return a(document.body, m, d, h) - } - n.exports = f - }, - 817: function (n) { - function o(i) { - var s - if (i.nodeName === "SELECT") i.focus(), (s = i.value) - else if (i.nodeName === "INPUT" || i.nodeName === "TEXTAREA") { - var a = i.hasAttribute("readonly") - a || i.setAttribute("readonly", ""), - i.select(), - i.setSelectionRange(0, i.value.length), - a || i.removeAttribute("readonly"), - (s = i.value) - } else { - i.hasAttribute("contenteditable") && i.focus() - var f = window.getSelection(), - c = document.createRange() - c.selectNodeContents(i), f.removeAllRanges(), f.addRange(c), (s = f.toString()) - } - return s - } - n.exports = o - }, - 279: function (n) { - function o() {} - ;(o.prototype = { - on: function (i, s, a) { - var f = this.e || (this.e = {}) - return (f[i] || (f[i] = [])).push({ fn: s, ctx: a }), this - }, - once: function (i, s, a) { - var f = this - function c() { - f.off(i, c), s.apply(a, arguments) - } - return (c._ = s), this.on(i, c, a) - }, - emit: function (i) { - var s = [].slice.call(arguments, 1), - a = ((this.e || (this.e = {}))[i] || []).slice(), - f = 0, - c = a.length - for (f; f < c; f++) a[f].fn.apply(a[f].ctx, s) - return this - }, - off: function (i, s) { - var a = this.e || (this.e = {}), - f = a[i], - c = [] - if (f && s) - for (var u = 0, p = f.length; u < p; u++) - f[u].fn !== s && f[u].fn._ !== s && c.push(f[u]) - return c.length ? (a[i] = c) : delete a[i], this - } - }), - (n.exports = o), - (n.exports.TinyEmitter = o) - } - }, - t = {} - function r(n) { - if (t[n]) return t[n].exports - var o = (t[n] = { exports: {} }) - return e[n](o, o.exports, r), o.exports - } - return ( - (function () { - r.n = function (n) { - var o = - n && n.__esModule - ? function () { - return n.default - } - : function () { - return n - } - return r.d(o, { a: o }), o - } - })(), - (function () { - r.d = function (n, o) { - for (var i in o) - r.o(o, i) && !r.o(n, i) && Object.defineProperty(n, i, { enumerable: !0, get: o[i] }) - } - })(), - (function () { - r.o = function (n, o) { - return Object.prototype.hasOwnProperty.call(n, o) - } - })(), - r(686) - ) - })().default - }) - }) - var Go = Pt((nw, Yo) => { - "use strict" - /*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */ var rs = /["'&<>]/ - Yo.exports = ns - function ns(e) { - var t = "" + e, - r = rs.exec(t) - if (!r) return t - var n, - o = "", - i = 0, - s = 0 - for (i = r.index; i < t.length; i++) { - switch (t.charCodeAt(i)) { - case 34: - n = """ - break - case 38: - n = "&" - break - case 39: - n = "'" - break - case 60: - n = "<" - break - case 62: - n = ">" - break - default: - continue - } - s !== i && (o += t.substring(s, i)), (s = i + 1), (o += n) - } - return s !== i ? o + t.substring(s, i) : o - } - }) - Array.prototype.flat || - Object.defineProperty(Array.prototype, "flat", { - configurable: !0, - value: function e() { - var t = isNaN(arguments[0]) ? 1 : Number(arguments[0]) - return t - ? Array.prototype.reduce.call( - this, - function (r, n) { - return Array.isArray(n) ? r.push.apply(r, e.call(n, t - 1)) : r.push(n), r - }, - [] - ) - : Array.prototype.slice.call(this) - }, - writable: !0 - }), - Array.prototype.flatMap || - Object.defineProperty(Array.prototype, "flatMap", { - configurable: !0, - value: function (e) { - return Array.prototype.map.apply(this, arguments).flat() - }, - writable: !0 - }) - var RT = yt(sn()) - self.fetch || - (self.fetch = function (e, t) { - return ( - (t = t || {}), - new Promise(function (r, n) { - var o = new XMLHttpRequest(), - i = [], - s = {}, - a = function c() { - return { - ok: ((o.status / 100) | 0) == 2, - statusText: o.statusText, - status: o.status, - url: o.responseURL, - text: function () { - return Promise.resolve(o.responseText) - }, - json: function () { - return Promise.resolve(o.responseText).then(JSON.parse) - }, - blob: function () { - return Promise.resolve(new Blob([o.response])) - }, - clone: c, - headers: { - keys: function () { - return i - }, - entries: function () { - return i.map(function (u) { - return [u, o.getResponseHeader(u)] - }) - }, - get: function (u) { - return o.getResponseHeader(u) - }, - has: function (u) { - return o.getResponseHeader(u) != null - } - } - } - } - for (var f in (o.open(t.method || "get", e, !0), - (o.onload = function () { - o - .getAllResponseHeaders() - .toLowerCase() - .replace(/^(.+?):/gm, function (c, u) { - s[u] || i.push((s[u] = u)) - }), - r(a()) - }), - (o.onerror = n), - (o.withCredentials = t.credentials == "include"), - t.headers)) - o.setRequestHeader(f, t.headers[f]) - o.send(t.body || null) - }) - ) - }) - var HT = yt(cn()) - /*! ***************************************************************************** +"use strict";(()=>{var Ci=Object.create;var gr=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var ki=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Hi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,nn=Object.prototype.propertyIsEnumerable;var rn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&rn(e,r,t[r]);if(Ht)for(var r of Ht(t))nn.call(t,r)&&rn(e,r,t[r]);return e};var on=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&nn.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Pi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ki(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=Ri(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ci(Hi(e)):{},Pi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var sn=Pt((xr,an)=>{(function(e,t){typeof xr=="object"&&typeof an!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(O){return!!(O&&O!==document&&O.nodeName!=="HTML"&&O.nodeName!=="BODY"&&"classList"in O&&"contains"in O.classList)}function f(O){var Qe=O.type,De=O.tagName;return!!(De==="INPUT"&&s[Qe]&&!O.readOnly||De==="TEXTAREA"&&!O.readOnly||O.isContentEditable)}function c(O){O.classList.contains("focus-visible")||(O.classList.add("focus-visible"),O.setAttribute("data-focus-visible-added",""))}function u(O){O.hasAttribute("data-focus-visible-added")&&(O.classList.remove("focus-visible"),O.removeAttribute("data-focus-visible-added"))}function p(O){O.metaKey||O.altKey||O.ctrlKey||(a(r.activeElement)&&c(r.activeElement),n=!0)}function m(O){n=!1}function d(O){a(O.target)&&(n||f(O.target))&&c(O.target)}function h(O){a(O.target)&&(O.target.classList.contains("focus-visible")||O.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(O.target))}function v(O){document.visibilityState==="hidden"&&(o&&(n=!0),Y())}function Y(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function B(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(O){O.target.nodeName&&O.target.nodeName.toLowerCase()==="html"||(n=!1,B())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),Y(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var cn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},s=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(B,N){d.append(N,B)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(O){throw new Error("URL unable to set base "+c+" due to "+O)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,Y=!0,B=this;["append","delete","set"].forEach(function(O){var Qe=h[O];h[O]=function(){Qe.apply(h,arguments),v&&(Y=!1,B.search=h.toString(),Y=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,Y&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(f){Object.defineProperty(s,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){a(f)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var qr=Pt((Mt,Nr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Nr=="object"?Nr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ai}});var s=i(279),a=i.n(s),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(T){return!1}}var d=function(T){var E=p()(T);return m("cut"),E},h=d;function v(j){var T=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[T?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var Y=function(T,E){var H=v(T);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},B=function(T){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof T=="string"?H=Y(T,E):T instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(T==null?void 0:T.type)?H=Y(T.value,E):(H=p()(T),m("copy")),H},N=B;function O(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?O=function(E){return typeof E}:O=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},O(j)}var Qe=function(){var T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=T.action,H=E===void 0?"copy":E,I=T.container,q=T.target,Me=T.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&O(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function Ei(j,T){if(!(j instanceof T))throw new TypeError("Cannot call a class as a function")}function tn(j,T){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=c()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ai=Li},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,f){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(f))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return c(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),s=f.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var f=this;function c(){f.off(i,c),s.apply(a,arguments)}return c._=s,this.on(i,c,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=a.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var rs=/["'&<>]/;Yo.exports=ns;function ns(e){var t=""+e,r=rs.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i 0 && i[i.length - 1]) && (c[0] === 6 || c[0] === 2))) { - r = 0 - continue - } - if (c[0] === 3 && (!i || (c[1] > i[0] && c[1] < i[3]))) { - r.label = c[1] - break - } - if (c[0] === 6 && r.label < i[1]) { - ;(r.label = i[1]), (i = c) - break - } - if (i && r.label < i[2]) { - ;(r.label = i[2]), r.ops.push(c) - break - } - i[2] && r.ops.pop(), r.trys.pop() - continue - } - c = t.call(e, r) - } catch (u) { - ;(c = [6, u]), (o = 0) - } finally { - n = i = 0 - } - if (c[0] & 5) throw c[1] - return { value: c[0] ? c[1] : void 0, done: !0 } - } - } - function Ee(e) { - var t = typeof Symbol == "function" && Symbol.iterator, - r = t && e[t], - n = 0 - if (r) return r.call(e) - if (e && typeof e.length == "number") - return { - next: function () { - return e && n >= e.length && (e = void 0), { value: e && e[n++], done: !e } - } - } - throw new TypeError(t ? "Object is not iterable." : "Symbol.iterator is not defined.") - } - function W(e, t) { - var r = typeof Symbol == "function" && e[Symbol.iterator] - if (!r) return e - var n = r.call(e), - o, - i = [], - s - try { - for (; (t === void 0 || t-- > 0) && !(o = n.next()).done; ) i.push(o.value) - } catch (a) { - s = { error: a } - } finally { - try { - o && !o.done && (r = n.return) && r.call(n) - } finally { - if (s) throw s.error - } - } - return i - } - function D(e, t, r) { - if (r || arguments.length === 2) - for (var n = 0, o = t.length, i; n < o; n++) - (i || !(n in t)) && (i || (i = Array.prototype.slice.call(t, 0, n)), (i[n] = t[n])) - return e.concat(i || Array.prototype.slice.call(t)) - } - function et(e) { - return this instanceof et ? ((this.v = e), this) : new et(e) - } - function un(e, t, r) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.") - var n = r.apply(e, t || []), - o, - i = [] - return ( - (o = {}), - s("next"), - s("throw"), - s("return"), - (o[Symbol.asyncIterator] = function () { - return this - }), - o - ) - function s(m) { - n[m] && - (o[m] = function (d) { - return new Promise(function (h, v) { - i.push([m, d, h, v]) > 1 || a(m, d) - }) - }) - } - function a(m, d) { - try { - f(n[m](d)) - } catch (h) { - p(i[0][3], h) - } - } - function f(m) { - m.value instanceof et ? Promise.resolve(m.value.v).then(c, u) : p(i[0][2], m) - } - function c(m) { - a("next", m) - } - function u(m) { - a("throw", m) - } - function p(m, d) { - m(d), i.shift(), i.length && a(i[0][0], i[0][1]) - } - } - function pn(e) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.") - var t = e[Symbol.asyncIterator], - r - return t - ? t.call(e) - : ((e = typeof Ee == "function" ? Ee(e) : e[Symbol.iterator]()), - (r = {}), - n("next"), - n("throw"), - n("return"), - (r[Symbol.asyncIterator] = function () { - return this - }), - r) - function n(i) { - r[i] = - e[i] && - function (s) { - return new Promise(function (a, f) { - ;(s = e[i](s)), o(a, f, s.done, s.value) - }) - } - } - function o(i, s, a, f) { - Promise.resolve(f).then(function (c) { - i({ value: c, done: a }) - }, s) - } - } - function C(e) { - return typeof e == "function" - } - function at(e) { - var t = function (n) { - Error.call(n), (n.stack = new Error().stack) - }, - r = e(t) - return (r.prototype = Object.create(Error.prototype)), (r.prototype.constructor = r), r - } - var It = at(function (e) { - return function (r) { - e(this), - (this.message = r - ? r.length + - ` errors occurred during unsubscription: -` + - r.map(function (n, o) { - return o + 1 + ") " + n.toString() - }).join(` - `) - : ""), - (this.name = "UnsubscriptionError"), - (this.errors = r) - } - }) - function Ve(e, t) { - if (e) { - var r = e.indexOf(t) - 0 <= r && e.splice(r, 1) - } - } - var Ie = (function () { - function e(t) { - ;(this.initialTeardown = t), (this.closed = !1), (this._parentage = null), (this._finalizers = null) - } - return ( - (e.prototype.unsubscribe = function () { - var t, r, n, o, i - if (!this.closed) { - this.closed = !0 - var s = this._parentage - if (s) - if (((this._parentage = null), Array.isArray(s))) - try { - for (var a = Ee(s), f = a.next(); !f.done; f = a.next()) { - var c = f.value - c.remove(this) - } - } catch (v) { - t = { error: v } - } finally { - try { - f && !f.done && (r = a.return) && r.call(a) - } finally { - if (t) throw t.error - } - } - else s.remove(this) - var u = this.initialTeardown - if (C(u)) - try { - u() - } catch (v) { - i = v instanceof It ? v.errors : [v] - } - var p = this._finalizers - if (p) { - this._finalizers = null - try { - for (var m = Ee(p), d = m.next(); !d.done; d = m.next()) { - var h = d.value - try { - ln(h) - } catch (v) { - ;(i = i != null ? i : []), - v instanceof It ? (i = D(D([], W(i)), W(v.errors))) : i.push(v) - } - } - } catch (v) { - n = { error: v } - } finally { - try { - d && !d.done && (o = m.return) && o.call(m) - } finally { - if (n) throw n.error - } - } - } - if (i) throw new It(i) - } - }), - (e.prototype.add = function (t) { - var r - if (t && t !== this) - if (this.closed) ln(t) - else { - if (t instanceof e) { - if (t.closed || t._hasParent(this)) return - t._addParent(this) - } - ;(this._finalizers = (r = this._finalizers) !== null && r !== void 0 ? r : []).push(t) - } - }), - (e.prototype._hasParent = function (t) { - var r = this._parentage - return r === t || (Array.isArray(r) && r.includes(t)) - }), - (e.prototype._addParent = function (t) { - var r = this._parentage - this._parentage = Array.isArray(r) ? (r.push(t), r) : r ? [r, t] : t - }), - (e.prototype._removeParent = function (t) { - var r = this._parentage - r === t ? (this._parentage = null) : Array.isArray(r) && Ve(r, t) - }), - (e.prototype.remove = function (t) { - var r = this._finalizers - r && Ve(r, t), t instanceof e && t._removeParent(this) - }), - (e.EMPTY = (function () { - var t = new e() - return (t.closed = !0), t - })()), - e - ) - })() - var Sr = Ie.EMPTY - function jt(e) { - return e instanceof Ie || (e && "closed" in e && C(e.remove) && C(e.add) && C(e.unsubscribe)) - } - function ln(e) { - C(e) ? e() : e.unsubscribe() - } - var Le = { - onUnhandledError: null, - onStoppedNotification: null, - Promise: void 0, - useDeprecatedSynchronousErrorHandling: !1, - useDeprecatedNextContext: !1 - } - var st = { - setTimeout: function (e, t) { - for (var r = [], n = 2; n < arguments.length; n++) r[n - 2] = arguments[n] - var o = st.delegate - return o != null && o.setTimeout - ? o.setTimeout.apply(o, D([e, t], W(r))) - : setTimeout.apply(void 0, D([e, t], W(r))) - }, - clearTimeout: function (e) { - var t = st.delegate - return ((t == null ? void 0 : t.clearTimeout) || clearTimeout)(e) - }, - delegate: void 0 - } - function Ft(e) { - st.setTimeout(function () { - var t = Le.onUnhandledError - if (t) t(e) - else throw e - }) - } - function we() {} - var mn = (function () { - return Or("C", void 0, void 0) - })() - function dn(e) { - return Or("E", void 0, e) - } - function hn(e) { - return Or("N", e, void 0) - } - function Or(e, t, r) { - return { kind: e, value: t, error: r } - } - var tt = null - function ct(e) { - if (Le.useDeprecatedSynchronousErrorHandling) { - var t = !tt - if ((t && (tt = { errorThrown: !1, error: null }), e(), t)) { - var r = tt, - n = r.errorThrown, - o = r.error - if (((tt = null), n)) throw o - } - } else e() - } - function bn(e) { - Le.useDeprecatedSynchronousErrorHandling && tt && ((tt.errorThrown = !0), (tt.error = e)) - } - var xt = (function (e) { - ie(t, e) - function t(r) { - var n = e.call(this) || this - return (n.isStopped = !1), r ? ((n.destination = r), jt(r) && r.add(n)) : (n.destination = Fi), n - } - return ( - (t.create = function (r, n, o) { - return new rt(r, n, o) - }), - (t.prototype.next = function (r) { - this.isStopped ? _r(hn(r), this) : this._next(r) - }), - (t.prototype.error = function (r) { - this.isStopped ? _r(dn(r), this) : ((this.isStopped = !0), this._error(r)) - }), - (t.prototype.complete = function () { - this.isStopped ? _r(mn, this) : ((this.isStopped = !0), this._complete()) - }), - (t.prototype.unsubscribe = function () { - this.closed || ((this.isStopped = !0), e.prototype.unsubscribe.call(this), (this.destination = null)) - }), - (t.prototype._next = function (r) { - this.destination.next(r) - }), - (t.prototype._error = function (r) { - try { - this.destination.error(r) - } finally { - this.unsubscribe() - } - }), - (t.prototype._complete = function () { - try { - this.destination.complete() - } finally { - this.unsubscribe() - } - }), - t - ) - })(Ie) - var $i = Function.prototype.bind - function Tr(e, t) { - return $i.call(e, t) - } - var Ii = (function () { - function e(t) { - this.partialObserver = t - } - return ( - (e.prototype.next = function (t) { - var r = this.partialObserver - if (r.next) - try { - r.next(t) - } catch (n) { - Ut(n) - } - }), - (e.prototype.error = function (t) { - var r = this.partialObserver - if (r.error) - try { - r.error(t) - } catch (n) { - Ut(n) - } - else Ut(t) - }), - (e.prototype.complete = function () { - var t = this.partialObserver - if (t.complete) - try { - t.complete() - } catch (r) { - Ut(r) - } - }), - e - ) - })(), - rt = (function (e) { - ie(t, e) - function t(r, n, o) { - var i = e.call(this) || this, - s - if (C(r) || !r) - s = { - next: r != null ? r : void 0, - error: n != null ? n : void 0, - complete: o != null ? o : void 0 - } - else { - var a - i && Le.useDeprecatedNextContext - ? ((a = Object.create(r)), - (a.unsubscribe = function () { - return i.unsubscribe() - }), - (s = { - next: r.next && Tr(r.next, a), - error: r.error && Tr(r.error, a), - complete: r.complete && Tr(r.complete, a) - })) - : (s = r) - } - return (i.destination = new Ii(s)), i - } - return t - })(xt) - function Ut(e) { - Le.useDeprecatedSynchronousErrorHandling ? bn(e) : Ft(e) - } - function ji(e) { - throw e - } - function _r(e, t) { - var r = Le.onStoppedNotification - r && - st.setTimeout(function () { - return r(e, t) - }) - } - var Fi = { closed: !0, next: we, error: ji, complete: we } - var ft = (function () { - return (typeof Symbol == "function" && Symbol.observable) || "@@observable" - })() - function de(e) { - return e - } - function vn() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return Mr(e) - } - function Mr(e) { - return e.length === 0 - ? de - : e.length === 1 - ? e[0] - : function (r) { - return e.reduce(function (n, o) { - return o(n) - }, r) - } - } - var F = (function () { - function e(t) { - t && (this._subscribe = t) - } - return ( - (e.prototype.lift = function (t) { - var r = new e() - return (r.source = this), (r.operator = t), r - }), - (e.prototype.subscribe = function (t, r, n) { - var o = this, - i = Wi(t) ? t : new rt(t, r, n) - return ( - ct(function () { - var s = o, - a = s.operator, - f = s.source - i.add(a ? a.call(i, f) : f ? o._subscribe(i) : o._trySubscribe(i)) - }), - i - ) - }), - (e.prototype._trySubscribe = function (t) { - try { - return this._subscribe(t) - } catch (r) { - t.error(r) - } - }), - (e.prototype.forEach = function (t, r) { - var n = this - return ( - (r = gn(r)), - new r(function (o, i) { - var s = new rt({ - next: function (a) { - try { - t(a) - } catch (f) { - i(f), s.unsubscribe() - } - }, - error: i, - complete: o - }) - n.subscribe(s) - }) - ) - }), - (e.prototype._subscribe = function (t) { - var r - return (r = this.source) === null || r === void 0 ? void 0 : r.subscribe(t) - }), - (e.prototype[ft] = function () { - return this - }), - (e.prototype.pipe = function () { - for (var t = [], r = 0; r < arguments.length; r++) t[r] = arguments[r] - return Mr(t)(this) - }), - (e.prototype.toPromise = function (t) { - var r = this - return ( - (t = gn(t)), - new t(function (n, o) { - var i - r.subscribe( - function (s) { - return (i = s) - }, - function (s) { - return o(s) - }, - function () { - return n(i) - } - ) - }) - ) - }), - (e.create = function (t) { - return new e(t) - }), - e - ) - })() - function gn(e) { - var t - return (t = e != null ? e : Le.Promise) !== null && t !== void 0 ? t : Promise - } - function Ui(e) { - return e && C(e.next) && C(e.error) && C(e.complete) - } - function Wi(e) { - return (e && e instanceof xt) || (Ui(e) && jt(e)) - } - function Di(e) { - return C(e == null ? void 0 : e.lift) - } - function y(e) { - return function (t) { - if (Di(t)) - return t.lift(function (r) { - try { - return e(r, this) - } catch (n) { - this.error(n) - } - }) - throw new TypeError("Unable to lift unknown Observable type") - } - } - function w(e, t, r, n, o) { - return new Vi(e, t, r, n, o) - } - var Vi = (function (e) { - ie(t, e) - function t(r, n, o, i, s, a) { - var f = e.call(this, r) || this - return ( - (f.onFinalize = s), - (f.shouldUnsubscribe = a), - (f._next = n - ? function (c) { - try { - n(c) - } catch (u) { - r.error(u) - } - } - : e.prototype._next), - (f._error = i - ? function (c) { - try { - i(c) - } catch (u) { - r.error(u) - } finally { - this.unsubscribe() - } - } - : e.prototype._error), - (f._complete = o - ? function () { - try { - o() - } catch (c) { - r.error(c) - } finally { - this.unsubscribe() - } - } - : e.prototype._complete), - f - ) - } - return ( - (t.prototype.unsubscribe = function () { - var r - if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) { - var n = this.closed - e.prototype.unsubscribe.call(this), - !n && ((r = this.onFinalize) === null || r === void 0 || r.call(this)) - } - }), - t - ) - })(xt) - var ut = { - schedule: function (e) { - var t = requestAnimationFrame, - r = cancelAnimationFrame, - n = ut.delegate - n && ((t = n.requestAnimationFrame), (r = n.cancelAnimationFrame)) - var o = t(function (i) { - ;(r = void 0), e(i) - }) - return new Ie(function () { - return r == null ? void 0 : r(o) - }) - }, - requestAnimationFrame: function () { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = ut.delegate - return ((r == null ? void 0 : r.requestAnimationFrame) || requestAnimationFrame).apply(void 0, D([], W(e))) - }, - cancelAnimationFrame: function () { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = ut.delegate - return ((r == null ? void 0 : r.cancelAnimationFrame) || cancelAnimationFrame).apply(void 0, D([], W(e))) - }, - delegate: void 0 - } - var yn = at(function (e) { - return function () { - e(this), (this.name = "ObjectUnsubscribedError"), (this.message = "object unsubscribed") - } - }) - var x = (function (e) { - ie(t, e) - function t() { - var r = e.call(this) || this - return ( - (r.closed = !1), - (r.currentObservers = null), - (r.observers = []), - (r.isStopped = !1), - (r.hasError = !1), - (r.thrownError = null), - r - ) - } - return ( - (t.prototype.lift = function (r) { - var n = new xn(this, this) - return (n.operator = r), n - }), - (t.prototype._throwIfClosed = function () { - if (this.closed) throw new yn() - }), - (t.prototype.next = function (r) { - var n = this - ct(function () { - var o, i - if ((n._throwIfClosed(), !n.isStopped)) { - n.currentObservers || (n.currentObservers = Array.from(n.observers)) - try { - for (var s = Ee(n.currentObservers), a = s.next(); !a.done; a = s.next()) { - var f = a.value - f.next(r) - } - } catch (c) { - o = { error: c } - } finally { - try { - a && !a.done && (i = s.return) && i.call(s) - } finally { - if (o) throw o.error - } - } - } - }) - }), - (t.prototype.error = function (r) { - var n = this - ct(function () { - if ((n._throwIfClosed(), !n.isStopped)) { - ;(n.hasError = n.isStopped = !0), (n.thrownError = r) - for (var o = n.observers; o.length; ) o.shift().error(r) - } - }) - }), - (t.prototype.complete = function () { - var r = this - ct(function () { - if ((r._throwIfClosed(), !r.isStopped)) { - r.isStopped = !0 - for (var n = r.observers; n.length; ) n.shift().complete() - } - }) - }), - (t.prototype.unsubscribe = function () { - ;(this.isStopped = this.closed = !0), (this.observers = this.currentObservers = null) - }), - Object.defineProperty(t.prototype, "observed", { - get: function () { - var r - return ((r = this.observers) === null || r === void 0 ? void 0 : r.length) > 0 - }, - enumerable: !1, - configurable: !0 - }), - (t.prototype._trySubscribe = function (r) { - return this._throwIfClosed(), e.prototype._trySubscribe.call(this, r) - }), - (t.prototype._subscribe = function (r) { - return this._throwIfClosed(), this._checkFinalizedStatuses(r), this._innerSubscribe(r) - }), - (t.prototype._innerSubscribe = function (r) { - var n = this, - o = this, - i = o.hasError, - s = o.isStopped, - a = o.observers - return i || s - ? Sr - : ((this.currentObservers = null), - a.push(r), - new Ie(function () { - ;(n.currentObservers = null), Ve(a, r) - })) - }), - (t.prototype._checkFinalizedStatuses = function (r) { - var n = this, - o = n.hasError, - i = n.thrownError, - s = n.isStopped - o ? r.error(i) : s && r.complete() - }), - (t.prototype.asObservable = function () { - var r = new F() - return (r.source = this), r - }), - (t.create = function (r, n) { - return new xn(r, n) - }), - t - ) - })(F) - var xn = (function (e) { - ie(t, e) - function t(r, n) { - var o = e.call(this) || this - return (o.destination = r), (o.source = n), o - } - return ( - (t.prototype.next = function (r) { - var n, o - ;(o = (n = this.destination) === null || n === void 0 ? void 0 : n.next) === null || - o === void 0 || - o.call(n, r) - }), - (t.prototype.error = function (r) { - var n, o - ;(o = (n = this.destination) === null || n === void 0 ? void 0 : n.error) === null || - o === void 0 || - o.call(n, r) - }), - (t.prototype.complete = function () { - var r, n - ;(n = (r = this.destination) === null || r === void 0 ? void 0 : r.complete) === null || - n === void 0 || - n.call(r) - }), - (t.prototype._subscribe = function (r) { - var n, o - return (o = (n = this.source) === null || n === void 0 ? void 0 : n.subscribe(r)) !== null && - o !== void 0 - ? o - : Sr - }), - t - ) - })(x) - var Et = { - now: function () { - return (Et.delegate || Date).now() - }, - delegate: void 0 - } - var wt = (function (e) { - ie(t, e) - function t(r, n, o) { - r === void 0 && (r = 1 / 0), n === void 0 && (n = 1 / 0), o === void 0 && (o = Et) - var i = e.call(this) || this - return ( - (i._bufferSize = r), - (i._windowTime = n), - (i._timestampProvider = o), - (i._buffer = []), - (i._infiniteTimeWindow = !0), - (i._infiniteTimeWindow = n === 1 / 0), - (i._bufferSize = Math.max(1, r)), - (i._windowTime = Math.max(1, n)), - i - ) - } - return ( - (t.prototype.next = function (r) { - var n = this, - o = n.isStopped, - i = n._buffer, - s = n._infiniteTimeWindow, - a = n._timestampProvider, - f = n._windowTime - o || (i.push(r), !s && i.push(a.now() + f)), this._trimBuffer(), e.prototype.next.call(this, r) - }), - (t.prototype._subscribe = function (r) { - this._throwIfClosed(), this._trimBuffer() - for ( - var n = this._innerSubscribe(r), - o = this, - i = o._infiniteTimeWindow, - s = o._buffer, - a = s.slice(), - f = 0; - f < a.length && !r.closed; - f += i ? 1 : 2 - ) - r.next(a[f]) - return this._checkFinalizedStatuses(r), n - }), - (t.prototype._trimBuffer = function () { - var r = this, - n = r._bufferSize, - o = r._timestampProvider, - i = r._buffer, - s = r._infiniteTimeWindow, - a = (s ? 1 : 2) * n - if ((n < 1 / 0 && a < i.length && i.splice(0, i.length - a), !s)) { - for (var f = o.now(), c = 0, u = 1; u < i.length && i[u] <= f; u += 2) c = u - c && i.splice(0, c + 1) - } - }), - t - ) - })(x) - var En = (function (e) { - ie(t, e) - function t(r, n) { - return e.call(this) || this - } - return ( - (t.prototype.schedule = function (r, n) { - return n === void 0 && (n = 0), this - }), - t - ) - })(Ie) - var St = { - setInterval: function (e, t) { - for (var r = [], n = 2; n < arguments.length; n++) r[n - 2] = arguments[n] - var o = St.delegate - return o != null && o.setInterval - ? o.setInterval.apply(o, D([e, t], W(r))) - : setInterval.apply(void 0, D([e, t], W(r))) - }, - clearInterval: function (e) { - var t = St.delegate - return ((t == null ? void 0 : t.clearInterval) || clearInterval)(e) - }, - delegate: void 0 - } - var Wt = (function (e) { - ie(t, e) - function t(r, n) { - var o = e.call(this, r, n) || this - return (o.scheduler = r), (o.work = n), (o.pending = !1), o - } - return ( - (t.prototype.schedule = function (r, n) { - var o - if ((n === void 0 && (n = 0), this.closed)) return this - this.state = r - var i = this.id, - s = this.scheduler - return ( - i != null && (this.id = this.recycleAsyncId(s, i, n)), - (this.pending = !0), - (this.delay = n), - (this.id = (o = this.id) !== null && o !== void 0 ? o : this.requestAsyncId(s, this.id, n)), - this - ) - }), - (t.prototype.requestAsyncId = function (r, n, o) { - return o === void 0 && (o = 0), St.setInterval(r.flush.bind(r, this), o) - }), - (t.prototype.recycleAsyncId = function (r, n, o) { - if ((o === void 0 && (o = 0), o != null && this.delay === o && this.pending === !1)) return n - n != null && St.clearInterval(n) - }), - (t.prototype.execute = function (r, n) { - if (this.closed) return new Error("executing a cancelled action") - this.pending = !1 - var o = this._execute(r, n) - if (o) return o - this.pending === !1 && this.id != null && (this.id = this.recycleAsyncId(this.scheduler, this.id, null)) - }), - (t.prototype._execute = function (r, n) { - var o = !1, - i - try { - this.work(r) - } catch (s) { - ;(o = !0), (i = s || new Error("Scheduled action threw falsy error")) - } - if (o) return this.unsubscribe(), i - }), - (t.prototype.unsubscribe = function () { - if (!this.closed) { - var r = this, - n = r.id, - o = r.scheduler, - i = o.actions - ;(this.work = this.state = this.scheduler = null), - (this.pending = !1), - Ve(i, this), - n != null && (this.id = this.recycleAsyncId(o, n, null)), - (this.delay = null), - e.prototype.unsubscribe.call(this) - } - }), - t - ) - })(En) - var Lr = (function () { - function e(t, r) { - r === void 0 && (r = e.now), (this.schedulerActionCtor = t), (this.now = r) - } - return ( - (e.prototype.schedule = function (t, r, n) { - return r === void 0 && (r = 0), new this.schedulerActionCtor(this, t).schedule(n, r) - }), - (e.now = Et.now), - e - ) - })() - var Dt = (function (e) { - ie(t, e) - function t(r, n) { - n === void 0 && (n = Lr.now) - var o = e.call(this, r, n) || this - return (o.actions = []), (o._active = !1), o - } - return ( - (t.prototype.flush = function (r) { - var n = this.actions - if (this._active) { - n.push(r) - return - } - var o - this._active = !0 - do if ((o = r.execute(r.state, r.delay))) break - while ((r = n.shift())) - if (((this._active = !1), o)) { - for (; (r = n.shift()); ) r.unsubscribe() - throw o - } - }), - t - ) - })(Lr) - var ae = new Dt(Wt), - Ar = ae - var wn = (function (e) { - ie(t, e) - function t(r, n) { - var o = e.call(this, r, n) || this - return (o.scheduler = r), (o.work = n), o - } - return ( - (t.prototype.requestAsyncId = function (r, n, o) { - return ( - o === void 0 && (o = 0), - o !== null && o > 0 - ? e.prototype.requestAsyncId.call(this, r, n, o) - : (r.actions.push(this), - r._scheduled || - (r._scheduled = ut.requestAnimationFrame(function () { - return r.flush(void 0) - }))) - ) - }), - (t.prototype.recycleAsyncId = function (r, n, o) { - var i - if ((o === void 0 && (o = 0), o != null ? o > 0 : this.delay > 0)) - return e.prototype.recycleAsyncId.call(this, r, n, o) - var s = r.actions - n != null && - ((i = s[s.length - 1]) === null || i === void 0 ? void 0 : i.id) !== n && - (ut.cancelAnimationFrame(n), (r._scheduled = void 0)) - }), - t - ) - })(Wt) - var Sn = (function (e) { - ie(t, e) - function t() { - return (e !== null && e.apply(this, arguments)) || this - } - return ( - (t.prototype.flush = function (r) { - this._active = !0 - var n = this._scheduled - this._scheduled = void 0 - var o = this.actions, - i - r = r || o.shift() - do if ((i = r.execute(r.state, r.delay))) break - while ((r = o[0]) && r.id === n && o.shift()) - if (((this._active = !1), i)) { - for (; (r = o[0]) && r.id === n && o.shift(); ) r.unsubscribe() - throw i - } - }), - t - ) - })(Dt) - var Oe = new Sn(wn) - var M = new F(function (e) { - return e.complete() - }) - function Vt(e) { - return e && C(e.schedule) - } - function Cr(e) { - return e[e.length - 1] - } - function Ye(e) { - return C(Cr(e)) ? e.pop() : void 0 - } - function Te(e) { - return Vt(Cr(e)) ? e.pop() : void 0 - } - function zt(e, t) { - return typeof Cr(e) == "number" ? e.pop() : t - } - var pt = function (e) { - return e && typeof e.length == "number" && typeof e != "function" - } - function Nt(e) { - return C(e == null ? void 0 : e.then) - } - function qt(e) { - return C(e[ft]) - } - function Kt(e) { - return Symbol.asyncIterator && C(e == null ? void 0 : e[Symbol.asyncIterator]) - } - function Qt(e) { - return new TypeError( - "You provided " + - (e !== null && typeof e == "object" ? "an invalid object" : "'" + e + "'") + - " where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable." - ) - } - function zi() { - return typeof Symbol != "function" || !Symbol.iterator ? "@@iterator" : Symbol.iterator - } - var Yt = zi() - function Gt(e) { - return C(e == null ? void 0 : e[Yt]) - } - function Bt(e) { - return un(this, arguments, function () { - var r, n, o, i - return $t(this, function (s) { - switch (s.label) { - case 0: - ;(r = e.getReader()), (s.label = 1) - case 1: - s.trys.push([1, , 9, 10]), (s.label = 2) - case 2: - return [4, et(r.read())] - case 3: - return (n = s.sent()), (o = n.value), (i = n.done), i ? [4, et(void 0)] : [3, 5] - case 4: - return [2, s.sent()] - case 5: - return [4, et(o)] - case 6: - return [4, s.sent()] - case 7: - return s.sent(), [3, 2] - case 8: - return [3, 10] - case 9: - return r.releaseLock(), [7] - case 10: - return [2] - } - }) - }) - } - function Jt(e) { - return C(e == null ? void 0 : e.getReader) - } - function U(e) { - if (e instanceof F) return e - if (e != null) { - if (qt(e)) return Ni(e) - if (pt(e)) return qi(e) - if (Nt(e)) return Ki(e) - if (Kt(e)) return On(e) - if (Gt(e)) return Qi(e) - if (Jt(e)) return Yi(e) - } - throw Qt(e) - } - function Ni(e) { - return new F(function (t) { - var r = e[ft]() - if (C(r.subscribe)) return r.subscribe(t) - throw new TypeError("Provided object does not correctly implement Symbol.observable") - }) - } - function qi(e) { - return new F(function (t) { - for (var r = 0; r < e.length && !t.closed; r++) t.next(e[r]) - t.complete() - }) - } - function Ki(e) { - return new F(function (t) { - e.then( - function (r) { - t.closed || (t.next(r), t.complete()) - }, - function (r) { - return t.error(r) - } - ).then(null, Ft) - }) - } - function Qi(e) { - return new F(function (t) { - var r, n - try { - for (var o = Ee(e), i = o.next(); !i.done; i = o.next()) { - var s = i.value - if ((t.next(s), t.closed)) return - } - } catch (a) { - r = { error: a } - } finally { - try { - i && !i.done && (n = o.return) && n.call(o) - } finally { - if (r) throw r.error - } - } - t.complete() - }) - } - function On(e) { - return new F(function (t) { - Gi(e, t).catch(function (r) { - return t.error(r) - }) - }) - } - function Yi(e) { - return On(Bt(e)) - } - function Gi(e, t) { - var r, n, o, i - return fn(this, void 0, void 0, function () { - var s, a - return $t(this, function (f) { - switch (f.label) { - case 0: - f.trys.push([0, 5, 6, 11]), (r = pn(e)), (f.label = 1) - case 1: - return [4, r.next()] - case 2: - if (((n = f.sent()), !!n.done)) return [3, 4] - if (((s = n.value), t.next(s), t.closed)) return [2] - f.label = 3 - case 3: - return [3, 1] - case 4: - return [3, 11] - case 5: - return (a = f.sent()), (o = { error: a }), [3, 11] - case 6: - return f.trys.push([6, , 9, 10]), n && !n.done && (i = r.return) ? [4, i.call(r)] : [3, 8] - case 7: - f.sent(), (f.label = 8) - case 8: - return [3, 10] - case 9: - if (o) throw o.error - return [7] - case 10: - return [7] - case 11: - return t.complete(), [2] - } - }) - }) - } - function ve(e, t, r, n, o) { - n === void 0 && (n = 0), o === void 0 && (o = !1) - var i = t.schedule(function () { - r(), o ? e.add(this.schedule(null, n)) : this.unsubscribe() - }, n) - if ((e.add(i), !o)) return i - } - function Ae(e, t) { - return ( - t === void 0 && (t = 0), - y(function (r, n) { - r.subscribe( - w( - n, - function (o) { - return ve( - n, - e, - function () { - return n.next(o) - }, - t - ) - }, - function () { - return ve( - n, - e, - function () { - return n.complete() - }, - t - ) - }, - function (o) { - return ve( - n, - e, - function () { - return n.error(o) - }, - t - ) - } - ) - ) - }) - ) - } - function nt(e, t) { - return ( - t === void 0 && (t = 0), - y(function (r, n) { - n.add( - e.schedule(function () { - return r.subscribe(n) - }, t) - ) - }) - ) - } - function Tn(e, t) { - return U(e).pipe(nt(t), Ae(t)) - } - function _n(e, t) { - return U(e).pipe(nt(t), Ae(t)) - } - function Mn(e, t) { - return new F(function (r) { - var n = 0 - return t.schedule(function () { - n === e.length ? r.complete() : (r.next(e[n++]), r.closed || this.schedule()) - }) - }) - } - function Ln(e, t) { - return new F(function (r) { - var n - return ( - ve(r, t, function () { - ;(n = e[Yt]()), - ve( - r, - t, - function () { - var o, i, s - try { - ;(o = n.next()), (i = o.value), (s = o.done) - } catch (a) { - r.error(a) - return - } - s ? r.complete() : r.next(i) - }, - 0, - !0 - ) - }), - function () { - return C(n == null ? void 0 : n.return) && n.return() - } - ) - }) - } - function Xt(e, t) { - if (!e) throw new Error("Iterable cannot be null") - return new F(function (r) { - ve(r, t, function () { - var n = e[Symbol.asyncIterator]() - ve( - r, - t, - function () { - n.next().then(function (o) { - o.done ? r.complete() : r.next(o.value) - }) - }, - 0, - !0 - ) - }) - }) - } - function An(e, t) { - return Xt(Bt(e), t) - } - function Cn(e, t) { - if (e != null) { - if (qt(e)) return Tn(e, t) - if (pt(e)) return Mn(e, t) - if (Nt(e)) return _n(e, t) - if (Kt(e)) return Xt(e, t) - if (Gt(e)) return Ln(e, t) - if (Jt(e)) return An(e, t) - } - throw Qt(e) - } - function ue(e, t) { - return t ? Cn(e, t) : U(e) - } - function k() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Te(e) - return ue(e, r) - } - function Ot(e, t) { - var r = C(e) - ? e - : function () { - return e - }, - n = function (o) { - return o.error(r()) - } - return new F( - t - ? function (o) { - return t.schedule(n, 0, o) - } - : n - ) - } - var Zt = at(function (e) { - return function () { - e(this), (this.name = "EmptyError"), (this.message = "no elements in sequence") - } - }) - function Rn(e) { - return e instanceof Date && !isNaN(e) - } - function l(e, t) { - return y(function (r, n) { - var o = 0 - r.subscribe( - w(n, function (i) { - n.next(e.call(t, i, o++)) - }) - ) - }) - } - var Bi = Array.isArray - function Ji(e, t) { - return Bi(t) ? e.apply(void 0, D([], W(t))) : e(t) - } - function Ge(e) { - return l(function (t) { - return Ji(e, t) - }) - } - var Xi = Array.isArray, - Zi = Object.getPrototypeOf, - ea = Object.prototype, - ta = Object.keys - function kn(e) { - if (e.length === 1) { - var t = e[0] - if (Xi(t)) return { args: t, keys: null } - if (ra(t)) { - var r = ta(t) - return { - args: r.map(function (n) { - return t[n] - }), - keys: r - } - } - } - return { args: e, keys: null } - } - function ra(e) { - return e && typeof e == "object" && Zi(e) === ea - } - function Hn(e, t) { - return e.reduce(function (r, n, o) { - return (r[n] = t[o]), r - }, {}) - } - function G() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Te(e), - n = Ye(e), - o = kn(e), - i = o.args, - s = o.keys - if (i.length === 0) return ue([], r) - var a = new F( - Rr( - i, - r, - s - ? function (f) { - return Hn(s, f) - } - : de - ) - ) - return n ? a.pipe(Ge(n)) : a - } - function Rr(e, t, r) { - return ( - r === void 0 && (r = de), - function (n) { - Pn( - t, - function () { - for ( - var o = e.length, - i = new Array(o), - s = o, - a = o, - f = function (u) { - Pn( - t, - function () { - var p = ue(e[u], t), - m = !1 - p.subscribe( - w( - n, - function (d) { - ;(i[u] = d), m || ((m = !0), a--), a || n.next(r(i.slice())) - }, - function () { - --s || n.complete() - } - ) - ) - }, - n - ) - }, - c = 0; - c < o; - c++ - ) - f(c) - }, - n - ) - } - ) - } - function Pn(e, t, r) { - e ? ve(r, e, t) : t() - } - function $n(e, t, r, n, o, i, s, a) { - var f = [], - c = 0, - u = 0, - p = !1, - m = function () { - p && !f.length && !c && t.complete() - }, - d = function (v) { - return c < n ? h(v) : f.push(v) - }, - h = function (v) { - i && t.next(v), c++ - var Y = !1 - U(r(v, u++)).subscribe( - w( - t, - function (B) { - o == null || o(B), i ? d(B) : t.next(B) - }, - function () { - Y = !0 - }, - void 0, - function () { - if (Y) - try { - c-- - for ( - var B = function () { - var N = f.shift() - s - ? ve(t, s, function () { - return h(N) - }) - : h(N) - }; - f.length && c < n; - - ) - B() - m() - } catch (N) { - t.error(N) - } - } - ) - ) - } - return ( - e.subscribe( - w(t, d, function () { - ;(p = !0), m() - }) - ), - function () { - a == null || a() - } - ) - } - function se(e, t, r) { - return ( - r === void 0 && (r = 1 / 0), - C(t) - ? se(function (n, o) { - return l(function (i, s) { - return t(n, i, o, s) - })(U(e(n, o))) - }, r) - : (typeof t == "number" && (r = t), - y(function (n, o) { - return $n(n, o, e, r) - })) - ) - } - function lt(e) { - return e === void 0 && (e = 1 / 0), se(de, e) - } - function In() { - return lt(1) - } - function je() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return In()(ue(e, Te(e))) - } - function $(e) { - return new F(function (t) { - U(e()).subscribe(t) - }) - } - var na = ["addListener", "removeListener"], - oa = ["addEventListener", "removeEventListener"], - ia = ["on", "off"] - function b(e, t, r, n) { - if ((C(r) && ((n = r), (r = void 0)), n)) return b(e, t, r).pipe(Ge(n)) - var o = W( - ca(e) - ? oa.map(function (a) { - return function (f) { - return e[a](t, f, r) - } - }) - : aa(e) - ? na.map(jn(e, t)) - : sa(e) - ? ia.map(jn(e, t)) - : [], - 2 - ), - i = o[0], - s = o[1] - if (!i && pt(e)) - return se(function (a) { - return b(a, t, r) - })(U(e)) - if (!i) throw new TypeError("Invalid event target") - return new F(function (a) { - var f = function () { - for (var c = [], u = 0; u < arguments.length; u++) c[u] = arguments[u] - return a.next(1 < c.length ? c : c[0]) - } - return ( - i(f), - function () { - return s(f) - } - ) - }) - } - function jn(e, t) { - return function (r) { - return function (n) { - return e[r](t, n) - } - } - } - function aa(e) { - return C(e.addListener) && C(e.removeListener) - } - function sa(e) { - return C(e.on) && C(e.off) - } - function ca(e) { - return C(e.addEventListener) && C(e.removeEventListener) - } - function er(e, t, r) { - return r - ? er(e, t).pipe(Ge(r)) - : new F(function (n) { - var o = function () { - for (var s = [], a = 0; a < arguments.length; a++) s[a] = arguments[a] - return n.next(s.length === 1 ? s[0] : s) - }, - i = e(o) - return C(t) - ? function () { - return t(o, i) - } - : void 0 - }) - } - function Be(e, t, r) { - e === void 0 && (e = 0), r === void 0 && (r = Ar) - var n = -1 - return ( - t != null && (Vt(t) ? (r = t) : (n = t)), - new F(function (o) { - var i = Rn(e) ? +e - r.now() : e - i < 0 && (i = 0) - var s = 0 - return r.schedule(function () { - o.closed || (o.next(s++), 0 <= n ? this.schedule(void 0, n) : o.complete()) - }, i) - }) - ) - } - function L() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Te(e), - n = zt(e, 1 / 0), - o = e - return o.length ? (o.length === 1 ? U(o[0]) : lt(n)(ue(o, r))) : M - } - var ze = new F(we) - var fa = Array.isArray - function mt(e) { - return e.length === 1 && fa(e[0]) ? e[0] : e - } - function A(e, t) { - return y(function (r, n) { - var o = 0 - r.subscribe( - w(n, function (i) { - return e.call(t, i, o++) && n.next(i) - }) - ) - }) - } - function Tt() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Ye(e), - n = mt(e) - return n.length - ? new F(function (o) { - var i = n.map(function () { - return [] - }), - s = n.map(function () { - return !1 - }) - o.add(function () { - i = s = null - }) - for ( - var a = function (c) { - U(n[c]).subscribe( - w( - o, - function (u) { - if ( - (i[c].push(u), - i.every(function (m) { - return m.length - })) - ) { - var p = i.map(function (m) { - return m.shift() - }) - o.next(r ? r.apply(void 0, D([], W(p))) : p), - i.some(function (m, d) { - return !m.length && s[d] - }) && o.complete() - } - }, - function () { - ;(s[c] = !0), !i[c].length && o.complete() - } - ) - ) - }, - f = 0; - !o.closed && f < n.length; - f++ - ) - a(f) - return function () { - i = s = null - } - }) - : M - } - function Fn(e) { - return y(function (t, r) { - var n = !1, - o = null, - i = null, - s = !1, - a = function () { - if ((i == null || i.unsubscribe(), (i = null), n)) { - n = !1 - var c = o - ;(o = null), r.next(c) - } - s && r.complete() - }, - f = function () { - ;(i = null), s && r.complete() - } - t.subscribe( - w( - r, - function (c) { - ;(n = !0), (o = c), i || U(e(c)).subscribe((i = w(r, a, f))) - }, - function () { - ;(s = !0), (!n || !i || i.closed) && r.complete() - } - ) - ) - }) - } - function Ce(e, t) { - return ( - t === void 0 && (t = ae), - Fn(function () { - return Be(e, t) - }) - ) - } - function Re(e, t) { - return ( - t === void 0 && (t = null), - (t = t != null ? t : e), - y(function (r, n) { - var o = [], - i = 0 - r.subscribe( - w( - n, - function (s) { - var a, - f, - c, - u, - p = null - i++ % t === 0 && o.push([]) - try { - for (var m = Ee(o), d = m.next(); !d.done; d = m.next()) { - var h = d.value - h.push(s), e <= h.length && ((p = p != null ? p : []), p.push(h)) - } - } catch (B) { - a = { error: B } - } finally { - try { - d && !d.done && (f = m.return) && f.call(m) - } finally { - if (a) throw a.error - } - } - if (p) - try { - for (var v = Ee(p), Y = v.next(); !Y.done; Y = v.next()) { - var h = Y.value - Ve(o, h), n.next(h) - } - } catch (B) { - c = { error: B } - } finally { - try { - Y && !Y.done && (u = v.return) && u.call(v) - } finally { - if (c) throw c.error - } - } - }, - function () { - var s, a - try { - for (var f = Ee(o), c = f.next(); !c.done; c = f.next()) { - var u = c.value - n.next(u) - } - } catch (p) { - s = { error: p } - } finally { - try { - c && !c.done && (a = f.return) && a.call(f) - } finally { - if (s) throw s.error - } - } - n.complete() - }, - void 0, - function () { - o = null - } - ) - ) - }) - ) - } - function fe(e) { - return y(function (t, r) { - var n = null, - o = !1, - i - ;(n = t.subscribe( - w(r, void 0, void 0, function (s) { - ;(i = U(e(s, fe(e)(t)))), n ? (n.unsubscribe(), (n = null), i.subscribe(r)) : (o = !0) - }) - )), - o && (n.unsubscribe(), (n = null), i.subscribe(r)) - }) - } - function Un(e, t, r, n, o) { - return function (i, s) { - var a = r, - f = t, - c = 0 - i.subscribe( - w( - s, - function (u) { - var p = c++ - ;(f = a ? e(f, u, p) : ((a = !0), u)), n && s.next(f) - }, - o && - function () { - a && s.next(f), s.complete() - } - ) - ) - } - } - function kr() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Ye(e) - return r - ? vn(kr.apply(void 0, D([], W(e))), Ge(r)) - : y(function (n, o) { - Rr(D([n], W(mt(e))))(o) - }) - } - function Je() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return kr.apply(void 0, D([], W(e))) - } - function ke(e, t) { - return ( - t === void 0 && (t = ae), - y(function (r, n) { - var o = null, - i = null, - s = null, - a = function () { - if (o) { - o.unsubscribe(), (o = null) - var c = i - ;(i = null), n.next(c) - } - } - function f() { - var c = s + e, - u = t.now() - if (u < c) { - ;(o = this.schedule(void 0, c - u)), n.add(o) - return - } - a() - } - r.subscribe( - w( - n, - function (c) { - ;(i = c), (s = t.now()), o || ((o = t.schedule(f, e)), n.add(o)) - }, - function () { - a(), n.complete() - }, - void 0, - function () { - i = o = null - } - ) - ) - }) - ) - } - function He(e) { - return y(function (t, r) { - var n = !1 - t.subscribe( - w( - r, - function (o) { - ;(n = !0), r.next(o) - }, - function () { - n || r.next(e), r.complete() - } - ) - ) - }) - } - function ge(e) { - return e <= 0 - ? function () { - return M - } - : y(function (t, r) { - var n = 0 - t.subscribe( - w(r, function (o) { - ++n <= e && (r.next(o), e <= n && r.complete()) - }) - ) - }) - } - function Z() { - return y(function (e, t) { - e.subscribe(w(t, we)) - }) - } - function Wn(e) { - return l(function () { - return e - }) - } - function Hr(e, t) { - return t - ? function (r) { - return je(t.pipe(ge(1), Z()), r.pipe(Hr(e))) - } - : se(function (r, n) { - return U(e(r, n)).pipe(ge(1), Wn(r)) - }) - } - function Ne(e, t) { - t === void 0 && (t = ae) - var r = Be(e, t) - return Hr(function () { - return r - }) - } - function J(e, t) { - return ( - t === void 0 && (t = de), - (e = e != null ? e : ua), - y(function (r, n) { - var o, - i = !0 - r.subscribe( - w(n, function (s) { - var a = t(s) - ;(i || !e(o, a)) && ((i = !1), (o = a), n.next(s)) - }) - ) - }) - ) - } - function ua(e, t) { - return e === t - } - function ee(e, t) { - return J(function (r, n) { - return t ? t(r[e], n[e]) : r[e] === n[e] - }) - } - function Dn(e) { - return ( - e === void 0 && (e = pa), - y(function (t, r) { - var n = !1 - t.subscribe( - w( - r, - function (o) { - ;(n = !0), r.next(o) - }, - function () { - return n ? r.complete() : r.error(e()) - } - ) - ) - }) - ) - } - function pa() { - return new Zt() - } - function te() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return function (r) { - return je(r, k.apply(void 0, D([], W(e)))) - } - } - function R(e) { - return y(function (t, r) { - try { - t.subscribe(r) - } finally { - r.add(e) - } - }) - } - function Pe(e, t) { - var r = arguments.length >= 2 - return function (n) { - return n.pipe( - e - ? A(function (o, i) { - return e(o, i, n) - }) - : de, - ge(1), - r - ? He(t) - : Dn(function () { - return new Zt() - }) - ) - } - } - function Vn() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Te(e), - n = zt(e, 1 / 0) - return ( - (e = mt(e)), - y(function (o, i) { - lt(n)(ue(D([o], W(e)), r)).subscribe(i) - }) - ) - } - function qe() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return Vn.apply(void 0, D([], W(e))) - } - function _t(e) { - var t, - r = 1 / 0, - n - return ( - e != null && - (typeof e == "object" ? ((t = e.count), (r = t === void 0 ? 1 / 0 : t), (n = e.delay)) : (r = e)), - r <= 0 - ? function () { - return M - } - : y(function (o, i) { - var s = 0, - a, - f = function () { - if ((a == null || a.unsubscribe(), (a = null), n != null)) { - var u = typeof n == "number" ? Be(n) : U(n(s)), - p = w(i, function () { - p.unsubscribe(), c() - }) - u.subscribe(p) - } else c() - }, - c = function () { - var u = !1 - ;(a = o.subscribe( - w(i, void 0, function () { - ++s < r ? (a ? f() : (u = !0)) : i.complete() - }) - )), - u && f() - } - c() - }) - ) - } - function Pr(e, t) { - return y(Un(e, t, arguments.length >= 2, !0)) - } - function pe(e) { - e === void 0 && (e = {}) - var t = e.connector, - r = - t === void 0 - ? function () { - return new x() - } - : t, - n = e.resetOnError, - o = n === void 0 ? !0 : n, - i = e.resetOnComplete, - s = i === void 0 ? !0 : i, - a = e.resetOnRefCountZero, - f = a === void 0 ? !0 : a - return function (c) { - var u, - p, - m, - d = 0, - h = !1, - v = !1, - Y = function () { - p == null || p.unsubscribe(), (p = void 0) - }, - B = function () { - Y(), (u = m = void 0), (h = v = !1) - }, - N = function () { - var O = u - B(), O == null || O.unsubscribe() - } - return y(function (O, Qe) { - d++, !v && !h && Y() - var De = (m = m != null ? m : r()) - Qe.add(function () { - d--, d === 0 && !v && !h && (p = $r(N, f)) - }), - De.subscribe(Qe), - !u && - d > 0 && - ((u = new rt({ - next: function ($e) { - return De.next($e) - }, - error: function ($e) { - ;(v = !0), Y(), (p = $r(B, o, $e)), De.error($e) - }, - complete: function () { - ;(h = !0), Y(), (p = $r(B, s)), De.complete() - } - })), - U(O).subscribe(u)) - })(c) - } - } - function $r(e, t) { - for (var r = [], n = 2; n < arguments.length; n++) r[n - 2] = arguments[n] - if (t === !0) { - e() - return - } - if (t !== !1) { - var o = new rt({ - next: function () { - o.unsubscribe(), e() - } - }) - return U(t.apply(void 0, D([], W(r)))).subscribe(o) - } - } - function X(e, t, r) { - var n, - o, - i, - s, - a = !1 - return ( - e && typeof e == "object" - ? ((n = e.bufferSize), - (s = n === void 0 ? 1 / 0 : n), - (o = e.windowTime), - (t = o === void 0 ? 1 / 0 : o), - (i = e.refCount), - (a = i === void 0 ? !1 : i), - (r = e.scheduler)) - : (s = e != null ? e : 1 / 0), - pe({ - connector: function () { - return new wt(s, t, r) - }, - resetOnError: !0, - resetOnComplete: !1, - resetOnRefCountZero: a - }) - ) - } - function Fe(e) { - return A(function (t, r) { - return e <= r - }) - } - function Ir(e) { - return y(function (t, r) { - var n = !1, - o = w( - r, - function () { - o == null || o.unsubscribe(), (n = !0) - }, - we - ) - U(e).subscribe(o), - t.subscribe( - w(r, function (i) { - return n && r.next(i) - }) - ) - }) - } - function V() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Te(e) - return y(function (n, o) { - ;(r ? je(e, n, r) : je(e, n)).subscribe(o) - }) - } - function g(e, t) { - return y(function (r, n) { - var o = null, - i = 0, - s = !1, - a = function () { - return s && !o && n.complete() - } - r.subscribe( - w( - n, - function (f) { - o == null || o.unsubscribe() - var c = 0, - u = i++ - U(e(f, u)).subscribe( - (o = w( - n, - function (p) { - return n.next(t ? t(f, p, u, c++) : p) - }, - function () { - ;(o = null), a() - } - )) - ) - }, - function () { - ;(s = !0), a() - } - ) - ) - }) - } - function Q(e) { - return y(function (t, r) { - U(e).subscribe( - w( - r, - function () { - return r.complete() - }, - we - ) - ), - !r.closed && t.subscribe(r) - }) - } - function jr(e, t) { - return ( - t === void 0 && (t = !1), - y(function (r, n) { - var o = 0 - r.subscribe( - w(n, function (i) { - var s = e(i, o++) - ;(s || t) && n.next(i), !s && n.complete() - }) - ) - }) - ) - } - function S(e, t, r) { - var n = C(e) || t || r ? { next: e, error: t, complete: r } : e - return n - ? y(function (o, i) { - var s - ;(s = n.subscribe) === null || s === void 0 || s.call(n) - var a = !0 - o.subscribe( - w( - i, - function (f) { - var c - ;(c = n.next) === null || c === void 0 || c.call(n, f), i.next(f) - }, - function () { - var f - ;(a = !1), (f = n.complete) === null || f === void 0 || f.call(n), i.complete() - }, - function (f) { - var c - ;(a = !1), (c = n.error) === null || c === void 0 || c.call(n, f), i.error(f) - }, - function () { - var f, c - a && ((f = n.unsubscribe) === null || f === void 0 || f.call(n)), - (c = n.finalize) === null || c === void 0 || c.call(n) - } - ) - ) - }) - : de - } - function zn(e, t) { - return y(function (r, n) { - var o = t != null ? t : {}, - i = o.leading, - s = i === void 0 ? !0 : i, - a = o.trailing, - f = a === void 0 ? !1 : a, - c = !1, - u = null, - p = null, - m = !1, - d = function () { - p == null || p.unsubscribe(), (p = null), f && (Y(), m && n.complete()) - }, - h = function () { - ;(p = null), m && n.complete() - }, - v = function (B) { - return (p = U(e(B)).subscribe(w(n, d, h))) - }, - Y = function () { - if (c) { - c = !1 - var B = u - ;(u = null), n.next(B), !m && v(B) - } - } - r.subscribe( - w( - n, - function (B) { - ;(c = !0), (u = B), !(p && !p.closed) && (s ? Y() : v(B)) - }, - function () { - ;(m = !0), !(f && c && p && !p.closed) && n.complete() - } - ) - ) - }) - } - function Fr(e, t, r) { - t === void 0 && (t = ae) - var n = Be(e, t) - return zn(function () { - return n - }, r) - } - function ne() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - var r = Ye(e) - return y(function (n, o) { - for ( - var i = e.length, - s = new Array(i), - a = e.map(function () { - return !1 - }), - f = !1, - c = function (p) { - U(e[p]).subscribe( - w( - o, - function (m) { - ;(s[p] = m), !f && !a[p] && ((a[p] = !0), (f = a.every(de)) && (a = null)) - }, - we - ) - ) - }, - u = 0; - u < i; - u++ - ) - c(u) - n.subscribe( - w(o, function (p) { - if (f) { - var m = D([p], W(s)) - o.next(r ? r.apply(void 0, D([], W(m))) : m) - } - }) - ) - }) - } - function Nn() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return y(function (r, n) { - Tt.apply(void 0, D([r], W(e))).subscribe(n) - }) - } - function Ur() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t] - return Nn.apply(void 0, D([], W(e))) - } - function qn() { - let e = new wt(1) - return b(document, "DOMContentLoaded", { once: !0 }).subscribe(() => e.next(document)), e - } - function K(e, t = document) { - return Array.from(t.querySelectorAll(e)) - } - function z(e, t = document) { - let r = ce(e, t) - if (typeof r == "undefined") throw new ReferenceError(`Missing element: expected "${e}" to be present`) - return r - } - function ce(e, t = document) { - return t.querySelector(e) || void 0 - } - function _e() { - return (document.activeElement instanceof HTMLElement && document.activeElement) || void 0 - } - function tr(e) { - return L(b(document.body, "focusin"), b(document.body, "focusout")).pipe( - ke(1), - l(() => { - let t = _e() - return typeof t != "undefined" ? e.contains(t) : !1 - }), - V(e === _e()), - J() - ) - } - function Xe(e) { - return { x: e.offsetLeft, y: e.offsetTop } - } - function Kn(e) { - return L(b(window, "load"), b(window, "resize")).pipe( - Ce(0, Oe), - l(() => Xe(e)), - V(Xe(e)) - ) - } - function rr(e) { - return { x: e.scrollLeft, y: e.scrollTop } - } - function dt(e) { - return L(b(e, "scroll"), b(window, "resize")).pipe( - Ce(0, Oe), - l(() => rr(e)), - V(rr(e)) - ) - } - var Yn = (function () { - if (typeof Map != "undefined") return Map - function e(t, r) { - var n = -1 - return ( - t.some(function (o, i) { - return o[0] === r ? ((n = i), !0) : !1 - }), - n - ) - } - return (function () { - function t() { - this.__entries__ = [] - } - return ( - Object.defineProperty(t.prototype, "size", { - get: function () { - return this.__entries__.length - }, - enumerable: !0, - configurable: !0 - }), - (t.prototype.get = function (r) { - var n = e(this.__entries__, r), - o = this.__entries__[n] - return o && o[1] - }), - (t.prototype.set = function (r, n) { - var o = e(this.__entries__, r) - ~o ? (this.__entries__[o][1] = n) : this.__entries__.push([r, n]) - }), - (t.prototype.delete = function (r) { - var n = this.__entries__, - o = e(n, r) - ~o && n.splice(o, 1) - }), - (t.prototype.has = function (r) { - return !!~e(this.__entries__, r) - }), - (t.prototype.clear = function () { - this.__entries__.splice(0) - }), - (t.prototype.forEach = function (r, n) { - n === void 0 && (n = null) - for (var o = 0, i = this.__entries__; o < i.length; o++) { - var s = i[o] - r.call(n, s[1], s[0]) - } - }), - t - ) - })() - })(), - Wr = typeof window != "undefined" && typeof document != "undefined" && window.document === document, - nr = (function () { - return typeof global != "undefined" && global.Math === Math - ? global - : typeof self != "undefined" && self.Math === Math - ? self - : typeof window != "undefined" && window.Math === Math - ? window - : Function("return this")() - })(), - la = (function () { - return typeof requestAnimationFrame == "function" - ? requestAnimationFrame.bind(nr) - : function (e) { - return setTimeout(function () { - return e(Date.now()) - }, 1e3 / 60) - } - })(), - ma = 2 - function da(e, t) { - var r = !1, - n = !1, - o = 0 - function i() { - r && ((r = !1), e()), n && a() - } - function s() { - la(i) - } - function a() { - var f = Date.now() - if (r) { - if (f - o < ma) return - n = !0 - } else (r = !0), (n = !1), setTimeout(s, t) - o = f - } - return a - } - var ha = 20, - ba = ["top", "right", "bottom", "left", "width", "height", "size", "weight"], - va = typeof MutationObserver != "undefined", - ga = (function () { - function e() { - ;(this.connected_ = !1), - (this.mutationEventsAdded_ = !1), - (this.mutationsObserver_ = null), - (this.observers_ = []), - (this.onTransitionEnd_ = this.onTransitionEnd_.bind(this)), - (this.refresh = da(this.refresh.bind(this), ha)) - } - return ( - (e.prototype.addObserver = function (t) { - ~this.observers_.indexOf(t) || this.observers_.push(t), this.connected_ || this.connect_() - }), - (e.prototype.removeObserver = function (t) { - var r = this.observers_, - n = r.indexOf(t) - ~n && r.splice(n, 1), !r.length && this.connected_ && this.disconnect_() - }), - (e.prototype.refresh = function () { - var t = this.updateObservers_() - t && this.refresh() - }), - (e.prototype.updateObservers_ = function () { - var t = this.observers_.filter(function (r) { - return r.gatherActive(), r.hasActive() - }) - return ( - t.forEach(function (r) { - return r.broadcastActive() - }), - t.length > 0 - ) - }), - (e.prototype.connect_ = function () { - !Wr || - this.connected_ || - (document.addEventListener("transitionend", this.onTransitionEnd_), - window.addEventListener("resize", this.refresh), - va - ? ((this.mutationsObserver_ = new MutationObserver(this.refresh)), - this.mutationsObserver_.observe(document, { - attributes: !0, - childList: !0, - characterData: !0, - subtree: !0 - })) - : (document.addEventListener("DOMSubtreeModified", this.refresh), - (this.mutationEventsAdded_ = !0)), - (this.connected_ = !0)) - }), - (e.prototype.disconnect_ = function () { - !Wr || - !this.connected_ || - (document.removeEventListener("transitionend", this.onTransitionEnd_), - window.removeEventListener("resize", this.refresh), - this.mutationsObserver_ && this.mutationsObserver_.disconnect(), - this.mutationEventsAdded_ && document.removeEventListener("DOMSubtreeModified", this.refresh), - (this.mutationsObserver_ = null), - (this.mutationEventsAdded_ = !1), - (this.connected_ = !1)) - }), - (e.prototype.onTransitionEnd_ = function (t) { - var r = t.propertyName, - n = r === void 0 ? "" : r, - o = ba.some(function (i) { - return !!~n.indexOf(i) - }) - o && this.refresh() - }), - (e.getInstance = function () { - return this.instance_ || (this.instance_ = new e()), this.instance_ - }), - (e.instance_ = null), - e - ) - })(), - Gn = function (e, t) { - for (var r = 0, n = Object.keys(t); r < n.length; r++) { - var o = n[r] - Object.defineProperty(e, o, { value: t[o], enumerable: !1, writable: !1, configurable: !0 }) - } - return e - }, - ht = function (e) { - var t = e && e.ownerDocument && e.ownerDocument.defaultView - return t || nr - }, - Bn = ir(0, 0, 0, 0) - function or(e) { - return parseFloat(e) || 0 - } - function Qn(e) { - for (var t = [], r = 1; r < arguments.length; r++) t[r - 1] = arguments[r] - return t.reduce(function (n, o) { - var i = e["border-" + o + "-width"] - return n + or(i) - }, 0) - } - function ya(e) { - for (var t = ["top", "right", "bottom", "left"], r = {}, n = 0, o = t; n < o.length; n++) { - var i = o[n], - s = e["padding-" + i] - r[i] = or(s) - } - return r - } - function xa(e) { - var t = e.getBBox() - return ir(0, 0, t.width, t.height) - } - function Ea(e) { - var t = e.clientWidth, - r = e.clientHeight - if (!t && !r) return Bn - var n = ht(e).getComputedStyle(e), - o = ya(n), - i = o.left + o.right, - s = o.top + o.bottom, - a = or(n.width), - f = or(n.height) - if ( - (n.boxSizing === "border-box" && - (Math.round(a + i) !== t && (a -= Qn(n, "left", "right") + i), - Math.round(f + s) !== r && (f -= Qn(n, "top", "bottom") + s)), - !Sa(e)) - ) { - var c = Math.round(a + i) - t, - u = Math.round(f + s) - r - Math.abs(c) !== 1 && (a -= c), Math.abs(u) !== 1 && (f -= u) - } - return ir(o.left, o.top, a, f) - } - var wa = (function () { - return typeof SVGGraphicsElement != "undefined" - ? function (e) { - return e instanceof ht(e).SVGGraphicsElement - } - : function (e) { - return e instanceof ht(e).SVGElement && typeof e.getBBox == "function" - } - })() - function Sa(e) { - return e === ht(e).document.documentElement - } - function Oa(e) { - return Wr ? (wa(e) ? xa(e) : Ea(e)) : Bn - } - function Ta(e) { - var t = e.x, - r = e.y, - n = e.width, - o = e.height, - i = typeof DOMRectReadOnly != "undefined" ? DOMRectReadOnly : Object, - s = Object.create(i.prototype) - return Gn(s, { x: t, y: r, width: n, height: o, top: r, right: t + n, bottom: o + r, left: t }), s - } - function ir(e, t, r, n) { - return { x: e, y: t, width: r, height: n } - } - var _a = (function () { - function e(t) { - ;(this.broadcastWidth = 0), - (this.broadcastHeight = 0), - (this.contentRect_ = ir(0, 0, 0, 0)), - (this.target = t) - } - return ( - (e.prototype.isActive = function () { - var t = Oa(this.target) - return (this.contentRect_ = t), t.width !== this.broadcastWidth || t.height !== this.broadcastHeight - }), - (e.prototype.broadcastRect = function () { - var t = this.contentRect_ - return (this.broadcastWidth = t.width), (this.broadcastHeight = t.height), t - }), - e - ) - })(), - Ma = (function () { - function e(t, r) { - var n = Ta(r) - Gn(this, { target: t, contentRect: n }) - } - return e - })(), - La = (function () { - function e(t, r, n) { - if (((this.activeObservations_ = []), (this.observations_ = new Yn()), typeof t != "function")) - throw new TypeError("The callback provided as parameter 1 is not a function.") - ;(this.callback_ = t), (this.controller_ = r), (this.callbackCtx_ = n) - } - return ( - (e.prototype.observe = function (t) { - if (!arguments.length) throw new TypeError("1 argument required, but only 0 present.") - if (!(typeof Element == "undefined" || !(Element instanceof Object))) { - if (!(t instanceof ht(t).Element)) throw new TypeError('parameter 1 is not of type "Element".') - var r = this.observations_ - r.has(t) || - (r.set(t, new _a(t)), this.controller_.addObserver(this), this.controller_.refresh()) - } - }), - (e.prototype.unobserve = function (t) { - if (!arguments.length) throw new TypeError("1 argument required, but only 0 present.") - if (!(typeof Element == "undefined" || !(Element instanceof Object))) { - if (!(t instanceof ht(t).Element)) throw new TypeError('parameter 1 is not of type "Element".') - var r = this.observations_ - r.has(t) && (r.delete(t), r.size || this.controller_.removeObserver(this)) - } - }), - (e.prototype.disconnect = function () { - this.clearActive(), this.observations_.clear(), this.controller_.removeObserver(this) - }), - (e.prototype.gatherActive = function () { - var t = this - this.clearActive(), - this.observations_.forEach(function (r) { - r.isActive() && t.activeObservations_.push(r) - }) - }), - (e.prototype.broadcastActive = function () { - if (this.hasActive()) { - var t = this.callbackCtx_, - r = this.activeObservations_.map(function (n) { - return new Ma(n.target, n.broadcastRect()) - }) - this.callback_.call(t, r, t), this.clearActive() - } - }), - (e.prototype.clearActive = function () { - this.activeObservations_.splice(0) - }), - (e.prototype.hasActive = function () { - return this.activeObservations_.length > 0 - }), - e - ) - })(), - Jn = typeof WeakMap != "undefined" ? new WeakMap() : new Yn(), - Xn = (function () { - function e(t) { - if (!(this instanceof e)) throw new TypeError("Cannot call a class as a function.") - if (!arguments.length) throw new TypeError("1 argument required, but only 0 present.") - var r = ga.getInstance(), - n = new La(t, r, this) - Jn.set(this, n) - } - return e - })() - ;["observe", "unobserve", "disconnect"].forEach(function (e) { - Xn.prototype[e] = function () { - var t - return (t = Jn.get(this))[e].apply(t, arguments) - } - }) - var Aa = (function () { - return typeof nr.ResizeObserver != "undefined" ? nr.ResizeObserver : Xn - })(), - Zn = Aa - var eo = new x(), - Ca = $(() => - k( - new Zn(e => { - for (let t of e) eo.next(t) - }) - ) - ).pipe( - g(e => L(ze, k(e)).pipe(R(() => e.disconnect()))), - X(1) - ) - function he(e) { - return { width: e.offsetWidth, height: e.offsetHeight } - } - function ye(e) { - return Ca.pipe( - S(t => t.observe(e)), - g(t => - eo.pipe( - A(({ target: r }) => r === e), - R(() => t.unobserve(e)), - l(() => he(e)) - ) - ), - V(he(e)) - ) - } - function bt(e) { - return { width: e.scrollWidth, height: e.scrollHeight } - } - function ar(e) { - let t = e.parentElement - for (; t && e.scrollWidth <= t.scrollWidth && e.scrollHeight <= t.scrollHeight; ) t = (e = t).parentElement - return t ? e : void 0 - } - var to = new x(), - Ra = $(() => - k( - new IntersectionObserver( - e => { - for (let t of e) to.next(t) - }, - { threshold: 0 } - ) - ) - ).pipe( - g(e => L(ze, k(e)).pipe(R(() => e.disconnect()))), - X(1) - ) - function sr(e) { - return Ra.pipe( - S(t => t.observe(e)), - g(t => - to.pipe( - A(({ target: r }) => r === e), - R(() => t.unobserve(e)), - l(({ isIntersecting: r }) => r) - ) - ) - ) - } - function ro(e, t = 16) { - return dt(e).pipe( - l(({ y: r }) => { - let n = he(e), - o = bt(e) - return r >= o.height - n.height - t - }), - J() - ) - } - var cr = { drawer: z("[data-md-toggle=drawer]"), search: z("[data-md-toggle=search]") } - function no(e) { - return cr[e].checked - } - function Ke(e, t) { - cr[e].checked !== t && cr[e].click() - } - function Ue(e) { - let t = cr[e] - return b(t, "change").pipe( - l(() => t.checked), - V(t.checked) - ) - } - function ka(e, t) { - switch (e.constructor) { - case HTMLInputElement: - return e.type === "radio" ? /^Arrow/.test(t) : !0 - case HTMLSelectElement: - case HTMLTextAreaElement: - return !0 - default: - return e.isContentEditable - } - } - function Ha() { - return L(b(window, "compositionstart").pipe(l(() => !0)), b(window, "compositionend").pipe(l(() => !1))).pipe( - V(!1) - ) - } - function oo() { - let e = b(window, "keydown").pipe( - A(t => !(t.metaKey || t.ctrlKey)), - l(t => ({ - mode: no("search") ? "search" : "global", - type: t.key, - claim() { - t.preventDefault(), t.stopPropagation() - } - })), - A(({ mode: t, type: r }) => { - if (t === "global") { - let n = _e() - if (typeof n != "undefined") return !ka(n, r) - } - return !0 - }), - pe() - ) - return Ha().pipe(g(t => (t ? M : e))) - } - function le() { - return new URL(location.href) - } - function ot(e) { - location.href = e.href - } - function io() { - return new x() - } - function ao(e, t) { - if (typeof t == "string" || typeof t == "number") e.innerHTML += t.toString() - else if (t instanceof Node) e.appendChild(t) - else if (Array.isArray(t)) for (let r of t) ao(e, r) - } - function _(e, t, ...r) { - let n = document.createElement(e) - if (t) - for (let o of Object.keys(t)) - typeof t[o] != "undefined" && - (typeof t[o] != "boolean" ? n.setAttribute(o, t[o]) : n.setAttribute(o, "")) - for (let o of r) ao(n, o) - return n - } - function fr(e) { - if (e > 999) { - let t = +((e - 950) % 1e3 > 99) - return `${((e + 1e-6) / 1e3).toFixed(t)}k` - } else return e.toString() - } - function so() { - return location.hash.substring(1) - } - function Dr(e) { - let t = _("a", { href: e }) - t.addEventListener("click", r => r.stopPropagation()), t.click() - } - function Pa(e) { - return L(b(window, "hashchange"), e).pipe( - l(so), - V(so()), - A(t => t.length > 0), - X(1) - ) - } - function co(e) { - return Pa(e).pipe( - l(t => ce(`[id="${t}"]`)), - A(t => typeof t != "undefined") - ) - } - function Vr(e) { - let t = matchMedia(e) - return er(r => t.addListener(() => r(t.matches))).pipe(V(t.matches)) - } - function fo() { - let e = matchMedia("print") - return L(b(window, "beforeprint").pipe(l(() => !0)), b(window, "afterprint").pipe(l(() => !1))).pipe( - V(e.matches) - ) - } - function zr(e, t) { - return e.pipe(g(r => (r ? t() : M))) - } - function ur(e, t = { credentials: "same-origin" }) { - return ue(fetch(`${e}`, t)).pipe( - fe(() => M), - g(r => (r.status !== 200 ? Ot(() => new Error(r.statusText)) : k(r))) - ) - } - function We(e, t) { - return ur(e, t).pipe( - g(r => r.json()), - X(1) - ) - } - function uo(e, t) { - let r = new DOMParser() - return ur(e, t).pipe( - g(n => n.text()), - l(n => r.parseFromString(n, "text/xml")), - X(1) - ) - } - function pr(e) { - let t = _("script", { src: e }) - return $( - () => ( - document.head.appendChild(t), - L(b(t, "load"), b(t, "error").pipe(g(() => Ot(() => new ReferenceError(`Invalid script: ${e}`))))).pipe( - l(() => {}), - R(() => document.head.removeChild(t)), - ge(1) - ) - ) - ) - } - function po() { - return { x: Math.max(0, scrollX), y: Math.max(0, scrollY) } - } - function lo() { - return L(b(window, "scroll", { passive: !0 }), b(window, "resize", { passive: !0 })).pipe(l(po), V(po())) - } - function mo() { - return { width: innerWidth, height: innerHeight } - } - function ho() { - return b(window, "resize", { passive: !0 }).pipe(l(mo), V(mo())) - } - function bo() { - return G([lo(), ho()]).pipe( - l(([e, t]) => ({ offset: e, size: t })), - X(1) - ) - } - function lr(e, { viewport$: t, header$: r }) { - let n = t.pipe(ee("size")), - o = G([n, r]).pipe(l(() => Xe(e))) - return G([r, t, o]).pipe( - l(([{ height: i }, { offset: s, size: a }, { x: f, y: c }]) => ({ - offset: { x: s.x - f, y: s.y - c + i }, - size: a - })) - ) - } - ;(() => { - function e(n, o) { - parent.postMessage(n, o || "*") - } - function t(...n) { - return n.reduce( - (o, i) => - o.then( - () => - new Promise(s => { - let a = document.createElement("script") - ;(a.src = i), (a.onload = s), document.body.appendChild(a) - }) - ), - Promise.resolve() - ) - } - var r = class extends EventTarget { - constructor(n) { - super(), - (this.url = n), - (this.m = i => { - i.source === this.w && - (this.dispatchEvent(new MessageEvent("message", { data: i.data })), - this.onmessage && this.onmessage(i)) - }), - (this.e = (i, s, a, f, c) => { - if (s === `${this.url}`) { - let u = new ErrorEvent("error", { message: i, filename: s, lineno: a, colno: f, error: c }) - this.dispatchEvent(u), this.onerror && this.onerror(u) - } - }) - let o = document.createElement("iframe") - ;(o.hidden = !0), - document.body.appendChild((this.iframe = o)), - this.w.document.open(), - this.w.document.write( - ` + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + - - +

Cases

- - +

Cases Overview

+

Cases Model

- Cases - SOCFortress CoPilot - - - - - - - - - - - - +
- - - - - - - -
- -
- -
- -
- - -
-
-
-
-
- -
-
-
- -
-
-

Cases

- -

Cases Overview

-

Cases Model

- -
- -
-
-
-

- Case -

- -
-

- Bases: - db.Model -

- -

- Class for cases which stores the case ID, case name, and a list of - agents. This class inherits from SQLAlchemy's Model class. -

- -
- Source code in app\models\cases.py -
- - -
-
-
10
+
+  
+ + + +
+ + + + + + + + +
+ + + +

+ Case + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for cases which stores the case ID, case name, and a list of agents. +This class inherits from SQLAlchemy's Model class.

+ + +
+ Source code in app\models\cases.py +
- - -
10
 11
 12
 13
@@ -492,12 +711,7 @@ 

36 37 38 -39

-
-
-
-
class Case(db.Model):
+39
class Case(db.Model):
     """
     Class for cases which stores the case ID, case name, and a list of agents.
     This class inherits from SQLAlchemy's Model class.
@@ -527,44 +741,41 @@ 

:return: A string representation of the case ID. """ return f"<Case {self.case_id}>" -

-
-
-
-
- -
-
-

- __init__(case_id, - case_name, agents) -

- -
-

Initialize a new instance of the Case class.

-

- :param case_id: The ID of the case. :param case_name: - The name of the case. :param agents: A comma-separated - string of agents associated with the case. -

- -
- - Source code in app\models\cases.py - -
- - -
-
-
21
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(case_id, case_name, agents) + +

+ + +
+ +

Initialize a new instance of the Case class.

+

:param case_id: The ID of the case. +:param case_name: The name of the case. +:param agents: A comma-separated string of agents associated with the case.

+ +
+ Source code in app\models\cases.py +
- - -
21
 22
 23
 24
@@ -574,12 +785,7 @@ 

28 29 30 -31

-
-
-
-
def __init__(self, case_id: int, case_name: str, agents: str):
+31
def __init__(self, case_id: int, case_name: str, agents: str):
     """
     Initialize a new instance of the Case class.
 
@@ -590,96 +796,78 @@ 

self.case_id = case_id self.case_name = case_name self.agents = agents -

-
-
-
-
-
-
- -
-

- __repr__() -

- -
-

Returns a string representation of the Case instance.

-

:return: A string representation of the case ID.

- -
- - Source code in app\models\cases.py - -
- - -
-
-
33
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the Case instance.

+

:return: A string representation of the case ID.

+ +
+ Source code in app\models\cases.py +
- - -
33
 34
 35
 36
 37
 38
-39
-
-
-
-
def __repr__(self) -> str:
+39
def __repr__(self) -> str:
     """
     Returns a string representation of the Case instance.
 
     :return: A string representation of the case ID.
     """
     return f"<Case {self.case_id}>"
-
-
-
-
-
-
-
-
-
-
- -
-

- CaseSchema -

- -
-

- Bases: - ma.Schema -

- -

- Schema for serializing and deserializing instances of the Case - class. -

- -
- Source code in app\models\cases.py -
- - -
-
-
42
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ CaseSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the Case class.

+ + +
+ Source code in app\models\cases.py +
- - -
42
 43
 44
 45
@@ -694,12 +882,7 @@ 

54 55 56 -57

-
-
-
-
class CaseSchema(ma.Schema):
+57
class CaseSchema(ma.Schema):
     """
     Schema for serializing and deserializing instances of the Case class.
     """
@@ -715,39 +898,40 @@ 

"case_name", "agents", ) -

-
-
-
-
- -
-
-

- Meta -

- -
-

- Meta class defines the fields to be - serialized/deserialized. -

- -
- - Source code in app\models\cases.py - -
- - -
-
-
47
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\cases.py +
- - -
47
 48
 49
 50
@@ -757,12 +941,7 @@ 

54 55 56 -57

-
-
-
-
class Meta:
+57
class Meta:
     """
     Meta class defines the fields to be serialized/deserialized.
     """
@@ -773,104 +952,133 @@ 

"case_name", "agents", ) -

-
-
-
-
- -
-
-
-
-
-
-
-
-
-


-

Cases Routes

- -
- -
-
-
-

- create_case_note(case_id) -

- -
-

Endpoint to create a note for a specific case in DFIR IRIS.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
case_id - str - -
-

The ID of the case to create a note for.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the result of the - note creation operation. -

-
-
- -
- - Source code in app\routes\dfir_iris.py - -
- - -
-
-
57
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + +
+ +
+ +


+

Cases Routes

+ + +
+ + + +
+ + + +
+ + + + + + + + + +
+ + + +

+create_case_note(case_id) + +

+ + +
+ +

Endpoint to create a note for a specific case in DFIR IRIS.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
case_id + str + +
+

The ID of the case to create a note for.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the result of the note creation operation.

+
+
+ +
+ Source code in app\routes\dfir_iris.py +
- - -
57
 58
 59
 60
@@ -889,12 +1097,7 @@ 

73 74 75 -76

-
-
-
-
@bp.route("/dfir_iris/cases/<case_id>/note", methods=["POST"])
+76
@bp.route("/dfir_iris/cases/<case_id>/note", methods=["POST"])
 def create_case_note(case_id: str):
     """
     Endpoint to create a note for a specific case in DFIR IRIS.
@@ -914,60 +1117,50 @@ 

note_content=note_content, ) return created_note -

-
-
-
-
-
-
- -
-

- get_alerts() -

- -
-

Endpoint to retrieve all alerts from DFIR IRIS.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of alerts. -

-
-
- -
- - Source code in app\routes\dfir_iris.py - -
- - -
-
-
 95
+
+
+
+ +
+ +
+ + + +

+get_alerts() + +

+ + +
+ +

Endpoint to retrieve all alerts from DFIR IRIS.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of alerts.

+
+
+ +
+ Source code in app\routes\dfir_iris.py +
- - -
 95
  96
  97
  98
@@ -977,12 +1170,7 @@ 

102 103 104 -105

-
-
-
-
@bp.route("/dfir_iris/alerts", methods=["GET"])
+105
@bp.route("/dfir_iris/alerts", methods=["GET"])
 def get_alerts():
     """
     Endpoint to retrieve all alerts from DFIR IRIS.
@@ -993,87 +1181,78 @@ 

service = AlertsService() alerts = service.list_alerts() return alerts -

-
-
-
-
-
-
- -
-

- get_case(case_id) -

- -
-

Endpoint to retrieve a specific case from DFIR IRIS.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
case_id - str - -
-

The ID of the case to retrieve.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

A JSON response containing the case data.

-
-
- -
- - Source code in app\routes\dfir_iris.py - -
- - -
-
-
25
+
+
+
+ +
+ +
+ + + +

+get_case(case_id) + +

+ + +
+ +

Endpoint to retrieve a specific case from DFIR IRIS.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
case_id + str + +
+

The ID of the case to retrieve.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the case data.

+
+
+ +
+ Source code in app\routes\dfir_iris.py +
- - -
25
 26
 27
 28
@@ -1086,12 +1265,7 @@ 

35 36 37 -38

-
-
-
-
@bp.route("/dfir_iris/cases/<case_id>", methods=["GET"])
+38
@bp.route("/dfir_iris/cases/<case_id>", methods=["GET"])
 def get_case(case_id: str):
     """
     Endpoint to retrieve a specific case from DFIR IRIS.
@@ -1105,90 +1279,78 @@ 

service = CasesService() case = service.get_case(case_id=case_id) return case -

-
-
-
-
-
-
- -
-

- get_case_assets(case_id) -

- -
-

Endpoint to retrieve assets of a specific case from DFIR IRIS.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
case_id - str - -
-

The ID of the case to retrieve assets from.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of assets - for the case. -

-
-
- -
- - Source code in app\routes\dfir_iris.py - -
- - -
-
-
79
+
+
+
+ +
+ +
+ + + +

+get_case_assets(case_id) + +

+ + +
+ +

Endpoint to retrieve assets of a specific case from DFIR IRIS.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
case_id + str + +
+

The ID of the case to retrieve assets from.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of assets for the case.

+
+
+ +
+ Source code in app\routes\dfir_iris.py +
- - -
79
 80
 81
 82
@@ -1201,12 +1363,7 @@ 

89 90 91 -92

-
-
-
-
@bp.route("/dfir_iris/cases/<case_id>/assets", methods=["GET"])
+92
@bp.route("/dfir_iris/cases/<case_id>/assets", methods=["GET"])
 def get_case_assets(case_id: str):
     """
     Endpoint to retrieve assets of a specific case from DFIR IRIS.
@@ -1220,90 +1377,78 @@ 

asset_service = AssetsService() assets = asset_service.get_case_assets(cid=case_id) return assets -

-
-
-
-
-
-
- -
-

- get_case_notes(case_id) -

- -
-

Endpoint to retrieve notes of a specific case from DFIR IRIS.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
case_id - str - -
-

The ID of the case to retrieve notes from.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of notes for - the case. -

-
-
- -
- - Source code in app\routes\dfir_iris.py - -
- - -
-
-
41
+
+
+
+ +
+ +
+ + + +

+get_case_notes(case_id) + +

+ + +
+ +

Endpoint to retrieve notes of a specific case from DFIR IRIS.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
case_id + str + +
+

The ID of the case to retrieve notes from.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of notes for the case.

+
+
+ +
+ Source code in app\routes\dfir_iris.py +
- - -
41
 42
 43
 44
@@ -1316,12 +1461,7 @@ 

51 52 53 -54

-
-
-
-
@bp.route("/dfir_iris/cases/<case_id>/notes", methods=["GET"])
+54
@bp.route("/dfir_iris/cases/<case_id>/notes", methods=["GET"])
 def get_case_notes(case_id: int):
     """
     Endpoint to retrieve notes of a specific case from DFIR IRIS.
@@ -1335,58 +1475,50 @@ 

notes_service = NotesService() notes = notes_service.get_case_notes(search_term="%", cid=case_id) return notes -

-
-
-
-
-
-
- -
-

- get_cases() -

- -
-

Endpoint to retrieve all the cases from DFIR IRIS.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

A JSON response containing the list of cases.

-
-
- -
- - Source code in app\routes\dfir_iris.py - -
- - -
-
-
12
+
+
+
+ +
+ +
+ + + +

+get_cases() + +

+ + +
+ +

Endpoint to retrieve all the cases from DFIR IRIS.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of cases.

+
+
+ +
+ Source code in app\routes\dfir_iris.py +
- - -
12
 13
 14
 15
@@ -1396,12 +1528,7 @@ 

19 20 21 -22

-
-
-
-
@bp.route("/dfir_iris/cases", methods=["GET"])
+22
@bp.route("/dfir_iris/cases", methods=["GET"])
 def get_cases():
     """
     Endpoint to retrieve all the cases from DFIR IRIS.
@@ -1412,46 +1539,59 @@ 

service = CasesService() cases = service.list_cases() return cases -

-
-
-
-
-
-
-
-
-
-


-

Cases Services

- -
- -
-
-
-

- CasesService -

- -
-

- A service class that encapsulates the logic for pulling cases from - DFIR-IRIS. -

- -
- - Source code in app\services\DFIR_IRIS\cases.py - -
- - -
-
-
 14
+
+
+
+ +
+ + + +
+ +
+ +


+

Cases Services

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ CasesService + + +

+ + +
+ + +

A service class that encapsulates the logic for pulling cases from DFIR-IRIS.

+ + +
+ Source code in app\services\DFIR_IRIS\cases.py +
- - -
 14
  15
  16
  17
@@ -1538,12 +1678,7 @@ 

98 99 100 -101

-
-
-
-
class CasesService:
+101
class CasesService:
     """
     A service class that encapsulates the logic for pulling cases from DFIR-IRIS.
     """
@@ -1631,81 +1766,67 @@ 

dict: A dictionary containing the success status, a message and potentially the case. """ return self.get_case(case_id) -

-
-
-
-
- -
-
-

- check_case_id(case_id) -

- -
-

Checks if a case exists in DFIR-IRIS

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - bool - -
-

- A dictionary containing the success - status, a message and potentially - the case. -

-
-
- -
- - Source code in - app\services\DFIR_IRIS\cases.py - -
- - -
-
-
 94
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+check_case_id(case_id) + +

+ + +
+ +

Checks if a case exists in DFIR-IRIS

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + bool + +
+

A dictionary containing the success status, a message and potentially the case.

+
+
+ +
+ Source code in app\services\DFIR_IRIS\cases.py +
- - -
- -
-
def check_case_id(self, case_id: int) -> bool:
+101
def check_case_id(self, case_id: int) -> bool:
     """
     Checks if a case exists in DFIR-IRIS
 
@@ -1713,72 +1834,51 @@ 

dict: A dictionary containing the success status, a message and potentially the case. """ return self.get_case(case_id) -

-
-
-
-
-
-
- -
-

- get_case(case_id) -

- -
-

- Gets a case from DFIR-IRIS and returns all the details -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - bool - -
-

- A dictionary containing the success - status, a message and potentially - the case. -

-
-
- -
- - Source code in - app\services\DFIR_IRIS\cases.py - -
- - -
-
-
61
+
+
+
+ +
+ +
+ + + +

+get_case(case_id) + +

+ + +
+ +

Gets a case from DFIR-IRIS and returns all the details

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + bool + +
+

A dictionary containing the success status, a message and potentially the case.

+
+
+ +
+ Source code in app\services\DFIR_IRIS\cases.py +
- - -
61
 62
 63
 64
@@ -1809,12 +1909,7 @@ 

89 90 91 -92

-
-
-
-
def get_case(self, case_id: int) -> bool:
+92
def get_case(self, case_id: int) -> bool:
     """
     Gets a case from DFIR-IRIS and returns all the details
 
@@ -1846,72 +1941,51 @@ 

"message": f"Successfully collected case {case_id} from DFIR-IRIS", "case": result["data"], } -

-
-
-
-
-
-
- -
-

- list_cases() -

- -
-

Lists all cases from DFIR-IRIS

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, object] - -
-

- A dictionary containing the success - status, a message and potentially - the cases. -

-
-
- -
- - Source code in - app\services\DFIR_IRIS\cases.py - -
- - -
-
-
29
+
+
+
+ +
+ +
+ + + +

+list_cases() + +

+ + +
+ +

Lists all cases from DFIR-IRIS

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, object] + +
+

A dictionary containing the success status, a message and potentially the cases.

+
+
+ +
+ Source code in app\services\DFIR_IRIS\cases.py +
- - -
29
 30
 31
 32
@@ -1941,12 +2015,7 @@ 

56 57 58 -59

-
-
-
-
def list_cases(self) -> Dict[str, object]:
+59
def list_cases(self) -> Dict[str, object]:
     """
     Lists all cases from DFIR-IRIS
 
@@ -1977,103 +2046,82 @@ 

"message": "Successfully collected cases from DFIR-IRIS", "cases": result["data"], } -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
+
+
+
- -
- - -
-
-
-
+
+ + + +
+ +
- - - - - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/connectors/index.html b/backend/site/connectors/index.html index 0d183026..8bb549c2 100644 --- a/backend/site/connectors/index.html +++ b/backend/site/connectors/index.html @@ -1,707 +1,882 @@ + - - - + + + + + + + + + + + + + + + + + + + Connectors - SOCFortress CoPilot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + - - +

Connectors

- - +

Connectors Overview

+

Connectors Model

- Connectors - SOCFortress CoPilot - - - - - - - - - - - - +
- - - - - - - -
- -
- -
- -
- - -
-
-
-
-
- -
-
-
- -
-
-

Connectors

- -

Connectors Overview

-

Connectors Model

- -
- -
-
-
-

- Connector - - - dataclass - -

- -
-

- Bases: ABC -

- -

- This abstract base class defines the interface for a connector. A - connector is an object that connects to a specific service or system - and performs actions on it. The specific service or system a - connector connects to is defined by the connector's attributes. -

-

- :param attributes: A dictionary of attributes necessary for the - connector to connect to the service or system. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
36
+
+  
+ + + +
+ + + + + + + + +
+ + + +

+ Connector + + + + dataclass + + +

+ + +
+

+ Bases: ABC

+ + +

This abstract base class defines the interface for a connector. A connector is an object that +connects to a specific service or system and performs actions on it. The specific service or +system a connector connects to is defined by the connector's attributes.

+

:param attributes: A dictionary of attributes necessary for the connector to connect to the service or system.

+ + +
+ Source code in app\models\connectors.py +
- - -
36
 37
 38
 39
@@ -747,12 +922,7 @@ 

79 80 81 -82

-
-
-
-
@dataclass
+82
@dataclass
 class Connector(ABC):
     """
     This abstract base class defines the interface for a connector. A connector is an object that
@@ -799,87 +969,67 @@ 

return attributes else: raise NoResultFound -

-
-
-
-
- -
-
-

- get_connector_info_from_db(connector_name) - - - staticmethod - -

- -
-

- This method retrieves connector information from the - database. -

-

- :param connector_name: A string that specifies the name - of the connector whose information is to be retrieved. - :return: A dictionary of the connector's attributes if - the connector exists. Otherwise, it raises a - NoResultFound exception. -

- -

Raises:

- - - - - - - - - - - - - -
TypeDescription
- NoResultFound - -
-

- If the connector_name is not found - in the database. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
58
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+get_connector_info_from_db(connector_name) + + + staticmethod + + +

+ + +
+ +

This method retrieves connector information from the database.

+

:param connector_name: A string that specifies the name of the connector whose information is to be retrieved. +:return: A dictionary of the connector's attributes if the connector exists. Otherwise, it raises a +NoResultFound exception.

+ +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ NoResultFound + +
+

If the connector_name is not found in the database.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
58
 59
 60
 61
@@ -903,12 +1053,7 @@ 

79 80 81 -82

-
-
-
-
@staticmethod
+82
@staticmethod
 def get_connector_info_from_db(connector_name: str) -> Dict[str, Any]:
     """
     This method retrieves connector information from the database.
@@ -933,54 +1078,35 @@ 

return attributes else: raise NoResultFound -

-
-
-
-
-
-
- -
-

- verify_connection() - - - abstractmethod - -

- -
-

- This abstract method should be implemented by all - subclasses of Connector. It is meant to verify the - connection to the service or system the connector is - designed to connect to. -

-

- :return: Depends on the implementation in the subclass. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
48
+
+
+
+ +
+ +
+ + + +

+verify_connection() + + + abstractmethod + + +

+ + +
+ +

This abstract method should be implemented by all subclasses of Connector. It is meant to verify the +connection to the service or system the connector is designed to connect to.

+

:return: Depends on the implementation in the subclass.

+ +
+ Source code in app\models\connectors.py +
- - -
48
 49
 50
 51
@@ -988,12 +1114,7 @@ 

53 54 55 -56

-
-
-
-
@abstractmethod
+56
@abstractmethod
 def verify_connection(self) -> Dict[str, Any]:
     """
     This abstract method should be implemented by all subclasses of Connector. It is meant to verify the
@@ -1002,41 +1123,41 @@ 

:return: Depends on the implementation in the subclass. """ pass -

-
-
-
-
-
-
-
-
-
- -
-

- ConnectorFactory -

- -
-

This class represents a factory for creating connector instances.

-

- :param creators: A dictionary mapping connector keys to their - corresponding creator names. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
435
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ ConnectorFactory + + +

+ + +
+ + +

This class represents a factory for creating connector instances.

+

:param creators: A dictionary mapping connector keys to their corresponding creator names.

+ + +
+ Source code in app\models\connectors.py +
- - -
435
 436
 437
 438
@@ -1074,12 +1195,7 @@ 

470 471 472 -473

-
-
-
-
class ConnectorFactory:
+473
class ConnectorFactory:
     """
     This class represents a factory for creating connector instances.
 
@@ -1118,97 +1234,73 @@ 

# use dynamic_import to get the class and initialize it connector_class = dynamic_import("app.models.connectors", creator) return connector_class(connector_name) -

-
-
-
-
- -
-
-

- __init__() -

- -
-

Initialize a new instance of the ConnectorFactory.

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
442
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__() + +

+ + +
+ +

Initialize a new instance of the ConnectorFactory.

+ +
+ Source code in app\models\connectors.py +
- - -
- -
-
def __init__(self):
+446
def __init__(self):
     """
     Initialize a new instance of the ConnectorFactory.
     """
     self._creators = {}
-
-
-
-
-
-
-
- -
-

- create(key, - connector_name) -

- -
-

Create a new connector instance.

-

- :param key: The key of the connector. :param - connector_name: The name of the connector. -

-

:return: A new instance of the connector.

-

- :raises ValueError: If the key is not found in the list - of creators. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
457
+
+
+
+ +
+ +
+ + + +

+create(key, connector_name) + +

+ + +
+ +

Create a new connector instance.

+

:param key: The key of the connector. +:param connector_name: The name of the connector.

+

:return: A new instance of the connector.

+

:raises ValueError: If the key is not found in the list of creators.

+ +
+ Source code in app\models\connectors.py +
- - -
457
 458
 459
 460
@@ -1224,12 +1316,7 @@ 

470 471 472 -473

-
-
-
-
def create(self, key, connector_name):
+473
def create(self, key, connector_name):
     """
     Create a new connector instance.
 
@@ -1246,58 +1333,38 @@ 

# use dynamic_import to get the class and initialize it connector_class = dynamic_import("app.models.connectors", creator) return connector_class(connector_name) -

-
-
-
-
-
-
- -
-

- register_creator(key, creator) -

- -
-

Register a new connector creator.

-

- :param key: The key of the connector. :param creator: - The creator of the connector. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
448
+
+
+
+ +
+ +
+ + + +

+register_creator(key, creator) + +

+ + +
+ +

Register a new connector creator.

+

:param key: The key of the connector. +:param creator: The creator of the connector.

+ +
+ Source code in app\models\connectors.py +
- - -
- -
-
def register_creator(self, key, creator):
+455
def register_creator(self, key, creator):
     """
     Register a new connector creator.
 
@@ -1305,56 +1372,43 @@ 

:param creator: The creator of the connector. """ self._creators[key] = creator -

-
-
-
-
-
-
-
-
-
- -
-

- DfirIrisConnector -

- -
-

- Bases: - Connector -

- -

- This class represents a connector for the DFIR IRIS service. It is a - subclass of Connector. -

-

- :param connector_name: A string that specifies the name of the - connector. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
274
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ DfirIrisConnector + + +

+ + +
+

+ Bases: Connector

+ + +

This class represents a connector for the DFIR IRIS service. It is a subclass of Connector.

+

:param connector_name: A string that specifies the name of the connector.

+ + +
+ Source code in app\models\connectors.py +
- - -
274
 275
 276
 277
@@ -1398,12 +1452,7 @@ 

315 316 317 -318

-
-
-
-
class DfirIrisConnector(Connector):
+318
class DfirIrisConnector(Connector):
     """
     This class represents a connector for the DFIR IRIS service. It is a subclass of Connector.
 
@@ -1448,73 +1497,60 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "response": None} -

-
-
-
-
- -
-
-

- verify_connection() -

- -
-

Verifies the connection to DFIR IRIS service.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Any] - -
-

- A dictionary containing - 'connectionSuccessful' status and - 'response' if the connection is - successful. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
284
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+verify_connection() + +

+ + +
+ +

Verifies the connection to DFIR IRIS service.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Any] + +
+

A dictionary containing 'connectionSuccessful' status and 'response' if the connection is successful.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
284
 285
 286
 287
@@ -1548,12 +1584,7 @@ 

315 316 317 -318

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+318
def verify_connection(self) -> Dict[str, Any]:
     """
     Verifies the connection to DFIR IRIS service.
 
@@ -1588,56 +1619,43 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "response": None} -

-
-
-
-
-
-
-
-
-
- -
-

- GraylogConnector -

- -
-

- Bases: - Connector -

- -

- This class represents a connector for the Graylog service. It is a - subclass of Connector. -

-

- :param connector_name: A string that specifies the name of the - connector. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
127
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ GraylogConnector + + +

+ + +
+

+ Bases: Connector

+ + +

This class represents a connector for the Graylog service. It is a subclass of Connector.

+

:param connector_name: A string that specifies the name of the connector.

+ + +
+ Source code in app\models\connectors.py +
- - -
127
 128
 129
 130
@@ -1680,12 +1698,7 @@ 

167 168 169 -170

-
-
-
-
class GraylogConnector(Connector):
+170
class GraylogConnector(Connector):
     """
     This class represents a connector for the Graylog service. It is a subclass of Connector.
 
@@ -1729,73 +1742,60 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "roles": None} -

-
-
-
-
- -
-
-

- verify_connection() -

- -
-

Verifies the connection to Graylog service.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Any] - -
-

- A dictionary containing - 'connectionSuccessful' status and - 'roles' if the connection is - successful. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
137
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+verify_connection() + +

+ + +
+ +

Verifies the connection to Graylog service.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Any] + +
+

A dictionary containing 'connectionSuccessful' status and 'roles' if the connection is successful.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
137
 138
 139
 140
@@ -1828,12 +1828,7 @@ 

167 168 169 -170

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+170
def verify_connection(self) -> Dict[str, Any]:
     """
     Verifies the connection to Graylog service.
 
@@ -1867,77 +1862,70 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "roles": None} -

-
-
-
-
-
-
-
-
-
- -
-

- RabbitMQConnector -

- -
-

- Bases: - Connector -

- -

A connector for the RabbitMQ service, a subclass of Connector.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_name - str - -
-

The name of the connector.

-
-
- required -
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
386
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ RabbitMQConnector + + +

+ + +
+

+ Bases: Connector

+ + +

A connector for the RabbitMQ service, a subclass of Connector.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_name + str + +
+

The name of the connector.

+
+
+ required +
+ + +
+ Source code in app\models\connectors.py +
- - -
386
 387
 388
 389
@@ -1983,12 +1971,7 @@ 

429 430 431 -432

-
-
-
-
class RabbitMQConnector(Connector):
+432
class RabbitMQConnector(Connector):
     """
     A connector for the RabbitMQ service, a subclass of Connector.
 
@@ -2035,39 +2018,38 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "response": None} -

-
-
-
-
- -
-
-

- verify_connection() -

- -
-

Verifies the connection to RabbitMQ service.

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
397
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+verify_connection() + +

+ + +
+ +

Verifies the connection to RabbitMQ service.

+ +
+ Source code in app\models\connectors.py +
- - -
397
 398
 399
 400
@@ -2102,12 +2084,7 @@ 

429 430 431 -432

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+432
def verify_connection(self) -> Dict[str, Any]:
     """
     Verifies the connection to RabbitMQ service.
     """
@@ -2143,56 +2120,43 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "response": None} -

-
-
-
-
-
-
-
-
-
- -
-

- ShuffleConnector -

- -
-

- Bases: - Connector -

- -

- This class represents a connector for the Shuffle service. It is a - subclass of Connector. -

-

- :param connector_name: A string that specifies the name of the - connector. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
228
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ ShuffleConnector + + +

+ + +
+

+ Bases: Connector

+ + +

This class represents a connector for the Shuffle service. It is a subclass of Connector.

+

:param connector_name: A string that specifies the name of the connector.

+ + +
+ Source code in app\models\connectors.py +
- - -
228
 229
 230
 231
@@ -2235,12 +2199,7 @@ 

268 269 270 -271

-
-
-
-
class ShuffleConnector(Connector):
+271
class ShuffleConnector(Connector):
     """
     This class represents a connector for the Shuffle service. It is a subclass of Connector.
 
@@ -2284,73 +2243,60 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False} -

-
-
-
-
- -
-
-

- verify_connection() -

- -
-

Verifies the connection to Shuffle service.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Any] - -
-

- A dictionary containing - 'connectionSuccessful' status and - 'apps' if the connection is - successful. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
238
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+verify_connection() + +

+ + +
+ +

Verifies the connection to Shuffle service.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Any] + +
+

A dictionary containing 'connectionSuccessful' status and 'apps' if the connection is successful.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
238
 239
 240
 241
@@ -2383,12 +2329,7 @@ 

268 269 270 -271

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+271
def verify_connection(self) -> Dict[str, Any]:
     """
     Verifies the connection to Shuffle service.
 
@@ -2422,82 +2363,70 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False} -

-
-
-
-
-
-
-
-
-
- -
-

- VelociraptorConnector -

- -
-

- Bases: - Connector -

- -

- A connector for the Velociraptor service, a subclass of Connector. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_name - str - -
-

The name of the connector.

-
-
- required -
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
321
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ VelociraptorConnector + + +

+ + +
+

+ Bases: Connector

+ + +

A connector for the Velociraptor service, a subclass of Connector.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_name + str + +
+

The name of the connector.

+
+
+ required +
+ + +
+ Source code in app\models\connectors.py +
- - -
321
 322
 323
 324
@@ -2559,12 +2488,7 @@ 

380 381 382 -383

-
-
-
-
class VelociraptorConnector(Connector):
+383
class VelociraptorConnector(Connector):
     """
     A connector for the Velociraptor service, a subclass of Connector.
 
@@ -2627,73 +2551,60 @@ 

except Exception as e: logger.error(f"Failed to get connector_api_key from the database: {e}") return {"connectionSuccessful": False, "response": None} -

-
-
-
-
- -
-
-

- verify_connection() -

- -
-

Verifies the connection to Velociraptor service.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Any] - -
-

- A dictionary containing - 'connectionSuccessful' status and - 'response' if the connection is - successful. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
332
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+verify_connection() + +

+ + +
+ +

Verifies the connection to Velociraptor service.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Any] + +
+

A dictionary containing 'connectionSuccessful' status and 'response' if the connection is successful.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
332
 333
 334
 335
@@ -2744,12 +2655,7 @@ 

380 381 382 -383

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+383
def verify_connection(self) -> Dict[str, Any]:
     """
     Verifies the connection to Velociraptor service.
 
@@ -2801,59 +2707,43 @@ 

except Exception as e: logger.error(f"Failed to get connector_api_key from the database: {e}") return {"connectionSuccessful": False, "response": None} -

-
-
-
-
-
-
-
-
-
- -
-

- WazuhIndexerConnector -

- -
-

- Bases: - Connector -

- -

- This class represents a connector for the Wazuh indexer service. It - is a subclass of Connector. -

-

- :param connector_name: A string that specifies the name of the - connector. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
 85
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ WazuhIndexerConnector + + +

+ + +
+

+ Bases: Connector

+ + +

This class represents a connector for the Wazuh indexer service. It is a subclass of Connector.

+

:param connector_name: A string that specifies the name of the connector.

+ + +
+ Source code in app\models\connectors.py +
- - -
 85
  86
  87
  88
@@ -2892,12 +2782,7 @@ 

121 122 123 -124

-
-
-
-
class WazuhIndexerConnector(Connector):
+124
class WazuhIndexerConnector(Connector):
     """
     This class represents a connector for the Wazuh indexer service. It is a subclass of Connector.
 
@@ -2937,47 +2822,40 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "clusterHealth": None} -

-
-
-
-
- -
-
-

- verify_connection() -

- -
-

- This method verifies the connection to the Wazuh indexer - service. -

-

- :return: A dictionary containing the status of the - connection attempt and information about the cluster's - health. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
 95
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+verify_connection() + +

+ + +
+ +

This method verifies the connection to the Wazuh indexer service.

+

:return: A dictionary containing the status of the connection attempt and information about +the cluster's health.

+ +
+ Source code in app\models\connectors.py +
- - -
 95
  96
  97
  98
@@ -3006,12 +2884,7 @@ 

121 122 123 -124

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+124
def verify_connection(self) -> Dict[str, Any]:
     """
     This method verifies the connection to the Wazuh indexer service.
 
@@ -3041,59 +2914,43 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "clusterHealth": None} -

-
-
-
-
-
-
-
-
-
- -
-

- WazuhManagerConnector -

- -
-

- Bases: - Connector -

- -

- This class represents a connector for the Wazuh manager service. It - is a subclass of Connector. -

-

- :param connector_name: A string that specifies the name of the - connector. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
173
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ WazuhManagerConnector + + +

+ + +
+

+ Bases: Connector

+ + +

This class represents a connector for the Wazuh manager service. It is a subclass of Connector.

+

:param connector_name: A string that specifies the name of the connector.

+ + +
+ Source code in app\models\connectors.py +
- - -
173
 174
 175
 176
@@ -3145,12 +3002,7 @@ 

222 223 224 -225

-
-
-
-
class WazuhManagerConnector(Connector):
+225
class WazuhManagerConnector(Connector):
     """
     This class represents a connector for the Wazuh manager service. It is a subclass of Connector.
 
@@ -3203,81 +3055,67 @@ 

str: Authentication token for the Wazuh manager service. """ return self.verify_connection()["authToken"] -

-
-
-
-
- -
-
-

- get_auth_token() -

- -
-

- Returns the authentication token for the Wazuh manager - service. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
str - str - -
-

- Authentication token for the Wazuh - manager service. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
218
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+get_auth_token() + +

+ + +
+ +

Returns the authentication token for the Wazuh manager service.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
str + str + +
+

Authentication token for the Wazuh manager service.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
- -
-
def get_auth_token(self) -> str:
+225
def get_auth_token(self) -> str:
     """
     Returns the authentication token for the Wazuh manager service.
 
@@ -3285,74 +3123,51 @@ 

str: Authentication token for the Wazuh manager service. """ return self.verify_connection()["authToken"] -

-
-
-
-
-
-
- -
-

- verify_connection() -

- -
-

Verifies the connection to Wazuh manager service.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Any] - -
-

- A dictionary containing - 'connectionSuccessful' status and - 'authToken' if the connection is - successful. -

-
-
- -
- - Source code in app\models\connectors.py - -
- - -
-
-
183
+
+
+
+ +
+ +
+ + + +

+verify_connection() + +

+ + +
+ +

Verifies the connection to Wazuh manager service.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Any] + +
+

A dictionary containing 'connectionSuccessful' status and 'authToken' if the connection is successful.

+
+
+ +
+ Source code in app\models\connectors.py +
- - -
183
 184
 185
 186
@@ -3385,12 +3200,7 @@ 

213 214 215 -216

-
-
-
-
def verify_connection(self) -> Dict[str, Any]:
+216
def verify_connection(self) -> Dict[str, Any]:
     """
     Verifies the connection to Wazuh manager service.
 
@@ -3424,50 +3234,41 @@ 

f"Connection to {self.attributes['connector_url']} failed with error: {e}", ) return {"connectionSuccessful": False, "authToken": None} -

-
-
-
-
-
-
-
-
-
- -
-

- dynamic_import(module_name, - class_name) -

- -
-

- This function dynamically imports a module and returns a specific - class from it. -

-

- :param module_name: A string that specifies the name of the module - to import. :param class_name: A string that specifies the name of - the class to get from the module. :return: The class specified by - class_name from the module specified by module_name. -

- -
- - Source code in app\models\connectors.py - -
- - -
-
-
23
+
+
+
+ +
+ + + +
+ +
+ +
+ + +
+ + + +

+dynamic_import(module_name, class_name) + +

+ + +
+ +

This function dynamically imports a module and returns a specific class from it.

+

:param module_name: A string that specifies the name of the module to import. +:param class_name: A string that specifies the name of the class to get from the module. +:return: The class specified by class_name from the module specified by module_name.

+ +
+ Source code in app\models\connectors.py +
- - -
23
 24
 25
 26
@@ -3477,12 +3278,7 @@ 

30 31 32 -33

-
-
-
-
def dynamic_import(module_name: str, class_name: str) -> Any:
+33
def dynamic_import(module_name: str, class_name: str) -> Any:
     """
     This function dynamically imports a module and returns a specific class from it.
 
@@ -3493,102 +3289,106 @@ 

module = importlib.import_module(module_name) class_ = getattr(module, class_name) return class_ -

-
-
-
-
-
-
-
-
-
-


-

Connectors Routes

- -
- -
-
-
-

- get_connector_details(id) -

- -
-

Endpoint to retrieve the details of a connector.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
id - str - -
-

The ID of the connector to retrieve.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the details of the - connector. -

-
-
- -
- - Source code in app\routes\connectors.py - -
- - -
-
-
36
+
+
+
+ +
+ + + +
+ +
+ +


+

Connectors Routes

+ + +
+ + + +
+ + + +
+ + + + + + + + + +
+ + + +

+get_connector_details(id) + +

+ + +
+ +

Endpoint to retrieve the details of a connector.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
id + str + +
+

The ID of the connector to retrieve.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the details of the connector.

+
+
+ +
+ Source code in app\routes\connectors.py +
- - -
36
 37
 38
 39
@@ -3607,12 +3407,7 @@ 

Connectors Routes52 53 54 -55

-
-
-
-
@bp.route("/connectors/<id>", methods=["GET"])
+55
@bp.route("/connectors/<id>", methods=["GET"])
 def get_connector_details(id: str):
     """
     Endpoint to retrieve the details of a connector.
@@ -3632,66 +3427,50 @@ 

Connectors Routes return jsonify(instantiated_connector) else: return jsonify(connector), 404 -

-
-
-
-
-
-
- -
-

- list_connectors_available() -

- -
-

Endpoint to retrieve all available connectors.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
json -
-

- A JSON response containing the list of all - available connectors along with their connection - verification status. -

-
-
- -
- - Source code in app\routes\connectors.py - -
- - -
-
-
15
+
+
+
+ +
+ +
+ + + +

+list_connectors_available() + +

+ + +
+ +

Endpoint to retrieve all available connectors.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + +
+

A JSON response containing the list of all available connectors along with their connection verification status.

+
+
+ +
+ Source code in app\routes\connectors.py +
- - -
15
 16
 17
 18
@@ -3709,12 +3488,7 @@ 

Connectors Routes30 31 32 -33

-
-
-
-
@bp.route("/connectors", methods=["GET"])
+33
@bp.route("/connectors", methods=["GET"])
 def list_connectors_available():
     """
     Endpoint to retrieve all available connectors.
@@ -3733,108 +3507,87 @@ 

Connectors Routes ] return jsonify(instantiated_connectors) -

-
-
-
-
-
-
- -
-

- update_connector_route(id) -

- -
-

Endpoint to update the details of a connector.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
id - str - -
-

The ID of the connector to update.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
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. -

-
-
- -
- - Source code in app\routes\connectors.py - -
- - -
-
-
58
+
+
+
+ +
+ +
+ + + +

+update_connector_route(id) + +

+ + +
+ +

Endpoint to update the details of a connector.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
id + str + +
+

The ID of the connector to update.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
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.

+
+
+ +
+ Source code in app\routes\connectors.py +
- - -
58
 59
 60
 61
@@ -3868,12 +3621,7 @@ 

Connectors Routes89 90 91 -92

-
-
-
-
@bp.route("/connectors/<id>", methods=["PUT"])
+92
@bp.route("/connectors/<id>", methods=["PUT"])
 def update_connector_route(id: str):
     """
     Endpoint to update the details of a connector.
@@ -3908,51 +3656,60 @@ 

Connectors Routes return jsonify(data_validated), 400 else: return jsonify(connector), 404 -

-
-
-
-
-
-
-
-
-
-


-

Connectors Services

- -
- -
-
-
-

- ConnectorService -

- -
-

- Service class for Connector operations. This class contains methods - for updating and processing connectors, validating connector - existence and data, and verifying connector connections. -

- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
 14
+
+
+
+ +
+ + + +
+ +
+ +


+

Connectors Services

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ ConnectorService + + +

+ + +
+ + +

Service class for Connector operations. This class contains methods for updating and processing connectors, +validating connector existence and data, and verifying connector connections.

+ + +
+ Source code in app\services\connectors\connectors.py +
- - -
 14
  15
  16
  17
@@ -4185,12 +3942,7 @@ 

Connectors Services 244 245 246 -247

-
-
-
-
class ConnectorService:
+247
class ConnectorService:
     """
     Service class for Connector operations. This class contains methods for updating and processing connectors,
     validating connector existence and data, and verifying connector connections.
@@ -4424,84 +4176,72 @@ 

Connectors Services "message": "Request data is invalid. Ensure connector_url and connector_api_key are present", "success": False, } -

-
-
-
-
- -
-
-

- __init__(db) -

- -
-

- Initializes the ConnectorService with a database - session. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
db -
-

- The database session to be used for - connector operations. -

-
-
- required -
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
20
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(db) + +

+ + +
+ +

Initializes the ConnectorService with a database session.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
db + +
+

The database session to be used for connector operations.

+
+
+ required +
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
20
 21
 22
 23
 24
 25
 26
-27
-
-
-
-
def __init__(self, db):
+27
def __init__(self, db):
     """
     Initializes the ConnectorService with a database session.
 
@@ -4509,103 +4249,78 @@ 

Connectors Services db: The database session to be used for connector operations. """ self.db = db -

-
-
-
-
-
-
- -
-

- process_connector(connector_name) -

- -
-

- Creates a connector instance, verifies the connection, - and returns the connector details. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_name - str - -
-

- The name of the connector to be - processed. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the name of - the connector and the status of the - connection verification. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
68
+
+
+
+ +
+ +
+ + + +

+process_connector(connector_name) + +

+ + +
+ +

Creates a connector instance, verifies the connection, and returns the connector details.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_name + str + +
+

The name of the connector to be processed.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the name of the connector and the status of the connection verification.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
68
 69
 70
 71
@@ -4619,12 +4334,7 @@ 

Connectors Services 79 80 81 -82

-
-
-
-
def process_connector(self, connector_name: str):
+82
def process_connector(self, connector_name: str):
     """
     Creates a connector instance, verifies the connection, and returns the connector details.
 
@@ -4639,124 +4349,95 @@ 

Connectors Services connection_details = Connector.get_connector_info_from_db(connector_name) logger.info(f"Connection details: {connection_details}") return {"name": connector_name, **connection_successful, **connection_details} -

-
-
-
-
-
-
- -
-

- update_connector(connector_id, - updated_data) -

- -
-

- Updates a connector in the database with the provided - data. -

- -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_id - int - -
-

- The ID of the connector to update. -

-
-
- required -
updated_data - dict - -
-

The new data for the connector.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the success - status and a message indicating the - status. If the update operation was - successful, it returns the connector - name. If the connector was not - found, it returns a message - indicating so. If a database error - occurred, it returns the error - message. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
118
+
+
+
+ +
+ +
+ + + +

+update_connector(connector_id, updated_data) + +

+ + +
+ +

Updates a connector in the database with the provided data.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_id + int + +
+

The ID of the connector to update.

+
+
+ required +
updated_data + dict + +
+

The new data for the connector.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the success status and a message indicating the status. + If the update operation was successful, it returns the connector name. + If the connector was not found, it returns a message indicating so. + If a database error occurred, it returns the error message.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
118
 119
 120
 121
@@ -4793,12 +4474,7 @@ 

Connectors Services 152 153 154 -155

-
-
-
-
def update_connector(self, connector_id: int, updated_data: dict):
+155
def update_connector(self, connector_id: int, updated_data: dict):
     """
     Updates a connector in the database with the provided data.
 
@@ -4836,124 +4512,95 @@ 

Connectors Services except SQLAlchemyError as e: return {"message": f"Database error occurred: {e}", "success": False} -

-
-
-
-
-
-
- -
-

- update_connector_in_db(connector_id, - updated_data) -

- -
-

- Updates a connector in the database with the provided - data. -

- -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_id - int - -
-

- The ID of the connector to update. -

-
-
- required -
updated_data - dict - -
-

The new data for the connector.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the success - status and a message indicating the - status. If the update operation was - successful, it returns the connector - name. If the connector was not - found, it returns a message - indicating so. If a database error - occurred, it returns the error - message. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
29
+
+
+
+ +
+ +
+ + + +

+update_connector_in_db(connector_id, updated_data) + +

+ + +
+ +

Updates a connector in the database with the provided data.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_id + int + +
+

The ID of the connector to update.

+
+
+ required +
updated_data + dict + +
+

The new data for the connector.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the success status and a message indicating the status. + If the update operation was successful, it returns the connector name. + If the connector was not found, it returns a message indicating so. + If a database error occurred, it returns the error message.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
29
 30
 31
 32
@@ -4990,12 +4637,7 @@ 

Connectors Services 63 64 65 -66

-
-
-
-
def update_connector_in_db(self, connector_id: int, updated_data: dict):
+66
def update_connector_in_db(self, connector_id: int, updated_data: dict):
     """
     Updates a connector in the database with the provided data.
 
@@ -5033,104 +4675,81 @@ 

Connectors Services } except SQLAlchemyError as e: return {"success": False, "message": f"Database error occurred: {e}"} -

-
-
-
-
-
-
- -
-

- validate_connector_exists(connector_id) -

- -
-

Validates that a connector exists in the database.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_id - int - -
-

- The ID of the connector to validate. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the success - status and a message indicating the - status. If the connector exists, it - also returns the connector name. If - the connector was not found, it - returns a message indicating so. If - a database error occurred, it - returns the error message. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
 84
+
+
+
+ +
+ +
+ + + +

+validate_connector_exists(connector_id) + +

+ + +
+ +

Validates that a connector exists in the database.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_id + int + +
+

The ID of the connector to validate.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the success status and a message indicating the status. + If the connector exists, it also returns the connector name. + If the connector was not found, it returns a message indicating so. + If a database error occurred, it returns the error message.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
 84
  85
  86
  87
@@ -5162,12 +4781,7 @@ 

Connectors Services 113 114 115 -116

-
-
-
-
def validate_connector_exists(self, connector_id: int):
+116
def validate_connector_exists(self, connector_id: int):
     """
     Validates that a connector exists in the database.
 
@@ -5200,105 +4814,80 @@ 

Connectors Services } except SQLAlchemyError as e: return {"message": f"Database error occurred: {e}", "success": False} -

-
-
-
-
-
-
- -
-

- validate_request_data(request_data) -

- -
-

- Validates the request data to ensure - connector_url, - connector_username, and - connector_password are present. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
request_data - dict - -
-

The request data to validate.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the success - status and a message indicating the - status. If the data is valid, it - returns a message indicating so. If - the data is invalid, it returns a - message indicating so. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
201
+
+
+
+ +
+ +
+ + + +

+validate_request_data(request_data) + +

+ + +
+ +

Validates the request data to ensure connector_url, connector_username, and connector_password are present.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
request_data + dict + +
+

The request data to validate.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the success status and a message indicating the status. + If the data is valid, it returns a message indicating so. + If the data is invalid, it returns a message indicating so.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
201
 202
 203
 204
@@ -5321,12 +4910,7 @@ 

Connectors Services 221 222 223 -224

-
-
-
-
def validate_request_data(self, request_data: dict):
+224
def validate_request_data(self, request_data: dict):
     """
     Validates the request data to ensure `connector_url`, `connector_username`, and `connector_password` are present.
 
@@ -5350,104 +4934,80 @@ 

Connectors Services "are present", "success": False, } -

-
-
-
-
-
-
- -
-

- validate_request_data_api_key(request_data) -

- -
-

- Validates the request data to ensure - connector_url and - connector_api_key are present. -

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
request_data - dict - -
-

The request data to validate.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the success - status and a message indicating the - status. If the data is valid, it - returns a message indicating so. If - the data is invalid, it returns a - message indicating so. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
226
+
+
+
+ +
+ +
+ + + +

+validate_request_data_api_key(request_data) + +

+ + +
+ +

Validates the request data to ensure connector_url and connector_api_key are present.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
request_data + dict + +
+

The request data to validate.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the success status and a message indicating the status. + If the data is valid, it returns a message indicating so. + If the data is invalid, it returns a message indicating so.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
226
 227
 228
 229
@@ -5468,12 +5028,7 @@ 

Connectors Services 244 245 246 -247

-
-
-
-
def validate_request_data_api_key(self, request_data: dict):
+247
def validate_request_data_api_key(self, request_data: dict):
     """
     Validates the request data to ensure `connector_url` and `connector_api_key` are present.
 
@@ -5495,106 +5050,81 @@ 

Connectors Services "message": "Request data is invalid. Ensure connector_url and connector_api_key are present", "success": False, } -

-
-
-
-
-
-
- -
-

- verify_connector_connection(connector_id) -

- -
-

Verifies the connection of a connector.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_id - int - -
-

- The ID of the connector to verify. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict -
-

- A dictionary containing the success - status and a message indicating the - status. If the connection was - verified successfully, it returns - the connector name and the - connection status. If the connector - was not found, it returns a message - indicating so. If a database error - occurred, it returns the error - message. -

-
-
- -
- - Source code in - app\services\connectors\connectors.py - -
- - -
-
-
157
+
+
+
+ +
+ +
+ + + +

+verify_connector_connection(connector_id) + +

+ + +
+ +

Verifies the connection of a connector.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_id + int + +
+

The ID of the connector to verify.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + +
+

A dictionary containing the success status and a message indicating the status. + If the connection was verified successfully, it returns the connector name and the connection status. + If the connector was not found, it returns a message indicating so. + If a database error occurred, it returns the error message.

+
+
+ +
+ Source code in app\services\connectors\connectors.py +
- - -
157
 158
 159
 160
@@ -5636,12 +5166,7 @@ 

Connectors Services 196 197 198 -199

-
-
-
-
def verify_connector_connection(self, connector_id: int):
+199
def verify_connector_connection(self, connector_id: int):
     """
     Verifies the connection of a connector.
 
@@ -5684,103 +5209,82 @@ 

Connectors Services } except SQLAlchemyError as e: return {"message": f"Database error occurred: {e}", "success": False} -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
+
+
+
- -
- - -
-
-
-
+
+ + + +
- - - - - +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/graylog/index.html b/backend/site/graylog/index.html index 8ac4592e..b2089f7c 100644 --- a/backend/site/graylog/index.html +++ b/backend/site/graylog/index.html @@ -1,612 +1,818 @@ + - - - + + + + + + + + + + + + + + + + + + + Graylog - SOCFortress CoPilot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + - - - +

Graylog

- Graylog - SOCFortress CoPilot +

Graylog Overview

+

Graylog Model

- - - - - - - - - - - - - - - - - - - -
- -
- -
- -
- - -
-
- - -
-
-

Graylog

- -

Graylog Overview

-

Graylog Model

- -
- -
-
-
-

- GraylogMetricsAllocation -

- -
-

- Bases: - db.Model -

- -

- Class for Graylog metrics allocation which stores throughput - metrics. The timestamp is generated for each entry and it is invoked - every 5 minutes. This class inherits from SQLAlchemy's Model class. -

- -
- Source code in app\models\graylog.py -
- - -
-
-
13
+
+
+  
+ + + +
+ + + + + + + + +
+ + + +

+ GraylogMetricsAllocation + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for Graylog metrics allocation which stores throughput metrics. +The timestamp is generated for each entry and it is invoked every 5 minutes. +This class inherits from SQLAlchemy's Model class.

+ + +
+ Source code in app\models\graylog.py +
- - -
13
 14
 15
 16
@@ -658,12 +864,7 @@ 

Graylog Model

62 63 64 -65
-
-
-
-
class GraylogMetricsAllocation(db.Model):
+65
class GraylogMetricsAllocation(db.Model):
     """
     Class for Graylog metrics allocation which stores throughput metrics.
     The timestamp is generated for each entry and it is invoked every 5 minutes.
@@ -716,64 +917,45 @@ 

Graylog Model

:return: A string representation of the instance's id. """ return f"<GraylogMetricsAllocation {self.id}>" -
-
-
-
-
- -
-
-

- __init__(input_usage, - output_usage, - processor_usage, - input_1_sec_rate, - output_1_sec_rate, - total_input, - total_output) -

- -
-

- Initialize a new instance of the - GraylogMetricsAllocation class. -

-

- :param input_usage: The input usage value. :param - output_usage: The output usage value. :param - processor_usage: The processor usage value. :param - input_1_sec_rate: The input per second rate. :param - output_1_sec_rate: The output per second rate. :param - total_input: The total input value. :param total_output: - The total output value. -

- -
- - Source code in app\models\graylog.py - -
- - -
-
-
30
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(input_usage, output_usage, processor_usage, input_1_sec_rate, output_1_sec_rate, total_input, total_output) + +

+ + +
+ +

Initialize a new instance of the GraylogMetricsAllocation class.

+

:param input_usage: The input usage value. +:param output_usage: The output usage value. +:param processor_usage: The processor usage value. +:param input_1_sec_rate: The input per second rate. +:param output_1_sec_rate: The output per second rate. +:param total_input: The total input value. +:param total_output: The total output value.

+ +
+ Source code in app\models\graylog.py +
- - -
30
 31
 32
 33
@@ -800,12 +982,7 @@ 

Graylog Model

54 55 56 -57
-
-
-
-
def __init__(
+57
def __init__(
     self,
     input_usage: float,
     output_usage: float,
@@ -833,107 +1010,78 @@ 

Graylog Model

self.output_1_sec_rate = output_1_sec_rate self.total_input = total_input self.total_output = total_output -
-
-
-
-
-
-
- -
-

- __repr__() -

- -
-

- Returns a string representation of the - GraylogMetricsAllocation instance. -

-

- :return: A string representation of the instance's id. -

- -
- - Source code in app\models\graylog.py - -
- - -
-
-
59
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the GraylogMetricsAllocation instance.

+

:return: A string representation of the instance's id.

+ +
+ Source code in app\models\graylog.py +
- - -
59
 60
 61
 62
 63
 64
-65
-
-
-
-
def __repr__(self) -> str:
+65
def __repr__(self) -> str:
     """
     Returns a string representation of the GraylogMetricsAllocation instance.
 
     :return: A string representation of the instance's id.
     """
     return f"<GraylogMetricsAllocation {self.id}>"
-
-
-
-
-
-
-
-
-
-
- -
-

- GraylogMetricsAllocationSchema -

- -
-

- Bases: - ma.Schema -

- -

- Schema for serializing and deserializing instances of the - GraylogMetricsAllocation class. -

- -
- Source code in app\models\graylog.py -
- - -
-
-
68
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ GraylogMetricsAllocationSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the GraylogMetricsAllocation class.

+ + +
+ Source code in app\models\graylog.py +
- - -
68
 69
 70
 71
@@ -953,12 +1101,7 @@ 

Graylog Model

85 86 87 -88
-
-
-
-
class GraylogMetricsAllocationSchema(ma.Schema):
+88
class GraylogMetricsAllocationSchema(ma.Schema):
     """
     Schema for serializing and deserializing instances of the GraylogMetricsAllocation class.
     """
@@ -979,39 +1122,40 @@ 

Graylog Model

"total_output", "timestamp", ) -
-
-
-
-
- -
-
-

- Meta -

- -
-

- Meta class defines the fields to be - serialized/deserialized. -

- -
- - Source code in app\models\graylog.py - -
- - -
-
-
73
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\graylog.py +
- - -
73
 74
 75
 76
@@ -1026,12 +1170,7 @@ 

Graylog Model

85 86 87 -88
-
-
-
-
class Meta:
+88
class Meta:
     """
     Meta class defines the fields to be serialized/deserialized.
     """
@@ -1047,104 +1186,134 @@ 

Graylog Model

"total_output", "timestamp", ) -
-
-
-
-
- -
-
-
-
-
-
-
-
-
-


-

Graylog Routes

- -
- -
-
-
-

- delete_index(index_name) -

- -
-

Endpoint to delete a Graylog index.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
index_name - str - -
-

The name of the index to be deleted.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A JSON object containing the result of the - deletion operation. -

-
-
- -
- Source code in app\routes\graylog.py -
- - -
-
-
58
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + +
+ +
+ +


+

Graylog Routes

+ + +
+ + + +
+ + + +
+ + + + + + + + + +
+ + + +

+delete_index(index_name) + +

+ + +
+ +

Endpoint to delete a Graylog index.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
index_name + str + +
+

The name of the index to be deleted.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A JSON object containing the result of the deletion operation.

+
+
+ +
+ Source code in app\routes\graylog.py +
- - -
58
 59
 60
 61
@@ -1157,12 +1326,7 @@ 

68 69 70 -71

-
-
-
-
@bp.route("/graylog/indices/<index_name>/delete", methods=["DELETE"])
+71
@bp.route("/graylog/indices/<index_name>/delete", methods=["DELETE"])
 def delete_index(index_name: str) -> dict:
     """
     Endpoint to delete a Graylog index.
@@ -1176,61 +1340,51 @@ 

service = IndexService() result = service.delete_index(index_name) return result -

-
-
-
-
-
-
- -
-

- get_indices() -

- -
-

Endpoint to collect Graylog indices.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A JSON object containing the list of all - indices. -

-
-
- -
- Source code in app\routes\graylog.py -
- - -
-
-
45
+
+
+
+ +
+ +
+ + + +

+get_indices() + +

+ + +
+ +

Endpoint to collect Graylog indices.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A JSON object containing the list of all indices.

+
+
+ +
+ Source code in app\routes\graylog.py +
- - -
45
 46
 47
 48
@@ -1240,12 +1394,7 @@ 

52 53 54 -55

-
-
-
-
@bp.route("/graylog/indices", methods=["GET"])
+55
@bp.route("/graylog/indices", methods=["GET"])
 def get_indices() -> dict:
     """
     Endpoint to collect Graylog indices.
@@ -1256,61 +1405,51 @@ 

service = IndexService() indices = service.collect_indices() return indices -

-
-
-
-
-
-
- -
-

- get_inputs() -

- -
-

Endpoint to collect Graylog inputs.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A JSON object containing the list of all running - and configured inputs. -

-
-
- -
- Source code in app\routes\graylog.py -
- - -
-
-
74
+
+
+
+ +
+ +
+ + + +

+get_inputs() + +

+ + +
+ +

Endpoint to collect Graylog inputs.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A JSON object containing the list of all running and configured inputs.

+
+
+ +
+ Source code in app\routes\graylog.py +
- - -
74
 75
 76
 77
@@ -1323,12 +1462,7 @@ 

84 85 86 -87

-
-
-
-
@bp.route("/graylog/inputs", methods=["GET"])
+87
@bp.route("/graylog/inputs", methods=["GET"])
 def get_inputs() -> dict:
     """
     Endpoint to collect Graylog inputs.
@@ -1342,61 +1476,51 @@ 

return jsonify( {"running_inputs": running_inputs, "configured_inputs": configured_inputs}, ) -

-
-
-
-
-
-
- -
-

- get_messages() -

- -
-

Endpoint to collect the latest 10 messages from Graylog.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A JSON object containing the list of all the - messages. -

-
-
- -
- Source code in app\routes\graylog.py -
- - -
-
-
15
+
+
+
+ +
+ +
+ + + +

+get_messages() + +

+ + +
+ +

Endpoint to collect the latest 10 messages from Graylog.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A JSON object containing the list of all the messages.

+
+
+ +
+ Source code in app\routes\graylog.py +
- - -
15
 16
 17
 18
@@ -1406,12 +1530,7 @@ 

22 23 24 -25

-
-
-
-
@bp.route("/graylog/messages", methods=["GET"])
+25
@bp.route("/graylog/messages", methods=["GET"])
 def get_messages() -> dict:
     """
     Endpoint to collect the latest 10 messages from Graylog.
@@ -1422,61 +1541,52 @@ 

service = MessagesService() messages = service.collect_messages() return messages -

-
-
-
-
-
-
- -
-

- get_metrics() -

- -
-

Endpoint to collect Graylog metrics.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - dict - -
-

- A JSON object containing the list of all - metrics, including the uncommitted journal size. -

-
-
- -
- Source code in app\routes\graylog.py -
- - -
-
-
28
+
+
+
+ +
+ +
+ + + +

+get_metrics() + +

+ + +
+ +

Endpoint to collect Graylog metrics.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + dict + +
+

A JSON object containing the list of all metrics, + including the uncommitted journal size.

+
+
+ +
+ Source code in app\routes\graylog.py +
- - -
28
 29
 30
 31
@@ -1490,12 +1600,7 @@ 

39 40 41 -42

-
-
-
-
@bp.route("/graylog/metrics", methods=["GET"])
+42
@bp.route("/graylog/metrics", methods=["GET"])
 def get_metrics() -> dict:
     """
     Endpoint to collect Graylog metrics.
@@ -1510,46 +1615,59 @@ 

return jsonify( {"uncommitted_journal_size": uncommitted_journal_size, "metrics": metrics}, ) -

-
-
-
-
-
-
-
-
-
-


-

Graylog Services

- -
- -
-
-
-

- IndexService -

- -
-

- A service class that encapsulates the logic for pulling index data - from Graylog -

- -
- - Source code in app\services\Graylog\index.py - -
- - -
-
-
 12
+
+
+
+ +
+ + + +
+ +
+ +


+

Graylog Services

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ IndexService + + +

+ + +
+ + +

A service class that encapsulates the logic for pulling index data from Graylog

+ + +
+ Source code in app\services\Graylog\index.py +
- - -
 12
  13
  14
  15
@@ -1686,12 +1804,7 @@ 

146 147 148 -149

-
-
-
-
class IndexService:
+149
class IndexService:
     """
     A service class that encapsulates the logic for pulling index data from Graylog
     """
@@ -1829,43 +1942,38 @@ 

"it cannot be deleted.", "success": False, } -

-
-
-
-
- -
-
-

- __init__() -

- -
-

- Initializes the IndexService by collecting Graylog - details. -

- -
- - Source code in - app\services\Graylog\index.py - -
- - -
-
-
19
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__() + +

+ + +
+ +

Initializes the IndexService by collecting Graylog details.

+ +
+ Source code in app\services\Graylog\index.py +
- - -
19
 20
 21
 22
@@ -1873,12 +1981,7 @@ 

24 25 26 -27

-
-
-
-
def __init__(self):
+27
def __init__(self):
     """
     Initializes the IndexService by collecting Graylog details.
     """
@@ -1887,76 +1990,51 @@ 

self.connector_username, self.connector_password, ) = UniversalService().collect_graylog_details("Graylog") -

-
-
-
-
-
-
- -
-

- collect_indices() -

- -
-

Collects the indices that are managed by Graylog.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[bool, str, - Dict]] - -
-

- A dictionary containing the success - status, a message, and potentially a - dictionary with indices. -

-
-
- -
- - Source code in - app\services\Graylog\index.py - -
- - -
-
-
29
+
+
+
+ +
+ +
+ + + +

+collect_indices() + +

+ + +
+ +

Collects the indices that are managed by Graylog.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[bool, str, Dict]] + +
+

A dictionary containing the success status, a message, and potentially a dictionary with indices.

+
+
+ +
+ Source code in app\services\Graylog\index.py +
- - -
29
 30
 31
 32
@@ -1976,12 +2054,7 @@ 

46 47 48 -49

-
-
-
-
def collect_indices(self) -> Dict[str, Union[bool, str, Dict]]:
+49
def collect_indices(self) -> Dict[str, Union[bool, str, Dict]]:
     """
     Collects the indices that are managed by Graylog.
 
@@ -2002,103 +2075,79 @@ 

managed_indices["index_names"] = index_names return managed_indices -

-
-
-
-
-
-
- -
-

- delete_index(index_name) -

- -
-

Deletes the specified index from Graylog.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
index_name - str - -
-

The name of the index to delete.

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[bool, str]] - -
-

- A dictionary containing the - response. -

-
-
- -
- - Source code in - app\services\Graylog\index.py - -
- - -
-
-
 87
+
+
+
+ +
+ +
+ + + +

+delete_index(index_name) + +

+ + +
+ +

Deletes the specified index from Graylog.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
index_name + str + +
+

The name of the index to delete.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[bool, str]] + +
+

A dictionary containing the response.

+
+
+ +
+ Source code in app\services\Graylog\index.py +
- - -
 87
  88
  89
  90
@@ -2131,12 +2180,7 @@ 

117 118 119 -120

-
-
-
-
def delete_index(self, index_name: str) -> Dict[str, Union[bool, str]]:
+120
def delete_index(self, index_name: str) -> Dict[str, Union[bool, str]]:
     """
     Deletes the specified index from Graylog.
 
@@ -2170,47 +2214,66 @@ 

"message": f"Failed to delete index {index_name} from Graylog", "success": False, } -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- -
-
-
-

- InputsService -

- -
-

- A service class that encapsulates the logic for pulling index data - from Graylog -

- -
- - Source code in app\services\Graylog\inputs.py - -
- - -
-
-
 14
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ InputsService + + +

+ + +
+ + +

A service class that encapsulates the logic for pulling index data from Graylog

+ + +
+ Source code in app\services\Graylog\inputs.py +
- - -
 14
  15
  16
  17
@@ -2334,12 +2397,7 @@ 

135 136 137 -138

-
-
-
-
class InputsService:
+138
class InputsService:
     """
     A service class that encapsulates the logic for pulling index data from Graylog
     """
@@ -2464,43 +2522,38 @@ 

except Exception as e: logger.error(f"Failed to collect configured inputs: {e}") return {"message": "Failed to collect configured inputs", "success": False} -

-
-
-
-
- -
-
-

- __init__() -

- -
-

- Initializes the InputsService by collecting Graylog - details. -

- -
- - Source code in - app\services\Graylog\inputs.py - -
- - -
-
-
21
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__() + +

+ + +
+ +

Initializes the InputsService by collecting Graylog details.

+ +
+ Source code in app\services\Graylog\inputs.py +
- - -
21
 22
 23
 24
@@ -2508,12 +2561,7 @@ 

26 27 28 -29

-
-
-
-
def __init__(self):
+29
def __init__(self):
     """
     Initializes the InputsService by collecting Graylog details.
     """
@@ -2522,82 +2570,51 @@ 

self.connector_username, self.connector_password, ) = UniversalService().collect_graylog_details("Graylog") -

-
-
-
-
-
-
- -
-

- collect_configured_inputs() -

- -
-

- Collects the configured inputs that are managed by - Graylog. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[bool, str, - List[Dict[str, - Union[str, int]]]]] - -
-

- A dictionary containing the success - status, a message, and potentially a - list of configured inputs. -

-
-
- -
- - Source code in - app\services\Graylog\inputs.py - -
- - -
-
-
 86
+
+
+
+ +
+ +
+ + + +

+collect_configured_inputs() + +

+ + +
+ +

Collects the configured inputs that are managed by Graylog.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[bool, str, List[Dict[str, Union[str, int]]]]] + +
+

A dictionary containing the success status, a message, and potentially a list of configured inputs.

+
+
+ +
+ Source code in app\services\Graylog\inputs.py +
- - -
 86
  87
  88
  89
@@ -2616,12 +2633,7 @@ 

102 103 104 -105

-
-
-
-
def collect_configured_inputs(
+105
def collect_configured_inputs(
     self,
 ) -> Dict[str, Union[bool, str, List[Dict[str, Union[str, int]]]]]:
     """
@@ -2641,81 +2653,51 @@ 

if configured_inputs["success"]: return configured_inputs -

-
-
-
-
-
-
- -
-

- collect_running_inputs() -

- -
-

- Collects the running inputs that are managed by Graylog. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[bool, str, - List[Dict[str, - Union[str, int]]]]] - -
-

- A dictionary containing the success - status, a message, and potentially a - list of running inputs. -

-
-
- -
- - Source code in - app\services\Graylog\inputs.py - -
- - -
-
-
31
+
+
+
+ +
+ +
+ + + +

+collect_running_inputs() + +

+ + +
+ +

Collects the running inputs that are managed by Graylog.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[bool, str, List[Dict[str, Union[str, int]]]]] + +
+

A dictionary containing the success status, a message, and potentially a list of running inputs.

+
+
+ +
+ Source code in app\services\Graylog\inputs.py +
- - -
31
 32
 33
 34
@@ -2734,12 +2716,7 @@ 

47 48 49 -50

-
-
-
-
def collect_running_inputs(
+50
def collect_running_inputs(
     self,
 ) -> Dict[str, Union[bool, str, List[Dict[str, Union[str, int]]]]]:
     """
@@ -2759,50 +2736,66 @@ 

if running_inputs["success"]: return running_inputs -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- -
-
-
-

- MessagesService -

- -
-

- A service class that encapsulates the logic for polling messages - from Graylog. -

- -
- - Source code in app\services\Graylog\messages.py - -
- - -
-
-
13
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ MessagesService + + +

+ + +
+ + +

A service class that encapsulates the logic for polling messages from Graylog.

+ + +
+ Source code in app\services\Graylog\messages.py +
- - -
13
 14
 15
 16
@@ -2869,12 +2862,7 @@ 

77 78 79 -80

-
-
-
-
class MessagesService:
+80
class MessagesService:
     """
     A service class that encapsulates the logic for polling messages from Graylog.
     """
@@ -2942,78 +2930,60 @@ 

} except Exception as e: return self._handle_message_fetch_error(e) -

-
-
-
-
- -
-
-

- collect_messages() -

- -
-

Collects the latest 10 messages from Graylog.

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool, - List[Dict[str, - Any]]]] - -
-

- A dictionary containing the success - status, a message, and potentially a - list of Graylog messages. -

-
-
- -
- - Source code in - app\services\Graylog\messages.py - -
- - -
-
-
44
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+collect_messages() + +

+ + +
+ +

Collects the latest 10 messages from Graylog.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool, List[Dict[str, Any]]]] + +
+

A dictionary containing the success status, a message, and potentially a list of Graylog messages.

+
+
+ +
+ Source code in app\services\Graylog\messages.py +
- - -
44
 45
 46
 47
@@ -3049,12 +3019,7 @@ 

77 78 79 -80

-
-
-
-
def collect_messages(self) -> Dict[str, Union[str, bool, List[Dict[str, Any]]]]:
+80
def collect_messages(self) -> Dict[str, Union[str, bool, List[Dict[str, Any]]]]:
     """
     Collects the latest 10 messages from Graylog.
 
@@ -3091,50 +3056,66 @@ 

} except Exception as e: return self._handle_message_fetch_error(e) -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- -
-
-
-

- MetricsService -

- -
-

- A service class that encapsulates the logic for pulling metrics from - Graylog. -

- -
- - Source code in app\services\Graylog\metrics.py - -
- - -
-
-
 15
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ MetricsService + + +

+ + +
+ + +

A service class that encapsulates the logic for pulling metrics from Graylog.

+ + +
+ Source code in app\services\Graylog\metrics.py +
- - -
 15
  16
  17
  18
@@ -3363,12 +3344,7 @@ 

241 242 243 -244

-
-
-
-
class MetricsService:
+244
class MetricsService:
     """
     A service class that encapsulates the logic for pulling metrics from Graylog.
     """
@@ -3598,40 +3574,38 @@ 

"success": True, "throughput_metrics": throughput_metrics_list, } -

-
-
-
-
- -
-
-

- __init__() -

- -
-

Initializes the MetricsService with Graylog details.

- -
- - Source code in - app\services\Graylog\metrics.py - -
- - -
-
-
32
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__() + +

+ + +
+ +

Initializes the MetricsService with Graylog details.

+ +
+ Source code in app\services\Graylog\metrics.py +
- - -
32
 33
 34
 35
@@ -3639,12 +3613,7 @@ 

37 38 39 -40

-
-
-
-
def __init__(self):
+40
def __init__(self):
     """
     Initializes the MetricsService with Graylog details.
     """
@@ -3653,83 +3622,58 @@ 

self.connector_username, self.connector_password, ) = UniversalService().collect_graylog_details("Graylog") -

-
-
-
-
-
-
- -
-

- collect_throughput_metrics() -

- -
-

- Collects the following Graylog Metrics: - Input Usage - - Output Usage - Processor Usage - Input 1 Seconds Rate - - Output 1 Seconds Rate - Total Input - Total Output -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool, - List[Dict[str, - Any]]]] - -
-

- A dictionary containing the success - status, a message, and the list of - throughput metrics. -

-
-
- -
- - Source code in - app\services\Graylog\metrics.py - -
- - -
-
-
66
+
+
+
+ +
+ +
+ + + +

+collect_throughput_metrics() + +

+ + +
+ +

Collects the following Graylog Metrics: +- Input Usage +- Output Usage +- Processor Usage +- Input 1 Seconds Rate +- Output 1 Seconds Rate +- Total Input +- Total Output

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool, List[Dict[str, Any]]]] + +
+

A dictionary containing the success status, a message, and the list of throughput metrics.

+
+
+ +
+ Source code in app\services\Graylog\metrics.py +
- - -
66
 67
 68
 69
@@ -3760,12 +3704,7 @@ 

94 95 96 -97

-
-
-
-
def collect_throughput_metrics(
+97
def collect_throughput_metrics(
     self,
 ) -> Dict[str, Union[str, bool, List[Dict[str, Any]]]]:
     """
@@ -3797,77 +3736,51 @@ 

if throughput_usage["success"] is False: return throughput_usage return throughput_usage -

-
-
-
-
-
-
- -
-

- collect_uncommitted_journal_size() -

- -
-

- Collects the journal size of uncommitted messages from - Graylog. -

- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
dict - Dict[str, - Union[str, bool, int]] - -
-

- A dictionary containing the success - status, a message, and the size of - uncommitted journal entries. -

-
-
- -
- - Source code in - app\services\Graylog\metrics.py - -
- - -
-
-
42
+
+
+
+ +
+ +
+ + + +

+collect_uncommitted_journal_size() + +

+ + +
+ +

Collects the journal size of uncommitted messages from Graylog.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
dict + Dict[str, Union[str, bool, int]] + +
+

A dictionary containing the success status, a message, and the size of uncommitted journal entries.

+
+
+ +
+ Source code in app\services\Graylog\metrics.py +
- - -
42
 43
 44
 45
@@ -3889,12 +3802,7 @@ 

61 62 63 -64

-
-
-
-
def collect_uncommitted_journal_size(self) -> Dict[str, Union[str, bool, int]]:
+64
def collect_uncommitted_journal_size(self) -> Dict[str, Union[str, bool, int]]:
     """
     Collects the journal size of uncommitted messages from Graylog.
 
@@ -3917,50 +3825,66 @@ 

if journal_size["success"] is False: return journal_size return journal_size -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- -
-
-
-

- UniversalService -

- -
-

- A service class that encapsulates the logic for polling messages - from Graylog. -

- -
- - Source code in app\services\Graylog\universal.py - -
- - -
-
-
 5
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ UniversalService + + +

+ + +
+ + +

A service class that encapsulates the logic for polling messages from Graylog.

+ + +
+ Source code in app\services\Graylog\universal.py +
- - -
 5
  6
  7
  8
@@ -3988,12 +3912,7 @@ 

30 31 32 -33

-
-
-
-
class UniversalService:
+33
class UniversalService:
     """
     A service class that encapsulates the logic for polling messages from Graylog.
     """
@@ -4022,99 +3941,88 @@ 

) else: return None, None, None -

-
-
-
-
- -
-
-

- collect_graylog_details(connector_name) -

- -
-

Collects the details of the Graylog connector.

- -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
connector_name - str - -
-

- The name of the Graylog connector. -

-
-
- required -
- -

Returns:

- - - - - - - - - - - - - - - -
NameTypeDescription
tuple - tuple - -
-

- A tuple containing the connection - URL, username, and password. -

-
-
- -
- - Source code in - app\services\Graylog\universal.py - -
- - -
-
-
13
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+collect_graylog_details(connector_name) + +

+ + +
+ +

Collects the details of the Graylog connector.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_name + str + +
+

The name of the Graylog connector.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
tuple + tuple + +
+

A tuple containing the connection URL, username, and password.

+
+
+ +
+ Source code in app\services\Graylog\universal.py +
- - -
13
 14
 15
 16
@@ -4134,12 +4042,7 @@ 

30 31 32 -33

-
-
-
-
def collect_graylog_details(self, connector_name: str) -> tuple:
+33
def collect_graylog_details(self, connector_name: str) -> tuple:
     """
     Collects the details of the Graylog connector.
 
@@ -4160,103 +4063,82 @@ 

) else: return None, None, None -

-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
+
+
+
- -
- - -
-
-
-
+
+ + + +
+ +
- - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/site/index.html b/backend/site/index.html index eb9e260a..3b5d9071 100644 --- a/backend/site/index.html +++ b/backend/site/index.html @@ -1,378 +1,586 @@ + - - - - - - - - - + + + + + + + + + + + + + + + + SOCFortress CoPilot - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - -
- Skip to content + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+ + + + +
+
+ + -
- - - - - - - -
- - - -
- - -
-
-
-
-
- -
-
-
- -
-
-

Home

- -

Project overview

- -
- -
-
-
-

- AgentMetadata -

- -
-

- Bases: - db.Model -

- -

- Class for agent metadata which stores the agent ID, IP address, - hostname, OS, last seen timestamp, and boolean for critical asset. - This class inherits from SQLAlchemy's Model class. -

- -
- Source code in app\models\agents.py -
- - -
-
-
14
+
+
+
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ AgentMetadata + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for agent metadata which stores the agent ID, IP address, hostname, OS, last seen timestamp, +and boolean for critical asset. This class inherits from SQLAlchemy's Model class.

+ + +
+ Source code in app\models\agents.py +
- - -
14
 15
 16
 17
@@ -439,12 +647,7 @@ 

78 79 80 -81

-
-
-
-
class AgentMetadata(db.Model):
+81
class AgentMetadata(db.Model):
     """
     Class for agent metadata which stores the agent ID, IP address, hostname, OS, last seen timestamp,
     and boolean for critical asset. This class inherits from SQLAlchemy's Model class.
@@ -512,56 +715,44 @@ 

""" db.session.add(self) db.session.commit() -

-
-
-
-
- -
-
-

- __init__(agent_id, - ip_address, os, hostname, - critical_asset, - last_seen) -

- -
-

Initialize a new instance of the AgentMetadata class.

-

- :param agent_id: Unique ID for the agent. :param - ip_address: IP address of the agent. :param os: - Operating system of the agent. :param hostname: Hostname - of the agent. :param critical_asset: Boolean value - indicating if the agent is a critical asset. :param - last_seen: Timestamp of when the agent was last seen. -

- -
- - Source code in app\models\agents.py - -
- - -
-
-
28
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(agent_id, ip_address, os, hostname, critical_asset, last_seen) + +

+ + +
+ +

Initialize a new instance of the AgentMetadata class.

+

:param agent_id: Unique ID for the agent. +:param ip_address: IP address of the agent. +:param os: Operating system of the agent. +:param hostname: Hostname of the agent. +:param critical_asset: Boolean value indicating if the agent is a critical asset. +:param last_seen: Timestamp of when the agent was last seen.

+ +
+ Source code in app\models\agents.py +
- - -
28
 29
 30
 31
@@ -585,12 +776,7 @@ 

49 50 51 -52

-
-
-
-
def __init__(
+52
def __init__(
     self,
     agent_id: str,
     ip_address: str,
@@ -615,249 +801,177 @@ 

self.hostname = hostname self.critical_asset = critical_asset self.last_seen = last_seen -

-
-
-
-
-
-
- -
-

- __repr__() -

- -
-

- Returns a string representation of the AgentMetadata - instance. -

-

:return: A string representation of the agent ID.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
54
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the AgentMetadata instance.

+

:return: A string representation of the agent ID.

+ +
+ Source code in app\models\agents.py +
- - -
54
 55
 56
 57
 58
 59
-60
-
-
-
-
def __repr__(self) -> str:
+60
def __repr__(self) -> str:
     """
     Returns a string representation of the AgentMetadata instance.
 
     :return: A string representation of the agent ID.
     """
     return f"<AgentMetadata {self.agent_id}>"
-
-
-
-
-
-
-
- -
-

- commit_wazuh_agent_to_db() -

- -
-

Commits the agent to the database.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
76
+
+
+
+ +
+ +
+ + + +

+commit_wazuh_agent_to_db() + +

+ + +
+ +

Commits the agent to the database.

+ +
+ Source code in app\models\agents.py +
- - -
76
 77
 78
 79
 80
-81
-
-
-
-
def commit_wazuh_agent_to_db(self):
+81
def commit_wazuh_agent_to_db(self):
     """
     Commits the agent to the database.
     """
     db.session.add(self)
     db.session.commit()
-
-
-
-
-
-
-
- -
-

- mark_as_critical() -

- -
-

Marks the agent as a critical asset.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
62
+
+
+
+ +
+ +
+ + + +

+mark_as_critical() + +

+ + +
+ +

Marks the agent as a critical asset.

+ +
+ Source code in app\models\agents.py +
- - -
62
 63
 64
 65
 66
-67
-
-
-
-
def mark_as_critical(self):
+67
def mark_as_critical(self):
     """
     Marks the agent as a critical asset.
     """
     self.critical_asset = True
     db.session.commit()
-
-
-
-
-
-
-
- -
-

- mark_as_non_critical() -

- -
-

Marks the agent as a non-critical asset.

- -
- - Source code in app\models\agents.py - -
- - -
-
-
69
+
+
+
+ +
+ +
+ + + +

+mark_as_non_critical() + +

+ + +
+ +

Marks the agent as a non-critical asset.

+ +
+ Source code in app\models\agents.py +
- - -
69
 70
 71
 72
 73
-74
-
-
-
-
def mark_as_non_critical(self):
+74
def mark_as_non_critical(self):
     """
     Marks the agent as a non-critical asset.
     """
     self.critical_asset = False
     db.session.commit()
-
-
-
-
-
-
-
-
-
-
- -
-

- AgentMetadataSchema -

- -
-

- Bases: - ma.Schema -

- -

- Schema for serializing and deserializing instances of the - AgentMetadata class. -

- -
- Source code in app\models\agents.py -
- - -
-
-
 84
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ AgentMetadataSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the AgentMetadata class.

+ + +
+ Source code in app\models\agents.py +
- - -
 84
  85
  86
  87
@@ -875,12 +989,7 @@ 

99 100 101 -102

-
-
-
-
class AgentMetadataSchema(ma.Schema):
+102
class AgentMetadataSchema(ma.Schema):
     """
     Schema for serializing and deserializing instances of the AgentMetadata class.
     """
@@ -899,39 +1008,40 @@ 

"critical_asset", "last_seen", ) -

-
-
-
-
- -
-
-

- Meta -

- -
-

- Meta class defines the fields to be - serialized/deserialized. -

- -
- - Source code in app\models\agents.py - -
- - -
-
-
 89
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\agents.py +
- - -
 89
  90
  91
  92
@@ -944,12 +1054,7 @@ 

99 100 101 -102

-
-
-
-
class Meta:
+102
class Meta:
     """
     Meta class defines the fields to be serialized/deserialized.
     """
@@ -963,105 +1068,100 @@ 

"critical_asset", "last_seen", ) -

-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
- - -
+
+
- -
- - -
-
-
-
+ + +
+ + + + + + + + + + + +
- - - - - +
+ +
+ + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/docs/mkdocs.yml b/backend/site/mkdocs.yml similarity index 100% rename from backend/docs/mkdocs.yml rename to backend/site/mkdocs.yml diff --git a/backend/site/objects.inv b/backend/site/objects.inv index 6896e6a48df42464af3a3cc798991bdbbd0734ea..a059f383d2c25411f4f5087ecd00dd186d5bcafb 100644 GIT binary patch delta 1266 zcmVdoC$45D7{+fdCHxW$S)@ zmLH%f5)ko0yRjr1@3V{D#R8Ow>pH7ML72+$fpD!J>dRknp<(3V&xbesvpH6n!@uMo zzy9{{`!9GtK6`9mo{7c<*0@#Y3@a5Q&Jd!UYJ^%!+3;Lb!G9w`lGJhoP%f$pJnDp~ z8-cVy#XcEaVR=GWpYk&X0X<0?cvs@DFD4rPHv=*0*B8KQ&p583R{EE#7IH>~vLJSAr>1si6 zsQJ{gxgub9V2tE9+k@K1yBWHG_DPv)vhBbpkR$wue@YrWd>g1i&y zWAC0Yd+c6pX^Ez|P7(=3laS;<&A3#7tMGrMPPDE`v427~TdmZz#Q8kz=Bj&q_Bgz| z33PzY8QL(0^?Vk>Se{M7)r(_^1u-m@Ghrg964zSDwU}EZ*YcT!|NhKvly0R?Q=f=H zjPmAv3s8mgEiiAe7Q~y!)W#L|?-b^}ELP-WomksaI&M)8=`;4Q;hXIZEI~Pl&zVy# zNlD+AeSh;qjBQn(OIxzxqa8FDZt1=aGRKPCR(*HHJ}&zLf2SqAHrCbjJ#^MazouI@ zcR$6Vq0eMP11d@?5ta0Az~*DkjY2yN&ZC;#pVLC_JZ1R^^C4joGB`df5^c1bX{>lf zH!hN=ue%qtd)d9Nm$6IJ6@jf=$LKRCru@{;pMMr{leh)?<KdTllGH0pP-SH0+mu!%e&)H@R>+jUJQHLE(3XAar^g@Fx7$JY?pDg zzVU3slH>s8LnP{;eO`m9Pq5N=u+S5#V}aNxnSV=sVd9YB*_^PEV=s8Wr~3Jy1kV@NP=f`!bXlw4^Ii+Suq>AIK9D7aM6m{(#7Zg>)ccq=a~Lf)9548;zTy| zZ=<;v7mh2MBU;r$Y7fM4mI>H27mC?_XaDZbkJNAV<7b4=fWmu5qG(NBbO+ecwQh(9 zYCM-rU;z=jFTd_lw$ocWE@U$fMb}cmXn%DL=?B=DXYCVAK*0PCXQ779n9-Rj@(wkw zKGK#XKN2Y2|Vi;53#)E7ZPct#TOL|+|a(g5_LP)`~=zktgtr!nY7)yK-bn|lz*~r zbd0cqCJNhsoU{w@nsQuaLte5mI_|LAHCcB|v+PwddhfdTVVUnMJrn4I3@IfG&y+(@ zj9U?S(Y{1Rc}Yl#vxR0N4IQ?mhendZv-ZRAvf-TaLo9o`ssb`#WYDAfH&Iz|QO3#n zu6YZ2T=7A(>Qvd#kR#`0W5m~%gKK9Zq>s?=4oAp+beCJP0I2Q(Hw^{+T{Mzoh7|ot zg6%Y%!ns8C4`&2qWz=Qg%)6+}xH;oo2Z-Z-%(b0Mm`T7hk=^j@iK_M{t#~nFugB*s c+NdWY?VRujA}yT22Lhc0kLLmZ2U3-N5J%I6d;kCd delta 1209 zcmV;q1V;Pn3b6{1h=0p++cp%2_k9Y_wA--LZFinz($T_;+S6o-fk;q71OhAo%2u8} zhZj)1fFv%oH{|<0&04d?JObU?^rV@N2T&suj_BULp7nG>(TcZbn?*ZXRzzNDdS^I9)h=mM>81VOTKYK1 zlT;Gym?|!~VSnar7rqP8H6@){jJZYJ1vtn8_Z0L8dvvT-IAnWl+T0P)?HMEKsNJi5 zW;a1+&?YH!tx!hfw5*y-3nROvFruM>iL9Ue_j*nQo><#bJZ_c_@hf();Tr87EI~Smk6BYGNlxG9ee+$6?UkN$Te9N4 z9n=}F>3^;bGR2CFv%a}vAD4WFzr&Q?8oSi=9dy=4zonZucQ?hXp-*I81IltL5tVe+ zVbihZMxh=C=TS}8*EEwap0ZqoX&0~n862M#2{ziSG*rBy>x<;e*TXY9JRe@R(|AeK zB!R74hv*YXru5RTpJri;7z5p6=*D7HIl0%=h=1T5nu(ATzxJ0`6v=1vcH~k&X}fsy z2^vW*P%cF=zdQB?Ux}po&9LX~(v$ZU*MDyjOYI1Y?J|tkHlBT0lAIuYh(ryvjjK2H z3sm|63Oyn_W{91F`H#dmCQcDEn-X@K*fTyJsebx5VrDbMPE&h1S9#7z#LT9IohCLs zJby*Z&WhP-io+ZHh$&hzdrt8Qb9{=Q;uO=KYASt3T3pDE^V@0d*^T3Zrid1$klF#! zon<^W&6#4d-`SttxslqfzW7Q zi%ZgbXW6ZGP0}3GEPGXm-Z}4GSmt@9V*p){KB;8knRM{-aV>&b)GvWvo)cpHY@r!X zLx;`jsgfjf%zZz+tT?Cq6v`e}l}7}O40=@iCMXI^lzwrpHE$vJ74NmGMwN68IdDxj zMtE*5@QjD_8T#Gn46zUHavK%^)iV5GO+y0z5S8SZAw_=@Z#xyIa4u2pk25@?((97{ X%)6k-EOYv`c94bpv5|>inl{u{aFkNW diff --git a/backend/site/sitemap.xml b/backend/site/sitemap.xml index 7962ef42..0f8724ef 100644 --- a/backend/site/sitemap.xml +++ b/backend/site/sitemap.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/backend/site/sitemap.xml.gz b/backend/site/sitemap.xml.gz index 0d95050ffad8ffb736521cd18793eea354bffabd..9c5d7e5d3fa261c8ea5c92e009e904b38c1beba8 100644 GIT binary patch delta 12 Tcmb=gXOr*d;AmJek*yK{7~li< delta 12 Tcmb=gXOr*d;CMQDB3mT@8ifQ$ diff --git a/backend/site/wazuhmanager/index.html b/backend/site/wazuhmanager/index.html new file mode 100644 index 00000000..845b2d41 --- /dev/null +++ b/backend/site/wazuhmanager/index.html @@ -0,0 +1,1969 @@ + + + + + + + + + + + + + + + + + + + + Wazuh-Manager - SOCFortress CoPilot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Wazuh-Manager

+ +

Wazuh-Manager Overview

+

Wazuh-Manager Model

+ + +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ DisabledRules + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for disabled rules which stores the rule ID, previous configuration, new configuration, reason for +disabling, date disabled, and the length of time the rule will be disabled for. +This class inherits from SQLAlchemy's Model class.

+

:ivar id: Unique integer ID of the rule. +:ivar rule_id: ID of the rule. +:ivar previous_level: Previous level configuration of the rule. +:ivar new_level: New level configuration of the rule. +:ivar reason_for_disabling: Reason for disabling the rule. +:ivar date_disabled: Date when the rule was disabled. +:ivar length_of_time: Length of time the rule will be disabled for.

+ + +
+ Source code in app\models\rules.py +
13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
class DisabledRules(db.Model):
+    """
+    Class for disabled rules which stores the rule ID, previous configuration, new configuration, reason for
+    disabling, date disabled, and the length of time the rule will be disabled for.
+    This class inherits from SQLAlchemy's Model class.
+
+    :ivar id: Unique integer ID of the rule.
+    :ivar rule_id: ID of the rule.
+    :ivar previous_level: Previous level configuration of the rule.
+    :ivar new_level: New level configuration of the rule.
+    :ivar reason_for_disabling: Reason for disabling the rule.
+    :ivar date_disabled: Date when the rule was disabled.
+    :ivar length_of_time: Length of time the rule will be disabled for.
+    """
+
+    id: Column[Integer] = db.Column(db.Integer, primary_key=True)
+    rule_id: Column[String] = db.Column(db.String(100))
+    previous_level: Column[String] = db.Column(db.String(1000))
+    new_level: Column[String] = db.Column(db.String(1000))
+    reason_for_disabling: Column[String] = db.Column(db.String(100))
+    date_disabled: Column[DateTime] = db.Column(db.DateTime, default=datetime.utcnow)
+    length_of_time: Column[Integer] = db.Column(db.Integer)
+
+    def __init__(
+        self,
+        rule_id: str,
+        previous_level: str,
+        new_level: str,
+        reason_for_disabling: str,
+        length_of_time: int,
+    ):
+        """
+        Initialize a new instance of the DisabledRules class.
+
+        :param rule_id: The ID of the rule.
+        :param previous_level: The previous level configuration of the rule.
+        :param new_level: The new level configuration of the rule.
+        :param reason_for_disabling: The reason for disabling the rule.
+        :param length_of_time: The length of time the rule will be disabled for.
+        """
+        self.rule_id = rule_id
+        self.previous_level = previous_level
+        self.new_level = new_level
+        self.reason_for_disabling = reason_for_disabling
+        self.length_of_time = length_of_time
+
+    def __repr__(self) -> str:
+        """
+        Returns a string representation of the DisabledRules instance.
+
+        :return: A string representation of the rule ID.
+        """
+        return f"<DisabledRules {self.rule_id}>"
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(rule_id, previous_level, new_level, reason_for_disabling, length_of_time) + +

+ + +
+ +

Initialize a new instance of the DisabledRules class.

+

:param rule_id: The ID of the rule. +:param previous_level: The previous level configuration of the rule. +:param new_level: The new level configuration of the rule. +:param reason_for_disabling: The reason for disabling the rule. +:param length_of_time: The length of time the rule will be disabled for.

+ +
+ Source code in app\models\rules.py +
36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
def __init__(
+    self,
+    rule_id: str,
+    previous_level: str,
+    new_level: str,
+    reason_for_disabling: str,
+    length_of_time: int,
+):
+    """
+    Initialize a new instance of the DisabledRules class.
+
+    :param rule_id: The ID of the rule.
+    :param previous_level: The previous level configuration of the rule.
+    :param new_level: The new level configuration of the rule.
+    :param reason_for_disabling: The reason for disabling the rule.
+    :param length_of_time: The length of time the rule will be disabled for.
+    """
+    self.rule_id = rule_id
+    self.previous_level = previous_level
+    self.new_level = new_level
+    self.reason_for_disabling = reason_for_disabling
+    self.length_of_time = length_of_time
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the DisabledRules instance.

+

:return: A string representation of the rule ID.

+ +
+ Source code in app\models\rules.py +
59
+60
+61
+62
+63
+64
+65
def __repr__(self) -> str:
+    """
+    Returns a string representation of the DisabledRules instance.
+
+    :return: A string representation of the rule ID.
+    """
+    return f"<DisabledRules {self.rule_id}>"
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ DisabledRulesSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the DisabledRules class.

+ + +
+ Source code in app\models\rules.py +
68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
class DisabledRulesSchema(ma.Schema):
+    """
+    Schema for serializing and deserializing instances of the DisabledRules class.
+    """
+
+    class Meta:
+        """
+        Meta class defines the fields to be serialized/deserialized.
+        """
+
+        fields: tuple = (
+            "id",
+            "rule_id",
+            "previous_level",
+            "new_level",
+            "reason_for_disabling",
+            "date_disabled",
+            "length_of_time",
+        )
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\rules.py +
73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
class Meta:
+    """
+    Meta class defines the fields to be serialized/deserialized.
+    """
+
+    fields: tuple = (
+        "id",
+        "rule_id",
+        "previous_level",
+        "new_level",
+        "reason_for_disabling",
+        "date_disabled",
+        "length_of_time",
+    )
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + +
+ + + +
+ + + + + + + + +
+ + + +

+ AgentMetadata + + +

+ + +
+

+ Bases: db.Model

+ + +

Class for agent metadata which stores the agent ID, IP address, hostname, OS, last seen timestamp, +and boolean for critical asset. This class inherits from SQLAlchemy's Model class.

+ + +
+ Source code in app\models\agents.py +
14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
class AgentMetadata(db.Model):
+    """
+    Class for agent metadata which stores the agent ID, IP address, hostname, OS, last seen timestamp,
+    and boolean for critical asset. This class inherits from SQLAlchemy's Model class.
+    """
+
+    id: Column[Integer] = db.Column(db.Integer, primary_key=True)
+    agent_id: Column[String] = db.Column(db.String(100))
+    ip_address: Column[String] = db.Column(db.String(100))
+    os: Column[String] = db.Column(db.String(100))
+    hostname: Column[String] = db.Column(db.String(100))
+    critical_asset: Column[Boolean] = db.Column(db.Boolean, default=False)
+    last_seen: Column[DateTime] = db.Column(db.DateTime)
+
+    def __init__(
+        self,
+        agent_id: str,
+        ip_address: str,
+        os: str,
+        hostname: str,
+        critical_asset: bool,
+        last_seen: datetime,
+    ):
+        """
+        Initialize a new instance of the AgentMetadata class.
+
+        :param agent_id: Unique ID for the agent.
+        :param ip_address: IP address of the agent.
+        :param os: Operating system of the agent.
+        :param hostname: Hostname of the agent.
+        :param critical_asset: Boolean value indicating if the agent is a critical asset.
+        :param last_seen: Timestamp of when the agent was last seen.
+        """
+        self.agent_id = agent_id
+        self.ip_address = ip_address
+        self.os = os
+        self.hostname = hostname
+        self.critical_asset = critical_asset
+        self.last_seen = last_seen
+
+    def __repr__(self) -> str:
+        """
+        Returns a string representation of the AgentMetadata instance.
+
+        :return: A string representation of the agent ID.
+        """
+        return f"<AgentMetadata {self.agent_id}>"
+
+    def mark_as_critical(self):
+        """
+        Marks the agent as a critical asset.
+        """
+        self.critical_asset = True
+        db.session.commit()
+
+    def mark_as_non_critical(self):
+        """
+        Marks the agent as a non-critical asset.
+        """
+        self.critical_asset = False
+        db.session.commit()
+
+    def commit_wazuh_agent_to_db(self):
+        """
+        Commits the agent to the database.
+        """
+        db.session.add(self)
+        db.session.commit()
+
+
+ + + +
+ + + + + + + + + +
+ + + +

+__init__(agent_id, ip_address, os, hostname, critical_asset, last_seen) + +

+ + +
+ +

Initialize a new instance of the AgentMetadata class.

+

:param agent_id: Unique ID for the agent. +:param ip_address: IP address of the agent. +:param os: Operating system of the agent. +:param hostname: Hostname of the agent. +:param critical_asset: Boolean value indicating if the agent is a critical asset. +:param last_seen: Timestamp of when the agent was last seen.

+ +
+ Source code in app\models\agents.py +
28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
def __init__(
+    self,
+    agent_id: str,
+    ip_address: str,
+    os: str,
+    hostname: str,
+    critical_asset: bool,
+    last_seen: datetime,
+):
+    """
+    Initialize a new instance of the AgentMetadata class.
+
+    :param agent_id: Unique ID for the agent.
+    :param ip_address: IP address of the agent.
+    :param os: Operating system of the agent.
+    :param hostname: Hostname of the agent.
+    :param critical_asset: Boolean value indicating if the agent is a critical asset.
+    :param last_seen: Timestamp of when the agent was last seen.
+    """
+    self.agent_id = agent_id
+    self.ip_address = ip_address
+    self.os = os
+    self.hostname = hostname
+    self.critical_asset = critical_asset
+    self.last_seen = last_seen
+
+
+
+ +
+ +
+ + + +

+__repr__() + +

+ + +
+ +

Returns a string representation of the AgentMetadata instance.

+

:return: A string representation of the agent ID.

+ +
+ Source code in app\models\agents.py +
54
+55
+56
+57
+58
+59
+60
def __repr__(self) -> str:
+    """
+    Returns a string representation of the AgentMetadata instance.
+
+    :return: A string representation of the agent ID.
+    """
+    return f"<AgentMetadata {self.agent_id}>"
+
+
+
+ +
+ +
+ + + +

+commit_wazuh_agent_to_db() + +

+ + +
+ +

Commits the agent to the database.

+ +
+ Source code in app\models\agents.py +
76
+77
+78
+79
+80
+81
def commit_wazuh_agent_to_db(self):
+    """
+    Commits the agent to the database.
+    """
+    db.session.add(self)
+    db.session.commit()
+
+
+
+ +
+ +
+ + + +

+mark_as_critical() + +

+ + +
+ +

Marks the agent as a critical asset.

+ +
+ Source code in app\models\agents.py +
62
+63
+64
+65
+66
+67
def mark_as_critical(self):
+    """
+    Marks the agent as a critical asset.
+    """
+    self.critical_asset = True
+    db.session.commit()
+
+
+
+ +
+ +
+ + + +

+mark_as_non_critical() + +

+ + +
+ +

Marks the agent as a non-critical asset.

+ +
+ Source code in app\models\agents.py +
69
+70
+71
+72
+73
+74
def mark_as_non_critical(self):
+    """
+    Marks the agent as a non-critical asset.
+    """
+    self.critical_asset = False
+    db.session.commit()
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +

+ AgentMetadataSchema + + +

+ + +
+

+ Bases: ma.Schema

+ + +

Schema for serializing and deserializing instances of the AgentMetadata class.

+ + +
+ Source code in app\models\agents.py +
class AgentMetadataSchema(ma.Schema):
+    """
+    Schema for serializing and deserializing instances of the AgentMetadata class.
+    """
+
+    class Meta:
+        """
+        Meta class defines the fields to be serialized/deserialized.
+        """
+
+        fields: tuple = (
+            "id",
+            "agent_id",
+            "ip_address",
+            "os",
+            "hostname",
+            "critical_asset",
+            "last_seen",
+        )
+
+
+ + + +
+ + + + + + + + +
+ + + +

+ Meta + + +

+ + +
+ + +

Meta class defines the fields to be serialized/deserialized.

+ + +
+ Source code in app\models\agents.py +
class Meta:
+    """
+    Meta class defines the fields to be serialized/deserialized.
+    """
+
+    fields: tuple = (
+        "id",
+        "agent_id",
+        "ip_address",
+        "os",
+        "hostname",
+        "critical_asset",
+        "last_seen",
+    )
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + +
+ +
+ +


+

Wazuh-Manager Routes

+ + +
+ + + +
+ +

Endpoint to synchronize all agents.

+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the updated information of all synchronized agents.

+
+
+ +
+ Source code in app\routes\agents.py +
69
+70
+71
+72
+73
+74
+75
+76
+77
+78
@bp.route("/agents/sync", methods=["POST"])
+def sync_agents() -> Any:
+    """
+    Endpoint to synchronize all agents.
+    Returns:
+        json: A JSON response containing the updated information of all synchronized agents.
+    """
+    service = AgentSyncService()
+    result = service.sync_agents()
+    return jsonify(result)
+
+
+
+ +
+ +
+ + + +
+ +

Endpoint to delete an agent.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent to be deleted.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response indicating whether the deletion was successful.

+
+
+ +
+ Source code in app\routes\agents.py +
81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
@bp.route("/agents/<agent_id>/delete", methods=["POST"])
+def delete_agent(agent_id: str) -> Any:
+    """
+    Endpoint to delete an agent.
+    Args:
+        agent_id (str): The ID of the agent to be deleted.
+    Returns:
+        json: A JSON response indicating whether the deletion was successful.
+    """
+    service = AgentService()
+    result = service.delete_agent_db(agent_id=agent_id)
+
+    universal_service = UniversalService()
+    agent_service = WazuhManagerAgentService(universal_service)
+    agent_service.delete_agent(agent_id=agent_id)
+
+    return result
+
+
+
+ +
+ +
+ + + +
+ +

Endpoint to get the vulnerabilities of a specific agent.

+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
agent_id + str + +
+

The ID of the agent whose vulnerabilities are to be fetched.

+
+
+ required +
+ +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
json + Any + +
+

A JSON response containing the vulnerabilities of the agent.

+
+
+ +
+ Source code in app\routes\agents.py +
@bp.route("/agents/<agent_id>/vulnerabilities", methods=["GET"])
+def get_agent_vulnerabilities(agent_id: str) -> Any:
+    """
+    Endpoint to get the vulnerabilities of a specific agent.
+    Args:
+        agent_id (str): The ID of the agent whose vulnerabilities are to be fetched.
+    Returns:
+        json: A JSON response containing the vulnerabilities of the agent.
+    """
+    universal_service = UniversalService()
+    vulnerability_service = VulnerabilityService(universal_service)
+
+    agent_vulnerabilities = vulnerability_service.agent_vulnerabilities(
+        agent_id=agent_id,
+    )
+    return agent_vulnerabilities
+
+
+
+ +


+

Wazuh-Manager Services

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file