diff --git a/backend/app/incidents/routes/incident_alert.py b/backend/app/incidents/routes/incident_alert.py index 3ef7fdf5..60197eb3 100644 --- a/backend/app/incidents/routes/incident_alert.py +++ b/backend/app/incidents/routes/incident_alert.py @@ -101,7 +101,7 @@ class AlertTimelineResponse(BaseModel): The response object containing the detai description="Manually create an incident alert in CoPilot", dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], ) -async def create_alert_route( +async def create_alert_manual_route( create_alert_request: CreateAlertRequest, session: AsyncSession = Depends(get_db), ) -> CreateAlertResponse: diff --git a/backend/app/incidents/services/incident_alert.py b/backend/app/incidents/services/incident_alert.py index 42c6269b..5c26568a 100644 --- a/backend/app/incidents/services/incident_alert.py +++ b/backend/app/incidents/services/incident_alert.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from typing import Any from typing import Dict -from typing import List +from typing import List, Optional from fastapi import HTTPException from loguru import logger @@ -206,6 +206,70 @@ async def get_customer_code(alert_details: dict): ) +async def add_alert_to_document( + alert: CreateAlertRequest, + soc_alert_id: int, +) -> Optional[str]: + """ + Update the alert document in Elasticsearch with the provided SOC alert ID URL. + + Parameters: + - es_client: The Elasticsearch client instance to use for the update. + - alert: The alert request object containing alert_id and index_name. + - soc_alert_id: The alert ID as it exists within IRIS. + - session: The database session for retrieving connector information. + + Returns: + - True if the update is successful, False otherwise. + """ + es_client = await create_wazuh_indexer_client("Wazuh-Indexer") + try: + es_client.update( + index=alert.index_name, + id=alert.alert_id, + body={"doc": {"alert_id": soc_alert_id}}, + ) + logger.info( + f"Added alert ID {soc_alert_id} to alert {alert.alert_id} in index {alert.index_name}", + ) + return soc_alert_id + except Exception as e: + logger.error( + f"Failed to add alert ID {soc_alert_id} to alert {alert.alert_id} in index {alert.index_name}: {e}", + ) + # Attempt to remove read-only block + try: + es_client.indices.put_settings( + index=alert.index_name, + body={"index.blocks.write": None}, + ) + logger.info( + f"Removed read-only block from index {alert.index_name}. Retrying update.", + ) + + # Retry the update operation + es_client.update( + index=alert.index_name, + id=alert.alert_id, + body={"doc": {"alert_id": soc_alert_id}}, + ) + logger.info( + f"Added alert ID {soc_alert_id} to alert {alert.alert_id} in index {alert.index_name} after removing read-only block", + ) + + # Reenable the write block + es_client.indices.put_settings( + index=alert.index_name, + body={"index.blocks.write": True}, + ) + return soc_alert_id + except Exception as e2: + logger.error( + f"Failed to remove read-only block from index {alert.index_name}: {e2}", + ) + return False + + async def retrieve_agent_details_from_db(agent_name: str, session: AsyncSession): """ Retrieve agent details from the database. @@ -372,6 +436,8 @@ async def create_alert_full(alert_payload: CreatedAlertPayload, customer_code: s logger.info(f"Creating alert for customer code {customer_code} with alert context ID {alert_context_id} and asset ID {asset_id}") await handle_customer_notifications(customer_code, alert_payload, session) + await add_alert_to_document(CreateAlertRequest(index_name=alert_payload.index_name, alert_id=alert_payload.index_id), alert_id) + return alert_id @@ -579,6 +645,7 @@ async def create_alert( logger.info( f"Open alert exists for customer code {customer_code} with alert title {alert_payload.alert_title_payload} and alert ID {existing_alert}", ) + await add_alert_to_document(CreateAlertRequest(index_name=alert_payload.index_name, alert_id=alert_payload.index_id), existing_alert) await add_asset_to_copilot_alert(alert_payload, existing_alert, customer_code, session) return existing_alert return await create_alert_full(alert_payload, customer_code, session) diff --git a/backend/app/integrations/alert_escalation/routes/escalate_alert.py b/backend/app/integrations/alert_escalation/routes/escalate_alert.py index e98ff574..9656387b 100644 --- a/backend/app/integrations/alert_escalation/routes/escalate_alert.py +++ b/backend/app/integrations/alert_escalation/routes/escalate_alert.py @@ -9,6 +9,7 @@ from app.integrations.alert_escalation.schema.escalate_alert import CreateAlertRequest from app.integrations.alert_escalation.schema.escalate_alert import CreateAlertResponse from app.integrations.alert_escalation.services.escalate_alert import create_alert +from app.incidents.routes.incident_alert import create_alert_manual_route integration_escalate_alerts_router = APIRouter() @@ -16,7 +17,7 @@ @integration_escalate_alerts_router.post( "/create", response_model=CreateAlertResponse, - description="Manually create an alert in IRIS from Copilot WebUI", + description="Manually create an alert in CoPilot from Copilot WebUI", dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))], ) async def create_alert_route( @@ -24,7 +25,7 @@ async def create_alert_route( session: AsyncSession = Depends(get_db), ) -> CreateAlertResponse: """ - Create an alert in IRIS. Manually create an alert in IRIS from Copilot WebUI. + Create an alert in CoPilot. Manually create an alert in CoPilot from Copilot WebUI. Args: create_alert_request (CreateAlertRequest): The request object containing the details of the alert to be created. @@ -33,5 +34,6 @@ async def create_alert_route( Returns: CreateAlertResponse: The response object containing the result of the alert creation. """ - logger.info(f"Creating alert {create_alert_request.alert_id} in IRIS") - return await create_alert(create_alert_request, session) + logger.info(f"Creating alert {create_alert_request.alert_id} in CoPilot") + #return await create_alert(create_alert_request, session) + return await create_alert_manual_route(create_alert_request, session) diff --git a/backend/app/integrations/alert_escalation/routes/general_alert.py b/backend/app/integrations/alert_escalation/routes/general_alert.py index 66734140..ba27754c 100644 --- a/backend/app/integrations/alert_escalation/routes/general_alert.py +++ b/backend/app/integrations/alert_escalation/routes/general_alert.py @@ -9,10 +9,11 @@ from app.integrations.alert_escalation.schema.general_alert import CreateAlertRequest from app.integrations.alert_escalation.schema.general_alert import CreateAlertResponse from app.integrations.alert_escalation.services.general_alert import create_alert +from app.incidents.routes.incident_alert import create_alert_manual_route integration_general_alerts_router = APIRouter() - +# ! TODO: Remove this route and file after the new route is implemented ! # @integration_general_alerts_router.post( "/create", response_model=CreateAlertResponse, @@ -35,4 +36,6 @@ async def create_alert_route( CreateAlertResponse: The response object containing the result of the alert creation. """ logger.info(f"Creating alert {create_alert_request.alert_id} in IRIS") - return await create_alert(create_alert_request, session) + return None + #return await create_alert(create_alert_request, session) + return await create_alert_manual_route(create_alert_request, session) diff --git a/backend/app/integrations/alert_escalation/schema/escalate_alert.py b/backend/app/integrations/alert_escalation/schema/escalate_alert.py index d1c6eeb4..25979bfd 100644 --- a/backend/app/integrations/alert_escalation/schema/escalate_alert.py +++ b/backend/app/integrations/alert_escalation/schema/escalate_alert.py @@ -46,7 +46,7 @@ class CreateAlertResponse(BaseModel): success: bool message: str alert_id: int = Field(..., description="The alert id as created in IRIS.") - alert_url: str = Field(..., description="The alert url as created in IRIS.") + alert_url: Optional[str] = Field(None, description="The alert url as created in IRIS.") class GenericSourceModel(BaseModel): diff --git a/backend/app/integrations/alert_escalation/schema/general_alert.py b/backend/app/integrations/alert_escalation/schema/general_alert.py index e0bac64e..d9b9c609 100644 --- a/backend/app/integrations/alert_escalation/schema/general_alert.py +++ b/backend/app/integrations/alert_escalation/schema/general_alert.py @@ -27,7 +27,7 @@ class CreateAlertResponse(BaseModel): success: bool message: str alert_id: int = Field(..., description="The alert id as created in IRIS.") - alert_url: str = Field(..., description="The alert url as created in IRIS.") + alert_url: Optional[str] = Field(None, description="The alert url as created in IRIS.") class GenericSourceModel(BaseModel):