@cryptotaxi247 / CoPilot / commits / 59728170

Alerts page (#102)

* added alertsSummary page * updated alertsSummary page * updated alertsSummary page * updated alertsSummary page * updated alertsSummary page * auth to customers routes * middleware logging to db split into own directory * exception handler sep module * restructure routes for cleaner main file * apscheduler addition * updated alert component * updated alertsSummary page * async db * agent routes * requirements * updated agent page * add alert_url to alert document * updated alertsSummary page --------- Co-authored-by: Davide Di Modica <webmaster.ddm@gmail.com>

taylor_socfortress committed Nov 9, 2023 at 07:03 UTC 59728170f855c4c4adc1ebfb01402a33c5ec42b5
144 files changed +6875 -1587
backend/app/agents/routes/agents.py
+128 -63
@@ -1,8 +1,13 @@
1 +import requests
2 +from celery.result import AsyncResult
3 from fastapi import APIRouter
4 from fastapi import BackgroundTasks
5 +from fastapi import Depends
6 from fastapi import HTTPException
7 from fastapi import Security
8 from loguru import logger
9 +from sqlalchemy.ext.asyncio import AsyncSession
10 +from sqlalchemy.future import select
11 from starlette.status import HTTP_401_UNAUTHORIZED
12
13 from app.agents.schema.agents import AgentModifyResponse
@@ -12,7 +17,6 @@ from app.agents.schema.agents import AgentUpdateCustomerCodeResponse
17 from app.agents.schema.agents import OutdatedVelociraptorAgentsResponse
18 from app.agents.schema.agents import OutdatedWazuhAgentsResponse
19 from app.agents.schema.agents import SyncedAgentsResponse
15 -from app.agents.services.modify import mark_agent_criticality
20 from app.agents.services.status import get_outdated_agents_velociraptor
21 from app.agents.services.status import get_outdated_agents_wazuh
22 from app.agents.services.sync import sync_agents
@@ -23,41 +27,65 @@ from app.agents.wazuh.services.vulnerabilities import collect_agent_vulnerabilit
27
28 # App specific imports
29 from app.auth.routes.auth import AuthHandler
30 +from app.db.db_session import get_session
31
32 # App specific imports
28 -from app.db.db_session import session
33 +# from app.db.db_session import session
34 from app.db.universal_models import Agents
35
36 agents_router = APIRouter()
37
38 +# ! OLD
39 +# def fetch_velociraptor_id(agent_id: str) -> str:
40 +# try:
41 +# return session.query(Agents).filter(Agents.agent_id == agent_id).first().velociraptor_id
42 +# except Exception as e:
43 +# logger.error(f"Failed to fetch agent {agent_id} from database: {e}")
44 +# raise HTTPException(status_code=500, detail=f"Failed to fetch agent {agent_id} from database: {e}")
45
34 -def fetch_velociraptor_id(agent_id: str) -> str:
46 +
47 +# def delete_agent_from_database(agent_id: str):
48 +# try:
49 +# session.query(Agents).filter(Agents.agent_id == agent_id).delete()
50 +# session.commit()
51 +# except Exception as e:
52 +# logger.error(f"Failed to delete agent {agent_id} from database: {e}")
53 +# raise HTTPException(status_code=500, detail=f"Failed to delete agent {agent_id} from database: {e}")
54 +async def fetch_velociraptor_id(db: AsyncSession, agent_id: str) -> str:
55 try:
36 - return session.query(Agents).filter(Agents.agent_id == agent_id).first().velociraptor_id
56 + result = await db.execute(select(Agents).filter(Agents.agent_id == agent_id))
57 + agent = result.scalars().first()
58 + if agent:
59 + return agent.velociraptor_id
60 + else:
61 + raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
62 except Exception as e:
63 logger.error(f"Failed to fetch agent {agent_id} from database: {e}")
64 raise HTTPException(status_code=500, detail=f"Failed to fetch agent {agent_id} from database: {e}")
65
66
42 -def delete_agent_from_database(agent_id: str):
67 +async def delete_agent_from_database(db: AsyncSession, agent_id: str):
68 try:
44 - session.query(Agents).filter(Agents.agent_id == agent_id).delete()
45 - session.commit()
69 + await db.execute(select(Agents).filter(Agents.agent_id == agent_id).delete())
70 + await db.commit()
71 except Exception as e:
72 logger.error(f"Failed to delete agent {agent_id} from database: {e}")
73 + await db.rollback()
74 raise HTTPException(status_code=500, detail=f"Failed to delete agent {agent_id} from database: {e}")
75
76
77 @agents_router.get(
78 "",
79 response_model=AgentsResponse,
54 - description="Get all disabled rules",
80 + description="Get all agents currently synced to the database",
81 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
82 )
57 -async def get_agents() -> AgentsResponse:
83 +async def get_agents(db: AsyncSession = Depends(get_session)) -> AgentsResponse:
84 logger.info("Fetching all agents")
85 try:
60 - agents = session.query(Agents).all()
86 + # agents = session.query(Agents).all()
87 + result = await db.execute(select(Agents))
88 + agents = result.scalars().all()
89 return AgentsResponse(agents=agents, success=True, message="Agents fetched successfully")
90 except Exception as e:
91 logger.error(f"Failed to fetch agents: {e}")
@@ -70,14 +98,17 @@ async def get_agents() -> AgentsResponse:
98 description="Get agent by agent_id",
99 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
100 )
73 -async def get_agent(agent_id: str) -> AgentsResponse:
101 +async def get_agent(agent_id: str, db: AsyncSession = Depends(get_session)) -> AgentsResponse:
102 logger.info(f"Fetching agent with agent_id: {agent_id}")
103 try:
76 - agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
77 - return AgentsResponse(agents=[agent], success=True, message="Agent fetched successfully")
78 - except Exception as e:
79 - if not agent:
104 + result = await db.execute(select(Agents).filter(Agents.agent_id == agent_id))
105 + agent = result.scalars().first()
106 + if agent:
107 + return AgentsResponse(agents=[agent], success=True, message="Agent fetched successfully")
108 + else:
109 raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
110 + except Exception as e:
111 + logger.error(f"Failed to fetch agent: {e}")
112 raise HTTPException(status_code=500, detail=f"Failed to fetch agent: {e}")
113
114
@@ -87,15 +118,19 @@ async def get_agent(agent_id: str) -> AgentsResponse:
118 description="Get agent by hostname",
119 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
120 )
90 -async def get_agent_by_hostname(hostname: str) -> AgentsResponse:
121 +async def get_agent_by_hostname(hostname: str, db: AsyncSession = Depends(get_session)) -> AgentsResponse:
122 logger.info(f"Fetching agent with hostname: {hostname}")
123 try:
93 - agent = session.query(Agents).filter(Agents.hostname == hostname).first()
94 - return AgentsResponse(agents=[agent], success=True, message="Agent fetched successfully")
95 - except Exception as e:
96 - if not agent:
124 + result = await db.execute(select(Agents).filter(Agents.hostname == hostname))
125 + agent = result.scalars().first()
126 + if agent:
127 + return AgentsResponse(agents=[agent], success=True, message="Agent fetched successfully")
128 + else:
129 raise HTTPException(status_code=404, detail=f"Agent with hostname {hostname} not found")
98 - raise HTTPException(status_code=500, detail=f"Failed to fetch agent: {e}")
130 + except Exception as e:
131 + logger.error(f"Failed to fetch agent: {e}")
132 + # The exception message should not be exposed directly, especially in production
133 + raise HTTPException(status_code=500, detail="Failed to fetch agent")
134
135
136 @agents_router.post(
@@ -104,9 +139,9 @@ async def get_agent_by_hostname(hostname: str) -> AgentsResponse:
139 description="Sync agents from Wazuh Manager",
140 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
141 )
107 -async def sync_all_agents(backgroud_tasks: BackgroundTasks) -> SyncedAgentsResponse:
142 +async def sync_all_agents(backgroud_tasks: BackgroundTasks, session: AsyncSession = Depends(get_session)) -> SyncedAgentsResponse:
143 logger.info("Syncing agents from Wazuh Manager")
109 - backgroud_tasks.add_task(sync_agents)
144 + backgroud_tasks.add_task(sync_agents, session)
145 # return sync_agents()
146 return SyncedAgentsResponse(success=True, message="Agents synced started successfully")
147
@@ -117,9 +152,24 @@ async def sync_all_agents(backgroud_tasks: BackgroundTasks) -> SyncedAgentsRespo
152 description="Mark agent as critical",
153 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
154 )
120 -async def mark_agent_as_critical(agent_id: str) -> AgentModifyResponse:
155 +async def mark_agent_as_critical(agent_id: str, session: AsyncSession = Depends(get_session)) -> AgentModifyResponse:
156 logger.info(f"Marking agent {agent_id} as critical")
122 - return mark_agent_criticality(agent_id, True)
157 + # return mark_agent_criticality(agent_id, True)
158 + try:
159 + # Asynchronously fetch the agent by id
160 + result = await session.execute(select(Agents).filter(Agents.agent_id == agent_id))
161 + agent = result.scalars().first()
162 +
163 + if not agent:
164 + raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
165 +
166 + agent.critical_asset = True
167 + await session.commit()
168 +
169 + return AgentModifyResponse(success=True, message=f"Agent {agent_id} marked as critical: {True}")
170 + except Exception as e:
171 + session.rollback() # Roll back the session in case of error
172 + raise HTTPException(status_code=500, detail=f"Failed to mark agent as critical: {str(e)}")
173
174
175 @agents_router.post(
@@ -128,9 +178,23 @@ async def mark_agent_as_critical(agent_id: str) -> AgentModifyResponse:
178 description="Mark agent as not critical",
179 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
180 )
131 -async def mark_agent_as_not_critical(agent_id: str) -> AgentModifyResponse:
181 +async def mark_agent_as_not_critical(agent_id: str, session: AsyncSession = Depends(get_session)) -> AgentModifyResponse:
182 logger.info(f"Marking agent {agent_id} as not critical")
133 - return mark_agent_criticality(agent_id, False)
183 + try:
184 + # Asynchronously fetch the agent by id
185 + result = await session.execute(select(Agents).filter(Agents.agent_id == agent_id))
186 + agent = result.scalars().first()
187 +
188 + if not agent:
189 + raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
190 +
191 + agent.critical_asset = False
192 + await session.commit()
193 +
194 + return AgentModifyResponse(success=True, message=f"Agent {agent_id} marked as not critical")
195 + except Exception as e:
196 + await session.rollback() # Roll back the session in case of error
197 + raise HTTPException(status_code=500, detail=f"Failed to mark agent as not critical: {str(e)}")
198
199
200 @agents_router.get(
@@ -141,7 +205,7 @@ async def mark_agent_as_not_critical(agent_id: str) -> AgentModifyResponse:
205 )
206 async def get_agent_vulnerabilities(agent_id: str) -> WazuhAgentVulnerabilitiesResponse:
207 logger.info(f"Fetching agent {agent_id} vulnerabilities")
144 - return collect_agent_vulnerabilities(agent_id)
208 + return await collect_agent_vulnerabilities(agent_id)
209
210
211 @agents_router.get(
@@ -150,9 +214,9 @@ async def get_agent_vulnerabilities(agent_id: str) -> WazuhAgentVulnerabilitiesR
214 description="Get all outdated Wazuh agents",
215 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
216 )
153 -async def get_outdated_wazuh_agents() -> OutdatedWazuhAgentsResponse:
217 +async def get_outdated_wazuh_agents(session: AsyncSession = Depends(get_session)) -> OutdatedWazuhAgentsResponse:
218 logger.info("Fetching all outdated Wazuh agents")
155 - return get_outdated_agents_wazuh()
219 + return await get_outdated_agents_wazuh(session)
220
221
222 @agents_router.get(
@@ -161,39 +225,40 @@ async def get_outdated_wazuh_agents() -> OutdatedWazuhAgentsResponse:
225 description="Get all outdated Velociraptor agents",
226 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
227 )
164 -async def get_outdated_velociraptor_agents() -> OutdatedVelociraptorAgentsResponse:
228 +async def get_outdated_velociraptor_agents(session: AsyncSession = Depends(get_session)) -> OutdatedVelociraptorAgentsResponse:
229 logger.info("Fetching all outdated Velociraptor agents")
166 - return get_outdated_agents_velociraptor()
230 + return await get_outdated_agents_velociraptor(session)
231
232
169 -@agents_router.delete(
170 - "/{agent_id}/delete",
171 - response_model=AgentModifyResponse,
172 - description="Delete agent",
173 - dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
174 -)
175 -async def delete_agent(agent_id: str) -> AgentModifyResponse:
176 - logger.info(f"Deleting agent {agent_id}")
177 - delete_agent_wazuh(agent_id)
178 - client_id = fetch_velociraptor_id(agent_id)
179 - delete_agent_velociraptor(client_id)
180 - delete_agent_from_database(agent_id)
181 - return {"success": True, "message": f"Agent {agent_id} deleted from database, Wazuh, and Velociraptor"}
182 -
183 -
184 -@agents_router.put(
185 - "/{agent_id}/update-customer-code",
186 - response_model=AgentUpdateCustomerCodeResponse,
187 - description="Update `agent` customer code",
188 - dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
189 -)
190 -async def update_agent_customer_code(agent_id: str, body: AgentUpdateCustomerCodeBody) -> AgentUpdateCustomerCodeResponse:
191 - logger.info(f"Updating agent {agent_id} customer code to {body.customer_code}")
192 - try:
193 - agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
194 - agent.customer_code = body.customer_code
195 - session.commit()
196 - return {"success": True, "message": f"Agent {agent_id} customer code updated to {body.customer_code}"}
197 - except Exception as e:
198 - if not agent:
199 - raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
233 +# ! TODO: FINISH THIS
234 +# @agents_router.delete(
235 +# "/{agent_id}/delete",
236 +# response_model=AgentModifyResponse,
237 +# description="Delete agent",
238 +# dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
239 +# )
240 +# async def delete_agent(agent_id: str) -> AgentModifyResponse:
241 +# logger.info(f"Deleting agent {agent_id}")
242 +# delete_agent_wazuh(agent_id)
243 +# client_id = fetch_velociraptor_id(agent_id)
244 +# delete_agent_velociraptor(client_id)
245 +# delete_agent_from_database(agent_id)
246 +# return {"success": True, "message": f"Agent {agent_id} deleted from database, Wazuh, and Velociraptor"}
247 +
248 +
249 +# @agents_router.put(
250 +# "/{agent_id}/update-customer-code",
251 +# response_model=AgentUpdateCustomerCodeResponse,
252 +# description="Update `agent` customer code",
253 +# dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
254 +# )
255 +# async def update_agent_customer_code(agent_id: str, body: AgentUpdateCustomerCodeBody) -> AgentUpdateCustomerCodeResponse:
256 +# logger.info(f"Updating agent {agent_id} customer code to {body.customer_code}")
257 +# try:
258 +# agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
259 +# agent.customer_code = body.customer_code
260 +# session.commit()
261 +# return {"success": True, "message": f"Agent {agent_id} customer code updated to {body.customer_code}"}
262 +# except Exception as e:
263 +# if not agent:
264 +# raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
backend/app/agents/services/modify.py
-13
@@ -5,19 +5,6 @@ from app.db.db_session import session
5 from app.db.universal_models import Agents
6
7
8 -def mark_agent_criticality(agent_id: str, critical: bool):
9 - """Mark agent as critical or not critical."""
10 - try:
11 - agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
12 - agent.critical_asset = critical
13 - session.commit()
14 - return {"success": True, "message": f"Agent {agent_id} marked as critical: {critical}"}
15 - except Exception as e:
16 - if not agent:
17 - raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
18 - raise HTTPException(status_code=500, detail=f"Failed to mark agent as critical: {e}")
19 -
20 -
8 def delete_agent_db(agent_id: str):
9 """Delete agent from database."""
10 agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
backend/app/agents/services/status.py
+43 -27
@@ -2,6 +2,8 @@ from typing import List
2
3 from fastapi import HTTPException
4 from loguru import logger
5 +from sqlalchemy.ext.asyncio import AsyncSession
6 +from sqlalchemy.future import select
7
8 from app.agents.schema.agents import OutdatedVelociraptorAgentsResponse
9 from app.agents.schema.agents import OutdatedWazuhAgentsResponse
@@ -27,48 +29,62 @@ def get_agent(agent_id: str) -> List[Agents]:
29 raise HTTPException(status_code=500, detail=f"Failed to fetch agent with agent_id {agent_id}: {e}")
30
31
30 -def get_outdated_agents_wazuh() -> OutdatedWazuhAgentsResponse:
32 +async def get_outdated_agents_wazuh(session: AsyncSession) -> OutdatedWazuhAgentsResponse:
33 """
32 - Retrieves all agents with outdated Wazuh agent versions from the database.
34 + Retrieves all agents with outdated Wazuh agent versions from the database asynchronously.
35 +
36 + Args:
37 + session (AsyncSession): The SQLAlchemy asynchronous session to use for the query.
38
39 Returns:
35 - List[dict]: A list of dictionaries where each dictionary represents the serialized data of an outdated agent.
40 + OutdatedWazuhAgentsResponse: Response object containing the outdated agents.
41 """
37 - wazuh_manager = get_agent("000")
38 - if wazuh_manager is None:
39 - logger.error("Wazuh Manager with agent_id '000' not found.")
40 - raise HTTPException(status_code=404, detail="Wazuh Manager with agent_id '000' not found.")
42 try:
42 - outdated_wazuh_agents = (
43 - session.query(Agents).filter(Agents.agent_id != "000", Agents.wazuh_agent_version != wazuh_manager.wazuh_agent_version).all()
43 + wazuh_manager_result = await session.execute(select(Agents).filter(Agents.agent_id == "000"))
44 + wazuh_manager = wazuh_manager_result.scalars().first()
45 +
46 + if wazuh_manager is None:
47 + logger.error("Wazuh Manager with agent_id '000' not found.")
48 + raise HTTPException(status_code=404, detail="Wazuh Manager with agent_id '000' not found.")
49 +
50 + outdated_agents_result = await session.execute(
51 + select(Agents).filter(Agents.agent_id != "000", Agents.wazuh_agent_version != wazuh_manager.wazuh_agent_version),
52 + )
53 + outdated_wazuh_agents = outdated_agents_result.scalars().all()
54 +
55 + return OutdatedWazuhAgentsResponse(
56 + message="Outdated Wazuh agents fetched successfully.",
57 + success=True,
58 + outdated_wazuh_agents=outdated_wazuh_agents,
59 )
45 - return {"message": "Outdated Wazuh agents fetched successfully.", "success": True, "outdated_wazuh_agents": outdated_wazuh_agents}
60 except Exception as e:
61 raise HTTPException(status_code=500, detail=f"Failed to fetch outdated Wazuh agents: {e}")
62
63
50 -def get_outdated_agents_velociraptor() -> OutdatedVelociraptorAgentsResponse:
64 +async def get_outdated_agents_velociraptor(session: AsyncSession) -> OutdatedVelociraptorAgentsResponse:
65 """
52 - Retrieves all agents with outdated Velociraptor client versions from the database.
66 + Retrieves all agents with outdated Velociraptor client versions from the database asynchronously.
67 +
68 + Args:
69 + session (AsyncSession): The SQLAlchemy asynchronous session to use for the query.
70
71 Returns:
55 - List[dict]: A list of dictionaries where each dictionary represents the serialized data of an outdated agent.
72 + OutdatedVelociraptorAgentsResponse: Response object containing the outdated agents.
73 """
57 - outdated_velociraptor_agents = []
74 vql_server_version = "select * from config"
59 - server_version = UniversalService()._get_server_version(vql_server_version)
60 - try:
61 - agents = session.query(Agents).all()
62 - except Exception as e:
63 - raise HTTPException(status_code=500, detail=f"Failed to fetch agents: {e}")
75 + velociraptor_service = await UniversalService().create("Velociraptor")
76 +
77 try:
65 - for agent in agents:
66 - if agent.velociraptor_agent_version != server_version:
67 - outdated_velociraptor_agents.append(agent)
68 - return {
69 - "message": "Outdated Velociraptor agents fetched successfully.",
70 - "success": True,
71 - "outdated_velociraptor_agents": outdated_velociraptor_agents,
72 - }
78 + # Assuming _get_server_version is an async function
79 + server_version = await velociraptor_service._get_server_version(vql_server_version)
80 + agents_result = await session.execute(select(Agents))
81 + agents = agents_result.scalars().all()
82 + outdated_velociraptor_agents = [agent for agent in agents if agent.velociraptor_agent_version != server_version]
83 +
84 + return OutdatedVelociraptorAgentsResponse(
85 + message="Outdated Velociraptor agents fetched successfully.",
86 + success=True,
87 + outdated_velociraptor_agents=outdated_velociraptor_agents,
88 + )
89 except Exception as e:
90 raise HTTPException(status_code=500, detail=f"Failed to fetch outdated Velociraptor agents: {e}")
backend/app/agents/services/sync.py
+25 -15
@@ -1,6 +1,8 @@
1 from typing import List
2
3 from loguru import logger
4 +from sqlalchemy.ext.asyncio import AsyncSession
5 +from sqlalchemy.future import select
6
7 import app.agents.velociraptor.services.agents as velociraptor_services
8 import app.agents.wazuh.services.agents as wazuh_services
@@ -9,13 +11,12 @@ from app.agents.schema.agents import SyncedAgentsResponse
11 from app.agents.velociraptor.schema.agents import VelociraptorAgent
12 from app.agents.wazuh.schema.agents import WazuhAgent
13 from app.agents.wazuh.schema.agents import WazuhAgentsList
12 -from app.db.db_session import session
14 from app.db.universal_models import Agents
15
16
16 -def fetch_wazuh_agents() -> WazuhAgentsList:
17 +async def fetch_wazuh_agents() -> WazuhAgentsList:
18 """Fetch agents from Wazuh service."""
18 - collected_wazuh_agents = wazuh_services.collect_wazuh_agents()
19 + collected_wazuh_agents = await wazuh_services.collect_wazuh_agents()
20 return WazuhAgentsList(
21 agents=collected_wazuh_agents.agents,
22 success=collected_wazuh_agents.success,
@@ -23,23 +24,29 @@ def fetch_wazuh_agents() -> WazuhAgentsList:
24 )
25
26
26 -def fetch_velociraptor_agent(agent_name: str) -> VelociraptorAgent:
27 +async def fetch_velociraptor_agent(agent_name: str) -> VelociraptorAgent:
28 """Fetch agent details from Velociraptor service."""
28 - return velociraptor_services.collect_velociraptor_agent(agent_name)
29 + return await velociraptor_services.collect_velociraptor_agent(agent_name)
30
31
31 -def add_agent_to_db(agent: WazuhAgent, client: VelociraptorAgent, customer_code: str):
32 +async def add_agent_to_db(session: AsyncSession, agent: WazuhAgent, client: VelociraptorAgent, customer_code: str):
33 """Add new agent to database."""
34 new_agent = Agents.create_from_model(agent, client, customer_code)
35 session.add(new_agent)
35 - session.commit()
36 + await session.commit() # Use the await keyword to commit asynchronously
37 logger.info(f"Agent {agent.agent_name} added to the database")
38
39
39 -def update_agent_in_db(existing_agent: Agents, agent: WazuhAgent, client: VelociraptorAgent, customer_code: str):
40 +async def update_agent_in_db(
41 + session: AsyncSession,
42 + existing_agent: Agents,
43 + agent: WazuhAgent,
44 + client: VelociraptorAgent,
45 + customer_code: str,
46 +):
47 """Update existing agent in database."""
48 existing_agent.update_from_model(agent, client, customer_code)
42 - session.commit()
49 + await session.commit() # Use the await keyword to commit asynchronously
50 logger.info(f"Agent {agent.agent_name} updated in the database")
51
52
@@ -49,9 +56,9 @@ def extract_customer_code(customer_code: str):
56 return parts[1] if len(parts) > 1 else None
57
58
52 -def sync_agents() -> SyncedAgentsResponse:
59 +async def sync_agents(session: AsyncSession) -> SyncedAgentsResponse:
60 """Synchronize agents from Wazuh and Velociraptor services."""
54 - wazuh_agents_list = fetch_wazuh_agents()
61 + wazuh_agents_list = await fetch_wazuh_agents()
62 logger.info(f"Collected Wazuh Agents: {wazuh_agents_list}")
63
64 agents_added_list: List[WazuhAgent] = []
@@ -59,16 +66,19 @@ def sync_agents() -> SyncedAgentsResponse:
66 for wazuh_agent in wazuh_agents_list.agents:
67 logger.info(f"Collecting Velociraptor Agent for {wazuh_agent.agent_name}")
68
62 - velociraptor_agent = fetch_velociraptor_agent(wazuh_agent.agent_name)
69 + velociraptor_agent = await fetch_velociraptor_agent(wazuh_agent.agent_name)
70
71 customer_code = extract_customer_code(wazuh_agent.agent_label)
72
66 - existing_agent = session.query(Agents).filter(Agents.hostname == wazuh_agent.agent_name).first()
73 + # Asynchronously fetch the existing agent
74 + existing_agent_query = select(Agents).filter(Agents.hostname == wazuh_agent.agent_name)
75 + result = await session.execute(existing_agent_query)
76 + existing_agent = result.scalars().first()
77
78 if existing_agent:
69 - update_agent_in_db(existing_agent, wazuh_agent, velociraptor_agent, customer_code)
79 + await update_agent_in_db(session, existing_agent, wazuh_agent, velociraptor_agent, customer_code)
80 else:
71 - add_agent_to_db(wazuh_agent, velociraptor_agent, customer_code)
81 + await add_agent_to_db(session, wazuh_agent, velociraptor_agent, customer_code)
82
83 # Combine the wazuh agent and velociraptor agent into one object
84 synced_agent = SyncedAgent(**wazuh_agent.dict(), **velociraptor_agent.dict())
backend/app/agents/velociraptor/services/agents.py
+7 -4
@@ -21,7 +21,7 @@ def create_query(query: str) -> str:
21 return query
22
23
24 -def collect_velociraptor_agent(agent_name: str) -> VelociraptorAgent:
24 +async def collect_velociraptor_agent(agent_name: str) -> VelociraptorAgent:
25 """
26 Retrieves the client ID, last_seen_at and client version based on the agent name from Velociraptor.
27
@@ -33,15 +33,17 @@ def collect_velociraptor_agent(agent_name: str) -> VelociraptorAgent:
33 str: The last seen at timestamp if found, Default timsetamp otherwise.
34 """
35 logger.info(f"Collecting agent {agent_name} from Velociraptor")
36 + velociraptor_service = await UniversalService.create("Velociraptor")
37 try:
37 - client_id = UniversalService().get_client_id(agent_name)["results"][0]["client_id"]
38 + client_id = await velociraptor_service.get_client_id(agent_name)
39 + client_id = client_id["results"][0]["client_id"]
40 except (KeyError, IndexError, TypeError) as e:
41 logger.error(f"Failed to get client ID for {agent_name}. Error: {e}")
42 return VelociraptorAgent(client_id="Unknown", client_last_seen="Unknown", client_version="Unknown")
43
44 try:
45 vql_last_seen_at = f"select last_seen_at from clients(search='host:{agent_name}')"
44 - last_seen_at = UniversalService()._get_last_seen_timestamp(vql_last_seen_at)
46 + last_seen_at = await velociraptor_service._get_last_seen_timestamp(vql_last_seen_at)
47 client_last_seen = datetime.fromtimestamp(
48 int(last_seen_at) / 1000000,
49 ).strftime(
@@ -53,7 +55,8 @@ def collect_velociraptor_agent(agent_name: str) -> VelociraptorAgent:
55
56 try:
57 vql_client_version = f"select * from clients(search='host:{agent_name}')"
56 - client_version = UniversalService()._get_client_version(vql_client_version)
58 + # client_version = UniversalService()._get_client_version(vql_client_version)
59 + client_version = await velociraptor_service._get_client_version(vql_client_version)
60 except Exception as e:
61 logger.error(f"Failed to get client version for {agent_name}. Error: {e}")
62 client_version = "Unknown"
backend/app/agents/wazuh/services/agents.py
+2 -2
@@ -8,9 +8,9 @@ from app.connectors.wazuh_manager.utils.universal import send_delete_request
8 from app.connectors.wazuh_manager.utils.universal import send_get_request
9
10
11 -def collect_wazuh_agents() -> WazuhAgentsList:
11 +async def collect_wazuh_agents() -> WazuhAgentsList:
12 logger.info("Collecting all agents from Wazuh Manager")
13 - agents_collected = send_get_request(endpoint="/agents", params={"limit": 1000})
13 + agents_collected = await send_get_request(endpoint="/agents", params={"limit": 1000})
14
15 if agents_collected.get("success") == False:
16 raise HTTPException(
backend/app/agents/wazuh/services/vulnerabilities.py
+2 -2
@@ -8,10 +8,10 @@ from app.agents.wazuh.schema.agents import WazuhAgentVulnerabilitiesResponse
8 from app.connectors.wazuh_manager.utils.universal import send_get_request
9
10
11 -def collect_agent_vulnerabilities(agent_id: str):
11 +async def collect_agent_vulnerabilities(agent_id: str):
12 """Collect agent vulnerabilities from Wazuh Manager."""
13 logger.info(f"Collecting agent {agent_id} vulnerabilities from Wazuh Manager")
14 - agent_vulnerabilities = send_get_request(endpoint=f"/vulnerability/{agent_id}")
14 + agent_vulnerabilities = await send_get_request(endpoint=f"/vulnerability/{agent_id}")
15 if agent_vulnerabilities["success"] is False:
16 raise HTTPException(status_code=500, detail=agent_vulnerabilities["message"])
17
backend/app/auth/routes/auth.py
+14 -11
@@ -22,13 +22,14 @@ from app.db.db_session import session
22
23 ACCESS_TOKEN_EXPIRE_MINUTES = 1440
24
25 -user_router = APIRouter()
25 +auth_router = APIRouter()
26 auth_handler = AuthHandler()
27
28
29 -@user_router.post("/token", response_model=Token)
29 +@auth_router.post("/token", response_model=Token)
30 async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
31 - user = auth_handler.authenticate_user(form_data.username, form_data.password)
31 + # user = auth_handler.authenticate_user(form_data.username, form_data.password)
32 + user = await auth_handler.authenticate_user(form_data.username, form_data.password)
33 if not user:
34 raise HTTPException(
35 status_code=status.HTTP_401_UNAUTHORIZED,
@@ -36,20 +37,21 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
37 headers={"WWW-Authenticate": "Bearer"},
38 )
39 access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
39 - access_token = auth_handler.encode_token(user.username, access_token_expires)
40 + access_token = await auth_handler.encode_token(user.username, access_token_expires)
41 return {"access_token": access_token, "token_type": "bearer"}
42
43
43 -@user_router.get("/refresh", response_model=Token)
44 +@auth_router.get("/refresh", response_model=Token)
45 async def refresh_token(current_user: User = Depends(auth_handler.get_current_user)):
46 access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
47 access_token = auth_handler.encode_token(current_user.username, access_token_expires)
48 return {"access_token": access_token, "token_type": "bearer"}
49
50
50 -@user_router.post("/register", response_model=UserResponse, status_code=201, description="Register new user")
51 -def register(user: UserInput):
52 - users = select_all_users()
51 +@auth_router.post("/register", response_model=UserResponse, status_code=201, description="Register new user")
52 +async def register(user: UserInput):
53 + # users = select_all_users()
54 + users = await select_all_users()
55 if any(x.username == user.username for x in users):
56 raise HTTPException(status_code=400, detail="Username is taken")
57 hashed_pwd = auth_handler.get_password_hash(user.password)
@@ -59,9 +61,10 @@ def register(user: UserInput):
61 return {"message": "User created successfully", "success": True}
62
63
62 -@user_router.post("/login", response_model=UserLoginResponse, description="Login user", deprecated=True)
63 -def login(user: UserLogin):
64 - user_found = find_user(user.username)
64 +@auth_router.post("/login", response_model=UserLoginResponse, description="Login user", deprecated=True)
65 +async def login(user: UserLogin):
66 + # user_found = find_user(user.username)
67 + user_found = await find_user(user.username)
68 if not user_found:
69 raise HTTPException(status_code=401, detail="Invalid username and/or password")
70 verified = auth_handler.verify_password(user.password, user_found.password)
backend/app/auth/services/universal.py
+43 -16
@@ -1,29 +1,56 @@
1 +from loguru import logger
2 +
3 +# ! New with Async
4 +from sqlalchemy.ext.asyncio import AsyncSession
5 from sqlmodel import Session
6 from sqlmodel import select
7
8 from app.auth.models.users import Role
9 from app.auth.models.users import User
6 -from app.db.db_session import engine
10 +from app.db.db_session import async_engine
11 +
12 +# def select_all_users():
13 +# with Session(engine) as session:
14 +# statement = select(User)
15 +# res = session.exec(statement).all()
16 +# return res
17 +
18 +
19 +# def find_user(name):
20 +# with Session(engine) as session:
21 +# statement = select(User).where(User.username == name)
22 +# return session.exec(statement).first()
23
24
9 -def select_all_users():
10 - with Session(engine) as session:
25 +# def get_role(name):
26 +# with Session(engine) as session:
27 +# statement = select(User).where(User.username == name)
28 +# res = session.exec(statement).first()
29 +# # Get the role name
30 +# statement = select(Role).where(Role.id == res.role_id)
31 +# role = session.exec(statement).first()
32 +# return role.name
33 +
34 +
35 +async def select_all_users():
36 + async with AsyncSession(async_engine) as session:
37 statement = select(User)
12 - res = session.exec(statement).all()
13 - return res
38 + results = await session.execute(statement)
39 + return results.scalars().all()
40
41
16 -def find_user(name):
17 - with Session(engine) as session:
42 +async def find_user(name: str):
43 + async with AsyncSession(async_engine) as session:
44 statement = select(User).where(User.username == name)
19 - return session.exec(statement).first()
45 + result = await session.execute(statement)
46 + return result.scalars().first()
47
48
22 -def get_role(name):
23 - with Session(engine) as session:
24 - statement = select(User).where(User.username == name)
25 - res = session.exec(statement).first()
26 - # Get the role name
27 - statement = select(Role).where(Role.id == res.role_id)
28 - role = session.exec(statement).first()
29 - return role.name
49 +async def get_role(name: str):
50 + async with AsyncSession(async_engine) as session:
51 + user = await find_user(name)
52 + if user:
53 + statement = select(Role).where(Role.id == user.role_id)
54 + result = await session.execute(statement)
55 + role = result.scalars().first()
56 + return role.name
backend/app/auth/utils.py
+29 -6
@@ -6,6 +6,7 @@ from fastapi import Depends
6 from fastapi import HTTPException
7 from fastapi.security import OAuth2PasswordBearer
8 from fastapi.security import SecurityScopes
9 +from loguru import logger
10 from passlib.context import CryptContext
11
12 from app.auth.services.universal import find_user
@@ -26,18 +27,39 @@ class AuthHandler:
27 def verify_password(self, plain_password, hashed_password):
28 return self.pwd_context.verify(plain_password, hashed_password)
29
29 - def authenticate_user(self, username: str, password: str):
30 - user = find_user(username)
30 + # ! Old without Async
31 + # def authenticate_user(self, username: str, password: str):
32 + # user = find_user(username)
33 + # if not user or not self.verify_password(password, user.password):
34 + # return False
35 + # return user
36 +
37 + # ! New with Async
38 + async def authenticate_user(self, username: str, password: str):
39 + user = await find_user(username)
40 if not user or not self.verify_password(password, user.password):
41 + logger.info(f"Password is not verified")
42 return False
43 return user
44
35 - def encode_token(self, username: str, access_token_expires: timedelta = timedelta(minutes=60)):
45 + # ! Old without Async
46 + # def encode_token(self, username: str, access_token_expires: timedelta = timedelta(minutes=60)):
47 + # payload = {
48 + # "exp": datetime.utcnow() + access_token_expires,
49 + # "iat": datetime.utcnow(),
50 + # "sub": username,
51 + # "scopes": [get_role(username)],
52 + # }
53 + # return jwt.encode(payload, self.secret, algorithm="HS256")
54 +
55 + # ! New with Async
56 + async def encode_token(self, username: str, access_token_expires: timedelta = timedelta(minutes=60)):
57 + role = await get_role(username)
58 payload = {
59 "exp": datetime.utcnow() + access_token_expires,
60 "iat": datetime.utcnow(),
61 "sub": username,
40 - "scopes": [get_role(username)],
62 + "scopes": [role],
63 }
64 return jwt.encode(payload, self.secret, algorithm="HS256")
65
@@ -50,7 +72,7 @@ class AuthHandler:
72 except jwt.InvalidTokenError:
73 raise HTTPException(status_code=401, detail="Invalid token")
74
53 - def get_current_user(self, security_scopes: SecurityScopes, token: str = Depends(security)):
75 + async def get_current_user(self, security_scopes: SecurityScopes, token: str = Depends(security)):
76 if security_scopes.scopes:
77 authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
78 else:
@@ -77,7 +99,8 @@ class AuthHandler:
99 detail="Username not found in token",
100 headers={"WWW-Authenticate": authenticate_value},
101 )
80 - user = find_user(username)
102 + user = await find_user(username)
103 +
104 if user is None:
105 raise HTTPException(
106 status_code=401,
backend/app/connectors/cortex/routes/analyzers.py
+7 -6
@@ -19,12 +19,13 @@ from app.connectors.cortex.services.analyzers import run_analyzer
19 cortex_analyzer_router = APIRouter()
20
21
22 -def get_available_analyzers() -> List[str]:
23 - return get_analyzers().analyzers
22 +async def get_available_analyzers() -> List[str]:
23 + analyzers = await get_analyzers()
24 + return analyzers.analyzers
25
26
26 -def verify_analyzer_exists(run_analyzer_body: RunAnalyzerBody) -> RunAnalyzerBody:
27 - available_analyzers = get_available_analyzers()
27 +async def verify_analyzer_exists(run_analyzer_body: RunAnalyzerBody) -> RunAnalyzerBody:
28 + available_analyzers = await get_available_analyzers()
29 if run_analyzer_body.analyzer_name not in available_analyzers:
30 raise HTTPException(status_code=400, detail=f"Analyzer {run_analyzer_body.analyzer_name} does not exist.")
31 return run_analyzer_body
@@ -38,7 +39,7 @@ def verify_analyzer_exists(run_analyzer_body: RunAnalyzerBody) -> RunAnalyzerBod
39 )
40 async def get_all_analyzers() -> AnalyzersResponse:
41 logger.info("Fetching all analyzers")
41 - return get_analyzers()
42 + return await get_analyzers()
43
44
45 @cortex_analyzer_router.post(
@@ -53,4 +54,4 @@ async def run_analyzer_route(run_analyzer_body: RunAnalyzerBody = Depends(verify
54 raise HTTPException(status_code=400, detail=f"Invalid data type: {data_type}")
55
56 logger.info(f"Running analyzer {run_analyzer_body.analyzer_name} with data {run_analyzer_body.analyzer_data} of type {data_type}")
56 - return run_analyzer(run_analyzer_body, data_type)
57 + return await run_analyzer(run_analyzer_body, data_type)
backend/app/connectors/cortex/services/analyzers.py
+9 -9
@@ -20,7 +20,7 @@ from app.connectors.cortex.utils.universal import (
20 )
21
22
23 -def fetch_analyzers(api: Api) -> List[Dict]:
23 +async def fetch_analyzers(api: Api) -> List[Dict]:
24 try:
25 return api.analyzers.find_all({}, range="all")
26 except Exception as e:
@@ -36,8 +36,8 @@ def extract_analyzer_names(analyzers: List[Dict]) -> List[str]:
36 raise HTTPException(status_code=500, detail=f"Error processing analyzers: {e}")
37
38
39 -def init_cortex_client() -> Union[Api, None]:
40 - return create_cortex_client("Cortex")
39 +async def init_cortex_client() -> Union[Api, None]:
40 + return await create_cortex_client("Cortex")
41
42
43 def handle_api_initialization(api: Union[Api, None]) -> Api:
@@ -47,18 +47,18 @@ def handle_api_initialization(api: Union[Api, None]) -> Api:
47 return api
48
49
50 -def get_analyzers() -> AnalyzersResponse:
51 - api = init_cortex_client()
50 +async def get_analyzers() -> AnalyzersResponse:
51 + api = await init_cortex_client()
52 handle_api_initialization(api)
53
54 - analyzers = fetch_analyzers(api)
54 + analyzers = await fetch_analyzers(api)
55 analyzer_names = extract_analyzer_names(analyzers)
56
57 return AnalyzersResponse(success=True, message="Successfully fetched analyzers", analyzers=analyzer_names)
58
59
60 -def run_analyzer(run_analyzer_body: RunAnalyzerBody, data_type: str) -> RunAnalyzerResponse:
61 - api = init_cortex_client()
60 +async def run_analyzer(run_analyzer_body: RunAnalyzerBody, data_type: str) -> RunAnalyzerResponse:
61 + api = await init_cortex_client()
62 handle_api_initialization(api)
63
64 analyzer_name = run_analyzer_body.analyzer_name
@@ -66,7 +66,7 @@ def run_analyzer(run_analyzer_body: RunAnalyzerBody, data_type: str) -> RunAnaly
66 logger.info(f"Running analyzer {analyzer_name} with data {analyzer_data} of type {data_type}")
67 job_data = AnalyzerJobData(data=analyzer_data, dataType=data_type)
68
69 - result = run_and_wait_for_analyzer(analyzer_name=analyzer_name, job_data=job_data)
69 + result = await run_and_wait_for_analyzer(analyzer_name=analyzer_name, job_data=job_data)
70
71 if result is None:
72 logger.error(f"Failed to run analyzer {analyzer_name}")
backend/app/connectors/cortex/utils/universal.py
+15 -12
@@ -9,9 +9,10 @@ from loguru import logger
9
10 from app.connectors.cortex.schema.analyzers import AnalyzerJobData
11 from app.connectors.utils import get_connector_info_from_db
12 +from app.db.db_session import get_db_session
13
14
14 -def verify_cortex_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
15 +async def verify_cortex_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
16 """
17 Verifies the connection to Cortex service.
18
@@ -35,46 +36,48 @@ def verify_cortex_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
36 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error: {e}"}
37
38
38 -def verify_cortex_connection(connector_name: str) -> str:
39 +async def verify_cortex_connection(connector_name: str) -> str:
40 """
41 Returns the authentication token for the Cortex service.
42
43 Returns:
44 str: Authentication token for the Cortex service.
45 """
45 - attributes = get_connector_info_from_db(connector_name)
46 + async with get_db_session() as session: # This will correctly enter the context manager
47 + attributes = await get_connector_info_from_db(connector_name, session)
48 if attributes is None:
49 logger.error("No Cortex connector found in the database")
50 return None
49 - return verify_cortex_credentials(attributes)
51 + return await verify_cortex_credentials(attributes)
52
53
52 -def create_cortex_client(connector_name: str) -> Api:
54 +async def create_cortex_client(connector_name: str) -> Api:
55 """
56 Returns an Cortex client for the Wazuh Indexer service.
57
58 Returns:
59 Cortex: Cortex client for the Cortex service.
60 """
59 - attributes = get_connector_info_from_db(connector_name)
61 + async with get_db_session() as session: # This will correctly enter the context manager
62 + attributes = await get_connector_info_from_db(connector_name, session)
63 if attributes is None:
64 logger.error("No Wazuh Indexer connector found in the database")
65 return None
66 return Api(attributes["connector_url"], attributes["connector_api_key"], verify_cert=False)
67
68
66 -def run_and_wait_for_analyzer(analyzer_name: str, job_data: AnalyzerJobData) -> Dict[str, Any]:
67 - api = create_cortex_client("Cortex") # Create Api object
69 +async def run_and_wait_for_analyzer(analyzer_name: str, job_data: AnalyzerJobData) -> Dict[str, Any]:
70 + api = await create_cortex_client("Cortex") # Create Api object
71 if api is None:
72 return {"success": False, "message": "API initialization failed"}
73 try:
74 job = api.analyzers.run_by_name(analyzer_name, job_data.dict(), force=1)
72 - return monitor_analyzer_job(api, job)
75 + return await monitor_analyzer_job(api, job)
76 except Exception as e:
77 raise HTTPException(status_code=500, detail=f"Error running analyzer {analyzer_name}: {e}")
78
79
77 -def monitor_analyzer_job(api: Api, job: Any) -> Dict[str, Any]:
80 +async def monitor_analyzer_job(api: Api, job: Any) -> Dict[str, Any]:
81 r_json = job.json()
82 job_id = r_json["id"]
83 logger.info(f"Job ID is: {job_id}")
@@ -100,10 +103,10 @@ def monitor_analyzer_job(api: Api, job: Any) -> Dict[str, Any]:
103 r_json = followup_request.json()
104 job_state = r_json["status"]
105
103 - return retrieve_final_report(api, job_id)
106 + return await retrieve_final_report(api, job_id)
107
108
106 -def retrieve_final_report(api: Api, job_id: str) -> Dict[str, Any]:
109 +async def retrieve_final_report(api: Api, job_id: str) -> Dict[str, Any]:
110 report = api.jobs.get_report(job_id).report
111 final_report = report["full"]
112 return {"success": True, "message": "Analyzer ran successfully", "report": final_report}
backend/app/connectors/dfir_iris/routes/alerts.py
+6 -6
@@ -16,8 +16,8 @@ from app.connectors.dfir_iris.utils.universal import check_alert_exists
16 # App specific imports
17
18
19 -def verify_alert_exists(alert_id: str) -> str:
20 - if not check_alert_exists(alert_id):
19 +async def verify_alert_exists(alert_id: str) -> str:
20 + if not await check_alert_exists(alert_id):
21 raise HTTPException(status_code=400, detail=f"Alert {alert_id} does not exist.")
22 return alert_id
23
@@ -33,7 +33,7 @@ dfir_iris_alerts_router = APIRouter()
33 )
34 async def get_all_alerts() -> AlertsResponse:
35 logger.info("Fetching all alerts")
36 - return get_alerts()
36 + return await get_alerts()
37
38
39 @dfir_iris_alerts_router.get(
@@ -44,7 +44,7 @@ async def get_all_alerts() -> AlertsResponse:
44 )
45 async def get_all_bookmarked_alerts() -> BookmarkedAlertsResponse:
46 logger.info("Fetching all bookmarked alerts")
47 - return get_bookmarked_alerts()
47 + return await get_bookmarked_alerts()
48
49
50 @dfir_iris_alerts_router.post(
@@ -55,7 +55,7 @@ async def get_all_bookmarked_alerts() -> BookmarkedAlertsResponse:
55 )
56 async def bookmark_alert_route(alert_id: str = Depends(verify_alert_exists)) -> AlertResponse:
57 logger.info(f"Bookmarking alert {alert_id}")
58 - return bookmark_alert(alert_id, bookmarked=True)
58 + return await bookmark_alert(alert_id, bookmarked=True)
59
60
61 @dfir_iris_alerts_router.delete(
@@ -66,4 +66,4 @@ async def bookmark_alert_route(alert_id: str = Depends(verify_alert_exists)) ->
66 )
67 async def unbookmark_alert_route(alert_id: str = Depends(verify_alert_exists)) -> AlertResponse:
68 logger.info(f"Unbookmarking alert {alert_id}")
69 - return bookmark_alert(alert_id, bookmarked=False)
69 + return await bookmark_alert(alert_id, bookmarked=False)
backend/app/connectors/dfir_iris/routes/assets.py
+5 -5
@@ -12,16 +12,16 @@ from app.connectors.dfir_iris.utils.universal import check_case_exists
12 # App specific imports
13
14
15 -def verify_case_exists(case_id: int) -> int:
16 - if not check_case_exists(case_id):
15 +async def verify_case_exists(case_id: int) -> int:
16 + if not await check_case_exists(case_id):
17 raise HTTPException(status_code=400, detail=f"Case {case_id} does not exist.")
18 return case_id
19
20
21 -assets_router = APIRouter()
21 +dfir_iris_assets_router = APIRouter()
22
23
24 -@assets_router.get(
24 +@dfir_iris_assets_router.get(
25 "/{case_id}",
26 response_model=AssetResponse,
27 description="Get all assets for a case",
@@ -29,4 +29,4 @@ assets_router = APIRouter()
29 )
30 async def get_case_assets_route(case_id: int = Depends(verify_case_exists)) -> AssetResponse:
31 logger.info(f"Fetching assets for case {case_id}")
32 - return get_case_assets(case_id)
32 + return await get_case_assets(case_id)
backend/app/connectors/dfir_iris/routes/cases.py
+9 -9
@@ -19,13 +19,13 @@ from app.connectors.dfir_iris.services.cases import get_single_case
19 from app.connectors.dfir_iris.utils.universal import check_case_exists
20
21
22 -def verify_case_exists(case_id: int) -> int:
23 - if not check_case_exists(case_id):
22 +async def verify_case_exists(case_id: int) -> int:
23 + if not await check_case_exists(case_id):
24 raise HTTPException(status_code=400, detail=f"Case {case_id} does not exist.")
25 return case_id
26
27
28 -cases_router = APIRouter()
28 +dfir_iris_cases_router = APIRouter()
29
30
31 def get_timedelta(older_than: int, time_unit: TimeUnit) -> CaseOlderThanBody:
@@ -39,7 +39,7 @@ def get_timedelta(older_than: int, time_unit: TimeUnit) -> CaseOlderThanBody:
39 return CaseOlderThanBody(older_than=delta, time_unit=time_unit)
40
41
42 -@cases_router.get(
42 +@dfir_iris_cases_router.get(
43 "",
44 response_model=CaseResponse,
45 description="Get all cases",
@@ -47,10 +47,10 @@ def get_timedelta(older_than: int, time_unit: TimeUnit) -> CaseOlderThanBody:
47 )
48 async def get_cases_route() -> CaseResponse:
49 logger.info("Fetching all cases")
50 - return get_all_cases()
50 + return await get_all_cases()
51
52
53 -@cases_router.post(
53 +@dfir_iris_cases_router.post(
54 "/older_than",
55 response_model=CasesBreachedResponse,
56 description="Get all cases older than a specified date",
@@ -58,10 +58,10 @@ async def get_cases_route() -> CaseResponse:
58 )
59 async def get_cases_older_than_route(case_older_than_body: CaseOlderThanBody = Depends(get_timedelta)) -> CaseResponse:
60 logger.info(f"Fetching all cases older than {case_older_than_body.older_than} ({case_older_than_body.time_unit.value})")
61 - return get_cases_older_than(case_older_than_body)
61 + return await get_cases_older_than(case_older_than_body)
62
63
64 -@cases_router.get(
64 +@dfir_iris_cases_router.get(
65 "/{case_id}",
66 response_model=SingleCaseResponse,
67 description="Get a single case",
@@ -70,4 +70,4 @@ async def get_cases_older_than_route(case_older_than_body: CaseOlderThanBody = D
70 async def get_single_case_route(case_id: int = Depends(verify_case_exists)) -> SingleCaseResponse:
71 logger.info(f"Fetching case {case_id}")
72 single_case_body = SingleCaseBody(case_id=case_id)
73 - return get_single_case(single_case_body.case_id)
73 + return await get_single_case(single_case_body.case_id)
backend/app/connectors/dfir_iris/routes/notes.py
+7 -7
@@ -15,16 +15,16 @@ from app.connectors.dfir_iris.services.notes import get_case_notes
15 from app.connectors.dfir_iris.utils.universal import check_case_exists
16
17
18 -def verify_case_exists(case_id: int) -> int:
19 - if not check_case_exists(case_id):
18 +async def verify_case_exists(case_id: int) -> int:
19 + if not await check_case_exists(case_id):
20 raise HTTPException(status_code=400, detail=f"Case {case_id} does not exist.")
21 return case_id
22
23
24 -notes_router = APIRouter()
24 +dfir_iris_notes_router = APIRouter()
25
26
27 -@notes_router.get(
27 +@dfir_iris_notes_router.get(
28 "/{case_id}",
29 response_model=NotesResponse,
30 description="Get all notes for a case",
@@ -32,10 +32,10 @@ notes_router = APIRouter()
32 )
33 async def get_case_notes_route(case_id: int = Depends(verify_case_exists), search_term: Optional[str] = "%") -> NotesResponse:
34 logger.info(f"Fetching notes for case {case_id}")
35 - return get_case_notes(case_id, search_term)
35 + return await get_case_notes(case_id, search_term)
36
37
38 -@notes_router.post(
38 +@dfir_iris_notes_router.post(
39 "/{case_id}",
40 response_model=NoteCreationResponse,
41 description="Create a note for a case",
@@ -44,4 +44,4 @@ async def get_case_notes_route(case_id: int = Depends(verify_case_exists), searc
44 async def create_case_note_route(case_id: int, note_creation_body: NoteCreationBody) -> NoteCreationResponse:
45 verify_case_exists(case_id)
46 logger.info(f"Creating a note for case {case_id}")
47 - return create_case_note(case_id, note_creation_body)
47 + return await create_case_note(case_id, note_creation_body)
backend/app/connectors/dfir_iris/routes/users.py
+4 -4
@@ -20,8 +20,8 @@ def verify_user_exists(user_id: int) -> int:
20 return user_id
21
22
23 -def verify_alert_exists(alert_id: str) -> str:
24 - if not check_alert_exists(alert_id):
23 +async def verify_alert_exists(alert_id: str) -> str:
24 + if not await check_alert_exists(alert_id):
25 raise HTTPException(status_code=400, detail=f"Alert {alert_id} does not exist.")
26 return alert_id
27
@@ -37,7 +37,7 @@ dfir_iris_users_router = APIRouter()
37 )
38 async def get_all_users() -> UsersResponse:
39 logger.info("Fetching all users")
40 - return get_users()
40 + return await get_users()
41
42
43 @dfir_iris_users_router.post(
@@ -48,4 +48,4 @@ async def get_all_users() -> UsersResponse:
48 )
49 async def assign_user_to_alert_route(alert_id: str = Depends(verify_alert_exists), user_id: int = Depends(verify_user_exists)) -> User:
50 logger.info(f"Assigning user {user_id} to alert {alert_id}")
51 - return assign_user_to_alert(alert_id, user_id)
51 + return await assign_user_to_alert(alert_id, user_id)
backend/app/connectors/dfir_iris/services/alerts.py
+10 -9
@@ -7,23 +7,24 @@ from app.connectors.dfir_iris.utils.universal import fetch_and_validate_data
7 from app.connectors.dfir_iris.utils.universal import initialize_client_and_alert
8
9
10 -def get_alerts() -> AlertsResponse:
11 - client, alert = initialize_client_and_alert("DFIR-IRIS")
12 - result = fetch_and_validate_data(client, alert.filter_alerts)
10 +async def get_alerts() -> AlertsResponse:
11 + client, alert = await initialize_client_and_alert("DFIR-IRIS")
12 + result = await fetch_and_validate_data(client, alert.filter_alerts)
13 return AlertsResponse(success=True, message="Successfully fetched alerts", alerts=result["data"]["alerts"])
14
15
16 -def bookmark_alert(alert_id: str, bookmarked: bool) -> AlertResponse:
17 - client, alert = initialize_client_and_alert("DFIR-IRIS")
16 +async def bookmark_alert(alert_id: str, bookmarked: bool) -> AlertResponse:
17 + client, alert = await initialize_client_and_alert("DFIR-IRIS")
18 if bookmarked:
19 - result = fetch_and_validate_data(client, alert.update_alert, alert_id, {"alert_tags": "bookmarked"})
19 + result = await fetch_and_validate_data(client, alert.update_alert, alert_id, {"alert_tags": "bookmarked"})
20 return AlertResponse(success=True, message="Successfully bookmarked alert", alert=result["data"])
21 - result = fetch_and_validate_data(client, alert.update_alert, alert_id, {"alert_tags": ""})
21 + result = await fetch_and_validate_data(client, alert.update_alert, alert_id, {"alert_tags": ""})
22 return AlertResponse(success=True, message="Successfully removed bookmark from alert", alert=result["data"])
23
24
25 -def get_bookmarked_alerts() -> BookmarkedAlertsResponse:
26 - alerts = get_alerts().alerts
25 +async def get_bookmarked_alerts() -> BookmarkedAlertsResponse:
26 + alerts = await get_alerts()
27 + alerts = alerts.alerts
28 bookmarked_alerts = []
29 for alert in alerts:
30 if alert["alert_tags"] is not None and "bookmarked" in alert["alert_tags"]:
backend/app/connectors/dfir_iris/services/assets.py
+4 -3
@@ -1,4 +1,5 @@
1 from fastapi import HTTPException
2 +from loguru import logger
3
4 from app.connectors.dfir_iris.schema.assets import Asset
5 from app.connectors.dfir_iris.schema.assets import AssetResponse
@@ -7,9 +8,9 @@ from app.connectors.dfir_iris.utils.universal import fetch_and_validate_data
8 from app.connectors.dfir_iris.utils.universal import initialize_client_and_case
9
10
10 -def get_case_assets(case_id: int) -> AssetResponse:
11 - client, case = initialize_client_and_case("DFIR-IRIS")
12 - result = fetch_and_validate_data(client, case.list_assets, case_id)
11 +async def get_case_assets(case_id: int) -> AssetResponse:
12 + client, case = await initialize_client_and_case("DFIR-IRIS")
13 + result = await fetch_and_validate_data(client, case.list_assets, case_id)
14 try:
15 asset_list = result["data"]["assets"]
16 state_data = result["data"]["state"]
backend/app/connectors/dfir_iris/services/cases.py
+10 -10
@@ -15,17 +15,17 @@ from app.connectors.dfir_iris.utils.universal import create_dfir_iris_client
15 from app.connectors.dfir_iris.utils.universal import fetch_and_parse_data
16
17
18 -def get_client_and_cases() -> Dict:
18 +async def get_client_and_cases() -> Dict:
19 """
20 Initialize the client session and fetch all cases.
21
22 Returns:
23 Dictionary containing the success status and either the case data or an error message.
24 """
25 - dfir_iris_client = create_dfir_iris_client("DFIR-IRIS")
25 + dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
26 case = Case(session=dfir_iris_client)
27 logger.info("Fetching all cases after getting session")
28 - result = fetch_and_parse_data(dfir_iris_client, case.list_cases)
28 + result = await fetch_and_parse_data(dfir_iris_client, case.list_cases)
29 return result
30
31
@@ -67,8 +67,8 @@ def filter_cases_older_than(cases: List[Dict], older_than: datetime) -> List[Dic
67 return filtered_cases
68
69
70 -def get_all_cases() -> CaseResponse:
71 - result = get_client_and_cases()
70 +async def get_all_cases() -> CaseResponse:
71 + result = await get_client_and_cases()
72 try:
73 if not result["success"]:
74 logger.error(f"Failed to get all cases: {result['message']}")
@@ -79,8 +79,8 @@ def get_all_cases() -> CaseResponse:
79 raise HTTPException(status_code=500, detail=f"Failed to get all cases: {err}")
80
81
82 -def get_cases_older_than(case_older_than_body: CaseOlderThanBody) -> CasesBreachedResponse:
83 - result = get_client_and_cases()
82 +async def get_cases_older_than(case_older_than_body: CaseOlderThanBody) -> CasesBreachedResponse:
83 + result = await get_client_and_cases()
84 if not result["success"]:
85 logger.error(f"Failed to get all cases: {result['message']}")
86 return HTTPException(status_code=500, detail=f"Failed to get all cases: {result['message']}")
@@ -94,8 +94,8 @@ def get_cases_older_than(case_older_than_body: CaseOlderThanBody) -> CasesBreach
94 )
95
96
97 -def get_single_case(case_id: SingleCaseBody) -> SingleCaseResponse:
98 - dfir_iris_client = create_dfir_iris_client("DFIR-IRIS")
97 +async def get_single_case(case_id: SingleCaseBody) -> SingleCaseResponse:
98 + dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
99 case = Case(session=dfir_iris_client)
100 - result = fetch_and_parse_data(dfir_iris_client, case.get_case, case_id)
100 + result = await fetch_and_parse_data(dfir_iris_client, case.get_case, case_id)
101 return SingleCaseResponse(success=True, message="Successfully fetched single case", case=result["data"])
backend/app/connectors/dfir_iris/services/notes.py
+15 -15
@@ -14,35 +14,35 @@ from app.connectors.dfir_iris.utils.universal import fetch_and_validate_data
14 from app.connectors.dfir_iris.utils.universal import initialize_client_and_case
15
16
17 -def process_notes(notes: List[Dict], case_id: int) -> List[Dict]:
17 +async def process_notes(notes: List[Dict], case_id: int) -> List[Dict]:
18 processed_notes = []
19 for note in notes:
20 - note_details = get_case_note_details(note["note_id"], case_id)
20 + note_details = await get_case_note_details(note["note_id"], case_id)
21 logger.info(f"Note details: {note_details}")
22 note["note_details"] = note_details.note_details
23 processed_notes.append(note)
24 return processed_notes
25
26
27 -def get_case_notes(case_id: int, search_term: str) -> NotesResponse:
28 - client, case = initialize_client_and_case("DFIR-IRIS")
29 - result = fetch_and_validate_data(client, case.search_notes, search_term, case_id)
30 - processed_notes = process_notes(result["data"], case_id)
27 +async def get_case_notes(case_id: int, search_term: str) -> NotesResponse:
28 + client, case = await initialize_client_and_case("DFIR-IRIS")
29 + result = await fetch_and_validate_data(client, case.search_notes, search_term, case_id)
30 + processed_notes = await process_notes(result["data"], case_id)
31 return NotesResponse(success=True, message="Successfully fetched notes for case", notes=processed_notes)
32
33
34 -def get_case_note_details(note_id: int, case_id: int) -> NoteDetailsResponse:
35 - client, case = initialize_client_and_case("DFIR-IRIS")
36 - result = fetch_and_validate_data(client, case.get_note, note_id, case_id)
34 +async def get_case_note_details(note_id: int, case_id: int) -> NoteDetailsResponse:
35 + client, case = await initialize_client_and_case("DFIR-IRIS")
36 + result = await fetch_and_validate_data(client, case.get_note, note_id, case_id)
37 note_details = NoteDetails(**result["data"])
38 return NoteDetailsResponse(success=True, message="Successfully fetched note details", note_details=note_details)
39
40
41 -def perform_note_creation(client: Any, case: Case, note_creation_body: NoteCreationBody, case_id: int) -> Dict:
42 - result = fetch_and_validate_data(client, case.add_notes_group, note_creation_body.note_title, case_id)
41 +async def perform_note_creation(client: Any, case: Case, note_creation_body: NoteCreationBody, case_id: int) -> Dict:
42 + result = await fetch_and_validate_data(client, case.add_notes_group, note_creation_body.note_title, case_id)
43 note_id = result["data"]["group_id"]
44 custom_attributes = {}
45 - return fetch_and_validate_data(
45 + return await fetch_and_validate_data(
46 client,
47 case.add_note,
48 note_creation_body.note_title,
@@ -53,7 +53,7 @@ def perform_note_creation(client: Any, case: Case, note_creation_body: NoteCreat
53 )
54
55
56 -def create_case_note(case_id: int, note_creation_body: NoteCreationBody) -> NoteCreationResponse:
57 - client, case = initialize_client_and_case("DFIR-IRIS")
58 - result = perform_note_creation(client, case, note_creation_body, case_id)
56 +async def create_case_note(case_id: int, note_creation_body: NoteCreationBody) -> NoteCreationResponse:
57 + client, case = await initialize_client_and_case("DFIR-IRIS")
58 + result = await perform_note_creation(client, case, note_creation_body, case_id)
59 return NoteCreationResponse(success=True, message="Successfully created note", note=result["data"])
backend/app/connectors/dfir_iris/services/users.py
+6 -6
@@ -5,13 +5,13 @@ from app.connectors.dfir_iris.utils.universal import initialize_client_and_alert
5 from app.connectors.dfir_iris.utils.universal import initialize_client_and_user
6
7
8 -def get_users() -> UsersResponse:
9 - client, user = initialize_client_and_user("DFIR-IRIS")
10 - result = fetch_and_validate_data(client, user.list_users)
8 +async def get_users() -> UsersResponse:
9 + client, user = await initialize_client_and_user("DFIR-IRIS")
10 + result = await fetch_and_validate_data(client, user.list_users)
11 return UsersResponse(success=True, message="Successfully fetched users", users=result["data"])
12
13
14 -def assign_user_to_alert(alert_id: str, user_id: int) -> AlertResponse:
15 - client, alert = initialize_client_and_alert("DFIR-IRIS")
16 - result = fetch_and_validate_data(client, alert.update_alert, alert_id, {"alert_owner_id": user_id})
14 +async def assign_user_to_alert(alert_id: str, user_id: int) -> AlertResponse:
15 + client, alert = await initialize_client_and_alert("DFIR-IRIS")
16 + result = await fetch_and_validate_data(client, alert.update_alert, alert_id, {"alert_owner_id": user_id})
17 return AlertResponse(success=True, message="Successfully assigned user to alert", alert=result["data"])
backend/app/connectors/dfir_iris/utils/universal.py
+24 -21
@@ -16,9 +16,10 @@ from fastapi import HTTPException
16 from loguru import logger
17
18 from app.connectors.utils import get_connector_info_from_db
19 +from app.db.db_session import get_db_session
20
21
21 -def verify_dfir_iris_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
22 +async def verify_dfir_iris_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
23 """
24 Verifies the connection to DFIR-IRIS service.
25
@@ -48,21 +49,22 @@ def verify_dfir_iris_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
49 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error: {e}"}
50
51
51 -def verify_dfir_iris_connection(connector_name: str) -> str:
52 +async def verify_dfir_iris_connection(connector_name: str) -> str:
53 """
54 Returns the authentication token for the DFIR-IRIS service.
55
56 Returns:
57 str: Authentication token for the DFIR-IRIS service.
58 """
58 - attributes = get_connector_info_from_db(connector_name)
59 + async with get_db_session() as session: # This will correctly enter the context manager
60 + attributes = await get_connector_info_from_db(connector_name, session)
61 if attributes is None:
62 logger.error("No DFIR-IRIS connector found in the database")
63 return None
62 - return verify_dfir_iris_credentials(attributes)
64 + return await verify_dfir_iris_credentials(attributes)
65
66
65 -def create_dfir_iris_client(connector_name: str) -> ClientSession:
67 +async def create_dfir_iris_client(connector_name: str) -> ClientSession:
68 """
69 Creates a session with DFIR-IRIS.
70
@@ -74,7 +76,8 @@ def create_dfir_iris_client(connector_name: str) -> ClientSession:
76 dict: A dictionary containing the success status and either the session object or an error message.
77 """
78 try:
77 - attributes = get_connector_info_from_db(connector_name)
79 + async with get_db_session() as session: # This will correctly enter the context manager
80 + attributes = await get_connector_info_from_db(connector_name, session)
81 logger.info("Creating session with DFIR-IRIS.")
82 return ClientSession(
83 host=attributes["connector_url"],
@@ -89,7 +92,7 @@ def create_dfir_iris_client(connector_name: str) -> ClientSession:
92 raise HTTPException(status_code=500, detail=f"Error creating session with DFIR-IRIS: {e}")
93
94
92 -def fetch_and_parse_data(session: ClientSession, action: Callable, *args) -> Dict[str, Union[bool, Optional[Dict]]]:
95 +async def fetch_and_parse_data(session: ClientSession, action: Callable, *args) -> Dict[str, Union[bool, Optional[Dict]]]:
96 """
97 Fetches and parses data from DFIR-IRIS using a specified action.
98
@@ -113,20 +116,20 @@ def fetch_and_parse_data(session: ClientSession, action: Callable, *args) -> Dic
116 return HTTPException(status_code=500, detail=f"Failed to execute {action.__name__}: {err}")
117
118
116 -def initialize_client_and_case(service_name: str) -> Tuple[Any, Case]:
117 - dfir_iris_client = create_dfir_iris_client(service_name)
119 +async def initialize_client_and_case(service_name: str) -> Tuple[Any, Case]:
120 + dfir_iris_client = await create_dfir_iris_client(service_name)
121 case = Case(session=dfir_iris_client)
122 return dfir_iris_client, case
123
124
122 -def initialize_client_and_alert(service_name: str) -> Tuple[Any, Alert]:
123 - dfir_iris_client = create_dfir_iris_client(service_name)
125 +async def initialize_client_and_alert(service_name: str) -> Tuple[Any, Alert]:
126 + dfir_iris_client = await create_dfir_iris_client(service_name)
127 alert = Alert(session=dfir_iris_client)
128 return dfir_iris_client, alert
129
130
128 -def initialize_client_and_user(service_name: str) -> Tuple[Any, Alert]:
129 - dfir_iris_client = create_dfir_iris_client(service_name)
131 +async def initialize_client_and_user(service_name: str) -> Tuple[Any, Alert]:
132 + dfir_iris_client = await create_dfir_iris_client(service_name)
133 user = User(session=dfir_iris_client)
134 return dfir_iris_client, user
135
@@ -136,17 +139,17 @@ def handle_error(error_message: str, status_code: int = 500):
139 raise HTTPException(status_code=status_code, detail=error_message)
140
141
139 -def fetch_and_validate_data(client: Any, func: Callable, *args: Any) -> Dict:
140 - result = fetch_and_parse_data(client, func, *args)
142 +async def fetch_and_validate_data(client: Any, func: Callable, *args: Any) -> Dict:
143 + result = await fetch_and_parse_data(client, func, *args)
144 if not result["success"]:
145 handle_error(f"Failed to fetch data: {result['message']}")
146 return result
147
148
146 -def check_case_exists(case_id: int) -> bool:
149 +async def check_case_exists(case_id: int) -> bool:
150 try:
151 logger.info(f"Checking if case {case_id} exists")
149 - dfir_iris_client = create_dfir_iris_client("DFIR-IRIS")
152 + dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
153 case = Case(session=dfir_iris_client)
154 data = case.get_case(case_id)
155 assert_api_resp(data, soft_fail=False)
@@ -161,9 +164,9 @@ def check_case_exists(case_id: int) -> bool:
164 return False
165
166
164 -def check_alert_exists(alert_id: str) -> bool:
167 +async def check_alert_exists(alert_id: str) -> bool:
168 try:
166 - dfir_iris_client = create_dfir_iris_client("DFIR-IRIS")
169 + dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
170 except Exception as e:
171 raise HTTPException(
172 status_code=500,
@@ -186,10 +189,10 @@ def check_alert_exists(alert_id: str) -> bool:
189 return False
190
191
189 -def check_user_exists(user_id: int) -> bool:
192 +async def check_user_exists(user_id: int) -> bool:
193 try:
194 logger.info(f"Checking if user {user_id} exists")
192 - dfir_iris_client = create_dfir_iris_client("DFIR-IRIS")
195 + dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
196 user = User(session=dfir_iris_client)
197 data = user.get_user(user_id)
198 assert_api_resp(data, soft_fail=False)
backend/app/connectors/graylog/routes/collector.py
+4 -4
@@ -26,7 +26,7 @@ graylog_collector_router = APIRouter()
26 )
27 async def get_all_indices() -> GraylogIndicesResponse:
28 logger.info("Fetching all graylog indices")
29 - return get_indices_full()
29 + return await get_indices_full()
30
31
32 @graylog_collector_router.get(
@@ -37,7 +37,7 @@ async def get_all_indices() -> GraylogIndicesResponse:
37 )
38 async def get_all_inputs() -> GraylogInputsResponse:
39 logger.info("Fetching all graylog inputs")
40 - return get_inputs()
40 + return await get_inputs()
41
42
43 @graylog_collector_router.get(
@@ -48,7 +48,7 @@ async def get_all_inputs() -> GraylogInputsResponse:
48 )
49 async def get_all_running_inputs() -> RunningInputsResponse:
50 logger.info("Fetching all graylog running inputs")
51 - return get_inputs_running()
51 + return await get_inputs_running()
52
53
54 @graylog_collector_router.get(
@@ -59,4 +59,4 @@ async def get_all_running_inputs() -> RunningInputsResponse:
59 )
60 async def get_all_configured_inputs() -> ConfiguredInputsResponse:
61 logger.info("Fetching all graylog configured inputs")
62 - return get_inputs_configured()
62 + return await get_inputs_configured()
backend/app/connectors/graylog/routes/events.py
+2 -2
@@ -23,7 +23,7 @@ graylog_events_router = APIRouter()
23 )
24 async def get_all_event_definitions() -> GraylogEventDefinitionsResponse:
25 logger.info("Fetching all graylog event definitions")
26 - return get_event_definitions()
26 + return await get_event_definitions()
27
28
29 @graylog_events_router.post(
@@ -34,4 +34,4 @@ async def get_all_event_definitions() -> GraylogEventDefinitionsResponse:
34 )
35 async def get_all_alerts(alert_query: AlertQuery) -> GraylogAlertsResponse:
36 logger.info("Fetching all graylog alerts")
37 - return get_alerts(alert_query)
37 + return await get_alerts(alert_query)
backend/app/connectors/graylog/routes/management.py
+17 -17
@@ -29,23 +29,23 @@ from app.connectors.graylog.services.streams import get_stream_ids
29 graylog_management_router = APIRouter()
30
31
32 -def get_managed_index_names() -> List[str]:
33 - return get_index_names()
32 +async def get_managed_index_names() -> List[str]:
33 + return await get_index_names()
34
35
36 -def get_managed_input_ids() -> List[str]:
37 - return get_input_ids()
36 +async def get_managed_input_ids() -> List[str]:
37 + return await get_input_ids()
38
39
40 -def get_managed_stream_ids() -> List[str]:
41 - return get_stream_ids()
40 +async def get_managed_stream_ids() -> List[str]:
41 + return await get_stream_ids()
42
43
44 -def verify_index_name(deleted_index_body: DeletedIndexBody) -> DeletedIndexBody:
44 +async def verify_index_name(deleted_index_body: DeletedIndexBody) -> DeletedIndexBody:
45 # Remove any extra spaces from index_name
46 deleted_index_body.index_name = deleted_index_body.index_name.strip()
47
48 - managed_index_names = get_managed_index_names()
48 + managed_index_names = await get_managed_index_names()
49 if deleted_index_body.index_name not in managed_index_names:
50 raise HTTPException(
51 status_code=400,
@@ -54,21 +54,21 @@ def verify_index_name(deleted_index_body: DeletedIndexBody) -> DeletedIndexBody:
54 return deleted_index_body
55
56
57 -def verify_input_id(stop_input_body: StopInputBody) -> StopInputBody:
57 +async def verify_input_id(stop_input_body: StopInputBody) -> StopInputBody:
58 # Remove any extra spaces from input_id
59 stop_input_body.input_id = stop_input_body.input_id.strip()
60
61 - managed_input_ids = get_managed_input_ids()
61 + managed_input_ids = await get_managed_input_ids()
62 if stop_input_body.input_id not in managed_input_ids:
63 raise HTTPException(status_code=400, detail=f"Input ID '{stop_input_body.input_id}' is not managed by Graylog or no longer exists.")
64 return stop_input_body
65
66
67 -def verify_stream_id(stop_stream_body: StopStreamBody) -> StopStreamBody:
67 +async def verify_stream_id(stop_stream_body: StopStreamBody) -> StopStreamBody:
68 # Remove any extra spaces from stream_id
69 stop_stream_body.stream_id = stop_stream_body.stream_id.strip()
70
71 - managed_stream_ids = get_managed_stream_ids()
71 + managed_stream_ids = await get_managed_stream_ids()
72 if stop_stream_body.stream_id not in managed_stream_ids:
73 raise HTTPException(
74 status_code=400,
@@ -86,7 +86,7 @@ def verify_stream_id(stop_stream_body: StopStreamBody) -> StopStreamBody:
86 async def delete_index_route(deleted_index_body: DeletedIndexBody = Depends(verify_index_name)) -> DeletedIndexResponse:
87 logger.info(f"Deleting index {deleted_index_body.index_name}")
88
89 - return delete_index(deleted_index_body.index_name)
89 + return await delete_index(deleted_index_body.index_name)
90
91
92 @graylog_management_router.post(
@@ -98,7 +98,7 @@ async def delete_index_route(deleted_index_body: DeletedIndexBody = Depends(veri
98 async def stop_input_route(stop_input_body: StopInputBody = Depends(verify_input_id)) -> StopInputResponse:
99 logger.info(f"Stopping input {stop_input_body.input_id}")
100
101 - return stop_input(stop_input_body.input_id)
101 + return await stop_input(stop_input_body.input_id)
102
103
104 @graylog_management_router.post(
@@ -110,7 +110,7 @@ async def stop_input_route(stop_input_body: StopInputBody = Depends(verify_input
110 async def start_input_route(start_input_body: StartInputBody = Depends(verify_input_id)) -> StartInputResponse:
111 logger.info(f"Starting input {start_input_body.input_id}")
112
113 - return start_input(start_input_body.input_id)
113 + return await start_input(start_input_body.input_id)
114
115
116 @graylog_management_router.post(
@@ -122,7 +122,7 @@ async def start_input_route(start_input_body: StartInputBody = Depends(verify_in
122 async def stop_stream_route(stop_stream_body: StopStreamBody = Depends(verify_stream_id)) -> StopStreamResponse:
123 logger.info(f"Stopping stream {stop_stream_body.stream_id}")
124
125 - return stop_stream(stop_stream_body.stream_id)
125 + return await stop_stream(stop_stream_body.stream_id)
126
127
128 @graylog_management_router.post(
@@ -134,4 +134,4 @@ async def stop_stream_route(stop_stream_body: StopStreamBody = Depends(verify_st
134 async def start_stream_route(start_stream_body: StartStreamBody = Depends(verify_stream_id)) -> StartStreamResponse:
135 logger.info(f"Starting stream {start_stream_body.stream_id}")
136
137 - return start_stream(start_stream_body.stream_id)
137 + return await start_stream(start_stream_body.stream_id)
backend/app/connectors/graylog/routes/monitoring.py
+2 -2
@@ -23,7 +23,7 @@ graylog_monitoring_router = APIRouter()
23 async def get_all_messages(page_number: int = 1) -> GraylogMessagesResponse:
24 logger.info("Fetching all graylog messages")
25 logger.info(f"Page number: {page_number}")
26 - return get_messages(page_number)
26 + return await get_messages(page_number)
27
28
29 @graylog_monitoring_router.get(
@@ -34,4 +34,4 @@ async def get_all_messages(page_number: int = 1) -> GraylogMessagesResponse:
34 )
35 async def get_all_metrics() -> GraylogMetricsResponse:
36 logger.info("Fetching all graylog metrics")
37 - return get_metrics()
37 + return await get_metrics()
backend/app/connectors/graylog/routes/pipelines.py
+5 -5
@@ -55,7 +55,7 @@ def transform_pipeline_with_rule_ids(pipeline: Pipeline, rule_title_to_id: Dict[
55 )
56 async def get_all_pipelines() -> GraylogPipelinesResponse:
57 logger.info("Fetching all graylog pipelines")
58 - return get_pipelines()
58 + return await get_pipelines()
59
60
61 @graylog_pipelines_router.get(
@@ -65,8 +65,8 @@ async def get_all_pipelines() -> GraylogPipelinesResponse:
65 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
66 )
67 async def get_all_pipelines_with_rule_ids() -> GraylogPipelinesResponseWithRuleID:
68 - pipelines_response = get_pipelines()
69 - pipeline_rules_response = get_pipeline_rules()
68 + pipelines_response = await get_pipelines()
69 + pipeline_rules_response = await get_pipeline_rules()
70
71 rule_title_to_id = create_rule_title_to_id_dict(pipeline_rules_response.pipeline_rules)
72
@@ -87,7 +87,7 @@ async def get_all_pipelines_with_rule_ids() -> GraylogPipelinesResponseWithRuleI
87 )
88 async def get_all_pipeline_rules() -> PipelineRulesResponse:
89 logger.info("Fetching all graylog pipeline rules")
90 - return get_pipeline_rules()
90 + return await get_pipeline_rules()
91
92
93 @graylog_pipelines_router.get(
@@ -98,4 +98,4 @@ async def get_all_pipeline_rules() -> PipelineRulesResponse:
98 )
99 async def get_pipeline_rules_for_pipeline(pipeline_id: str) -> PipelineRulesResponse:
100 logger.info(f"Fetching all graylog pipeline rules for pipeline {pipeline_id}")
101 - return get_pipeline_rule_by_id(pipeline_id)
101 + return await get_pipeline_rule_by_id(pipeline_id)
backend/app/connectors/graylog/routes/streams.py
+1 -1
@@ -20,4 +20,4 @@ graylog_streams_router = APIRouter()
20 )
21 async def get_all_streams() -> GraylogStreamsResponse:
22 logger.info("Fetching all graylog streams")
23 - return get_streams()
23 + return await get_streams()
backend/app/connectors/graylog/services/collector.py
+17 -17
@@ -14,10 +14,10 @@ from app.connectors.graylog.schema.collector import RunningInputsResponse
14 from app.connectors.graylog.utils.universal import send_get_request
15
16
17 -def get_indices_full() -> GraylogIndicesResponse:
17 +async def get_indices_full() -> GraylogIndicesResponse:
18 """Get indices from Graylog."""
19 logger.info("Getting indices from Graylog")
20 - indices_collected = send_get_request(endpoint="/api/system/indexer/indices")
20 + indices_collected = await send_get_request(endpoint="/api/system/indexer/indices")
21 if indices_collected["success"]:
22 try:
23 indices_data = indices_collected["data"]["all"]["indices"]
@@ -32,8 +32,8 @@ def get_indices_full() -> GraylogIndicesResponse:
32 return GraylogIndicesResponse(indices=[], success=False, message="Failed to collect indices")
33
34
35 -def fetch_configured_inputs() -> Tuple[bool, List[ConfiguredInput]]:
36 - configured_inputs_collected = send_get_request(endpoint="/api/system/inputs")
35 +async def fetch_configured_inputs() -> Tuple[bool, List[ConfiguredInput]]:
36 + configured_inputs_collected = await send_get_request(endpoint="/api/system/inputs")
37 success = configured_inputs_collected.get("success", False)
38
39 if success:
@@ -43,8 +43,8 @@ def fetch_configured_inputs() -> Tuple[bool, List[ConfiguredInput]]:
43 return False, []
44
45
46 -def fetch_running_inputs() -> Tuple[bool, List[RunningInput]]:
47 - running_inputs_collected = send_get_request(endpoint="/api/system/inputstates")
46 +async def fetch_running_inputs() -> Tuple[bool, List[RunningInput]]:
47 + running_inputs_collected = await send_get_request(endpoint="/api/system/inputstates")
48 success = running_inputs_collected.get("success", False)
49
50 if success:
@@ -54,12 +54,12 @@ def fetch_running_inputs() -> Tuple[bool, List[RunningInput]]:
54 return False, []
55
56
57 -def get_inputs() -> GraylogInputsResponse:
57 +async def get_inputs() -> GraylogInputsResponse:
58 """Get inputs from Graylog."""
59 logger.info("Getting inputs from Graylog")
60
61 - config_success, configured_inputs_list = fetch_configured_inputs()
62 - run_success, running_inputs_list = fetch_running_inputs()
61 + config_success, configured_inputs_list = await fetch_configured_inputs()
62 + run_success, running_inputs_list = await fetch_running_inputs()
63
64 if config_success and run_success:
65 logger.info("Successfully fetched both configured and running inputs")
@@ -74,18 +74,18 @@ def get_inputs() -> GraylogInputsResponse:
74 return GraylogInputsResponse(configured_inputs=[], running_inputs=[], success=False, message="Failed to collect inputs")
75
76
77 -def get_inputs_running() -> RunningInputsResponse:
77 +async def get_inputs_running() -> RunningInputsResponse:
78 """Get running inputs from Graylog."""
79 logger.info("Getting running inputs from Graylog")
80 - run_success, running_inputs_list = fetch_running_inputs()
80 + run_success, running_inputs_list = await fetch_running_inputs()
81 if run_success:
82 return RunningInputsResponse(running_inputs=running_inputs_list, success=True, message="Successfully retrieved running inputs")
83
84
85 -def get_inputs_configured() -> ConfiguredInputsResponse:
85 +async def get_inputs_configured() -> ConfiguredInputsResponse:
86 """Get configured inputs from Graylog."""
87 logger.info("Getting configured inputs from Graylog")
88 - config_success, configured_inputs_list = fetch_configured_inputs()
88 + config_success, configured_inputs_list = await fetch_configured_inputs()
89 if config_success:
90 return ConfiguredInputsResponse(
91 configured_inputs=configured_inputs_list,
@@ -94,7 +94,7 @@ def get_inputs_configured() -> ConfiguredInputsResponse:
94 )
95
96
97 -def get_index_names() -> List[str]:
97 +async def get_index_names() -> List[str]:
98 """
99 Gets the names of all the indices in Graylog.
100
@@ -103,7 +103,7 @@ def get_index_names() -> List[str]:
103 """
104 logger.info("Getting index names from Graylog")
105
106 - indices_collected = get_indices_full()
106 + indices_collected = await get_indices_full()
107
108 if indices_collected.success:
109 # Access the index_name attribute directly
@@ -112,7 +112,7 @@ def get_index_names() -> List[str]:
112 return []
113
114
115 -def get_input_ids() -> List[str]:
115 +async def get_input_ids() -> List[str]:
116 """
117 Gets the IDs of all the inputs in Graylog.
118
@@ -121,7 +121,7 @@ def get_input_ids() -> List[str]:
121 """
122 logger.info("Getting input IDs from Graylog")
123
124 - success, inputs_collected = fetch_configured_inputs()
124 + success, inputs_collected = await fetch_configured_inputs()
125
126 if success:
127 # Access the input_id attribute directly
backend/app/connectors/graylog/services/events.py
+4 -4
@@ -13,10 +13,10 @@ from app.connectors.graylog.utils.universal import send_get_request
13 from app.connectors.graylog.utils.universal import send_post_request
14
15
16 -def get_event_definitions() -> GraylogEventDefinitionsResponse:
16 +async def get_event_definitions() -> GraylogEventDefinitionsResponse:
17 """Get event definitions from Graylog."""
18 logger.info("Getting event definitions from Graylog")
19 - event_definitions_collected = send_get_request(endpoint="/api/events/definitions")
19 + event_definitions_collected = await send_get_request(endpoint="/api/events/definitions")
20 if event_definitions_collected["success"]:
21 try:
22 event_definitions_data = event_definitions_collected["data"]["event_definitions"]
@@ -35,9 +35,9 @@ def get_event_definitions() -> GraylogEventDefinitionsResponse:
35 return GraylogEventDefinitionsResponse(event_definitions=[], success=False, message="Failed to collect event definitions")
36
37
38 -def get_alerts(alert_query: AlertQuery) -> GraylogAlertsResponse:
38 +async def get_alerts(alert_query: AlertQuery) -> GraylogAlertsResponse:
39 logger.info("Getting alerts from Graylog")
40 - response = send_post_request(endpoint="/api/events/search", data=alert_query.dict())
40 + response = await send_post_request(endpoint="/api/events/search", data=alert_query.dict())
41
42 if response["success"]:
43 try:
backend/app/connectors/graylog/services/management.py
+11 -11
@@ -16,12 +16,12 @@ from app.connectors.graylog.utils.universal import send_post_request
16 from app.connectors.graylog.utils.universal import send_put_request
17
18
19 -def delete_index(index_name: DeletedIndexBody) -> DeletedIndexResponse:
19 +async def delete_index(index_name: DeletedIndexBody) -> DeletedIndexResponse:
20 """Delete an index from Graylog."""
21 logger.info(f"Deleting index {index_name} from Graylog")
22 - send_delete_request(endpoint=f"/api/system/indexer/indices/{index_name}")
22 + await send_delete_request(endpoint=f"/api/system/indexer/indices/{index_name}")
23 # Check if the index still exists
24 - index_names = get_index_names()
24 + index_names = await get_index_names()
25 logger.info(f"Index names: {index_names}")
26 if index_name in index_names:
27 return DeletedIndexResponse(
@@ -32,30 +32,30 @@ def delete_index(index_name: DeletedIndexBody) -> DeletedIndexResponse:
32 return DeletedIndexResponse(success=True, message=f"Successfully deleted index {index_name}")
33
34
35 -def stop_input(input_id: StopInputBody) -> StopInputResponse:
35 +async def stop_input(input_id: StopInputBody) -> StopInputResponse:
36 """Stop an input in Graylog."""
37 logger.info(f"Stopping input {input_id} in Graylog")
38 - response = send_delete_request(endpoint=f"/api/system/inputstates/{input_id}")
38 + response = await send_delete_request(endpoint=f"/api/system/inputstates/{input_id}")
39 if response["success"]:
40 return StopInputResponse(success=True, message=f"Successfully stopped input {input_id}")
41 else:
42 return StopInputResponse(success=False, message=f"Failed to stop input {input_id}")
43
44
45 -def start_input(input_id: StartInputBody) -> StartInputResponse:
45 +async def start_input(input_id: StartInputBody) -> StartInputResponse:
46 """Start an input in Graylog."""
47 logger.info(f"Starting input {input_id} in Graylog")
48 - response = send_put_request(endpoint=f"/api/system/inputstates/{input_id}")
48 + response = await send_put_request(endpoint=f"/api/system/inputstates/{input_id}")
49 if response["success"]:
50 return StartInputResponse(success=True, message=f"Successfully started input {input_id}")
51 else:
52 return StartInputResponse(success=False, message=f"Failed to start input {input_id}")
53
54
55 -def stop_stream(stream_id: StopStreamBody) -> StopStreamResponse:
55 +async def stop_stream(stream_id: StopStreamBody) -> StopStreamResponse:
56 """Stop a stream in Graylog."""
57 logger.info(f"Stopping stream {stream_id} in Graylog")
58 - response = send_post_request(endpoint=f"/api/streams/{stream_id}/pause")
58 + response = await send_post_request(endpoint=f"/api/streams/{stream_id}/pause")
59 logger.info(f"Response: {response}")
60 if response["success"]:
61 return StopStreamResponse(success=True, message=f"Successfully stopped stream {stream_id}")
@@ -63,10 +63,10 @@ def stop_stream(stream_id: StopStreamBody) -> StopStreamResponse:
63 return StopStreamResponse(success=False, message=f"Failed to stop stream {stream_id}")
64
65
66 -def start_stream(stream_id: StartStreamBody) -> StartStreamResponse:
66 +async def start_stream(stream_id: StartStreamBody) -> StartStreamResponse:
67 """Start a stream in Graylog."""
68 logger.info(f"Starting stream {stream_id} in Graylog")
69 - response = send_post_request(endpoint=f"/api/streams/{stream_id}/resume")
69 + response = await send_post_request(endpoint=f"/api/streams/{stream_id}/resume")
70 if response["success"]:
71 return StartStreamResponse(success=True, message=f"Successfully started stream {stream_id}")
72 else:
backend/app/connectors/graylog/services/monitoring.py
+9 -9
@@ -10,11 +10,11 @@ from app.connectors.graylog.schema.monitoring import GraylogUncommittedJournalEn
10 from app.connectors.graylog.utils.universal import send_get_request
11
12
13 -def get_messages(page_number: int) -> GraylogMessagesResponse:
13 +async def get_messages(page_number: int) -> GraylogMessagesResponse:
14 """Get messages from Graylog."""
15 logger.info("Getting messages from Graylog")
16 params = {"page": page_number}
17 - messages_collected = send_get_request(endpoint="/api/system/messages", params=params)
17 + messages_collected = await send_get_request(endpoint="/api/system/messages", params=params)
18 try:
19 if messages_collected["success"]:
20 graylog_messages_list = []
@@ -42,12 +42,12 @@ def get_messages(page_number: int) -> GraylogMessagesResponse:
42 return GraylogMessagesResponse(graylog_messages=[], success=False, message="Failed to collect messages")
43
44
45 -def fetch_metrics_from_graylog() -> dict:
46 - return send_get_request(endpoint="/api/system/metrics")
45 +async def fetch_metrics_from_graylog() -> dict:
46 + return await send_get_request(endpoint="/api/system/metrics")
47
48
49 -def fetch_uncommitted_journal_entries() -> dict:
50 - return send_get_request(endpoint="/api/system/journal")
49 +async def fetch_uncommitted_journal_entries() -> dict:
50 + return await send_get_request(endpoint="/api/system/journal")
51
52
53 def merge_metrics_data(throughput_metrics_collected: dict) -> dict:
@@ -66,10 +66,10 @@ def filter_and_create_throughput_metrics(merged_metrics: dict) -> list:
66 return throughput_metrics_list
67
68
69 -def get_metrics() -> GraylogMetricsResponse:
69 +async def get_metrics() -> GraylogMetricsResponse:
70 logger.info("Getting metrics from Graylog")
71 - throughput_metrics_collected = fetch_metrics_from_graylog()
72 - uncommitted_journal_entries_collected = fetch_uncommitted_journal_entries()
71 + throughput_metrics_collected = await fetch_metrics_from_graylog()
72 + uncommitted_journal_entries_collected = await fetch_uncommitted_journal_entries()
73 try:
74 if throughput_metrics_collected["success"] and uncommitted_journal_entries_collected["success"]:
75 merged_metrics = merge_metrics_data(throughput_metrics_collected)
backend/app/connectors/graylog/services/pipelines.py
+7 -7
@@ -8,10 +8,10 @@ from app.connectors.graylog.schema.pipelines import PipelineRulesResponse
8 from app.connectors.graylog.utils.universal import send_get_request
9
10
11 -def get_pipelines() -> GraylogPipelinesResponse:
11 +async def get_pipelines() -> GraylogPipelinesResponse:
12 """Get pipelines from Graylog."""
13 logger.info("Getting pipelines from Graylog")
14 - pipelines_collected = send_get_request(endpoint="/api/system/pipelines/pipeline")
14 + pipelines_collected = await send_get_request(endpoint="/api/system/pipelines/pipeline")
15 try:
16 if pipelines_collected["success"]:
17 pipelines_list = [Pipeline(**pipeline_data) for pipeline_data in pipelines_collected["data"]]
@@ -24,10 +24,10 @@ def get_pipelines() -> GraylogPipelinesResponse:
24 raise HTTPException(status_code=500, detail=f"Failed to collect pipelines: {e}")
25
26
27 -def get_pipeline_rules() -> PipelineRulesResponse:
27 +async def get_pipeline_rules() -> PipelineRulesResponse:
28 """Get pipeline rules from Graylog."""
29 logger.info("Getting pipeline rules from Graylog")
30 - pipeline_rules_collected = send_get_request(endpoint="/api/system/pipelines/rule")
30 + pipeline_rules_collected = await send_get_request(endpoint="/api/system/pipelines/rule")
31 try:
32 if pipeline_rules_collected["success"]:
33 pipeline_rules_list = [PipelineRule(**pipeline_rule_data) for pipeline_rule_data in pipeline_rules_collected["data"]]
@@ -40,10 +40,10 @@ def get_pipeline_rules() -> PipelineRulesResponse:
40 raise HTTPException(status_code=500, detail=f"Failed to collect pipeline rules: {e}")
41
42
43 -def get_pipeline_rule_by_id(pipeline_id) -> PipelineRulesResponse:
43 +async def get_pipeline_rule_by_id(rule_id) -> PipelineRulesResponse:
44 """Get pipeline rules from Graylog."""
45 - logger.info(f"Getting pipeline rules from Graylog for pipeline {pipeline_id}")
46 - pipeline_rules_collected = send_get_request(endpoint=f"/api/system/pipelines/rule/{pipeline_id}")
45 + logger.info(f"Getting pipeline rules from Graylog for pipeline {rule_id}")
46 + pipeline_rules_collected = await send_get_request(endpoint=f"/api/system/pipelines/rule/{rule_id}")
47 logger.info(pipeline_rules_collected)
48 try:
49 if pipeline_rules_collected["success"]:
backend/app/connectors/graylog/services/streams.py
+4 -4
@@ -8,10 +8,10 @@ from app.connectors.graylog.schema.streams import Stream
8 from app.connectors.graylog.utils.universal import send_get_request
9
10
11 -def get_streams() -> GraylogStreamsResponse:
11 +async def get_streams() -> GraylogStreamsResponse:
12 """Get streams from Graylog."""
13 logger.info("Getting streams from Graylog")
14 - streams_collected = send_get_request(endpoint="/api/streams")
14 + streams_collected = await send_get_request(endpoint="/api/streams")
15 try:
16 if streams_collected["success"]:
17 streams_list = [Stream(**stream_data) for stream_data in streams_collected["data"]["streams"]]
@@ -31,10 +31,10 @@ def get_streams() -> GraylogStreamsResponse:
31 raise HTTPException(status_code=500, detail=f"Failed to collect streams: {e}")
32
33
34 -def get_stream_ids() -> List[str]:
34 +async def get_stream_ids() -> List[str]:
35 """Get stream IDs from Graylog."""
36 logger.info("Getting stream IDs from Graylog")
37 - streams_collected = send_get_request(endpoint="/api/streams")
37 + streams_collected = await send_get_request(endpoint="/api/streams")
38 try:
39 if streams_collected["success"]:
40 return [stream_data["id"] for stream_data in streams_collected["data"]["streams"]]
backend/app/connectors/graylog/utils/universal.py
+18 -12
@@ -7,11 +7,12 @@ from fastapi import HTTPException
7 from loguru import logger
8
9 from app.connectors.utils import get_connector_info_from_db
10 +from app.db.db_session import get_db_session
11
12 HEADERS = {"X-Requested-By": "CoPilot"}
13
14
14 -def verify_graylog_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
15 +async def verify_graylog_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
16 """
17 Verifies the connection to Graylog service.
18
@@ -50,19 +51,20 @@ def verify_graylog_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
51 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error: {e}"}
52
53
53 -def verify_graylog_connection(connector_name: str) -> str:
54 +async def verify_graylog_connection(connector_name: str) -> str:
55 """
56 Returns if connection to Graylog service is successful.
57 """
58 logger.info("Getting Graylog authentication token")
58 - attributes = get_connector_info_from_db(connector_name)
59 + async with get_db_session() as session: # This will correctly enter the context manager
60 + attributes = await get_connector_info_from_db(connector_name, session)
61 if attributes is None:
62 logger.error("No Graylog connector found in the database")
63 return None
62 - return verify_graylog_credentials(attributes)
64 + return await verify_graylog_credentials(attributes)
65
66
65 -def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
67 +async def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
68 """
69 Sends a GET request to the Graylog service.
70
@@ -75,7 +77,8 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
77 Dict[str, Any]: The response from the GET request.
78 """
79 logger.info(f"Sending GET request to {endpoint}")
78 - attributes = get_connector_info_from_db(connector_name)
80 + async with get_db_session() as session: # This will correctly enter the context manager
81 + attributes = await get_connector_info_from_db(connector_name, session)
82 if attributes is None:
83 logger.error("No Graylog connector found in the database")
84 return None
@@ -102,7 +105,7 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
105 raise HTTPException(status_code=500, detail=f"Failed to send GET request to {endpoint} with error: {e}")
106
107
105 -def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
108 +async def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
109 """
110 Sends a POST request to the Graylog service.
111
@@ -115,7 +118,8 @@ def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name
118 Dict[str, Any]: The response from the POST request.
119 """
120 logger.info(f"Sending POST request to {endpoint}")
118 - attributes = get_connector_info_from_db(connector_name)
121 + async with get_db_session() as session: # This will correctly enter the context manager
122 + attributes = await get_connector_info_from_db(connector_name, session)
123 if attributes is None:
124 logger.error("No Graylog connector found in the database")
125 return {"success": False, "message": "No Graylog connector found in the database"}
@@ -147,7 +151,7 @@ def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name
151 raise HTTPException(status_code=500, detail=f"Failed to send POST request to {endpoint} with error: {e}")
152
153
150 -def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
154 +async def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
155 """
156 Sends a DELETE request to the Graylog service.
157
@@ -160,7 +164,8 @@ def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None,
164 Dict[str, Any]: The response from the DELETE request.
165 """
166 logger.info(f"Sending DELETE request to {endpoint}")
163 - attributes = get_connector_info_from_db(connector_name)
167 + async with get_db_session() as session: # This will correctly enter the context manager
168 + attributes = await get_connector_info_from_db(connector_name, session)
169 if attributes is None:
170 logger.error("No Graylog connector found in the database")
171 return None
@@ -188,7 +193,7 @@ def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None,
193 return {"success": False, "message": f"Failed to send DELETE request to {endpoint} with error: {e}"}
194
195
191 -def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
196 +async def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
197 """
198 Sends a PUT request to the Graylog service.
199
@@ -201,7 +206,8 @@ def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, conne
206 Dict[str, Any]: The response from the PUT request.
207 """
208 logger.info(f"Sending PUT request to {endpoint}")
204 - attributes = get_connector_info_from_db(connector_name)
209 + async with get_db_session() as session: # This will correctly enter the context manager
210 + attributes = await get_connector_info_from_db(connector_name, session)
211 if attributes is None:
212 logger.error("No Graylog connector found in the database")
213 return None
backend/app/connectors/models.py
+1 -1
@@ -68,7 +68,7 @@ class Connectors(SQLModel, table=True):
68 connector_accepts_file: bool = Field(default=False)
69
70 # Relationship
71 - history_logs: List[ConnectorHistory] = Relationship(back_populates="connector")
71 + history_logs: List[ConnectorHistory] = Relationship(back_populates="connector", sa_relationship_kwargs={"lazy": "selectin"})
72
73
74 # Example usage
backend/app/connectors/routes.py
+21 -11
@@ -2,11 +2,13 @@ from typing import Union
2
3 ## Auth Things
4 from fastapi import APIRouter
5 +from fastapi import Depends
6 from fastapi import File
7 from fastapi import HTTPException
8 from fastapi import Security
9 from fastapi import UploadFile
10 from loguru import logger
11 +from sqlalchemy.ext.asyncio import AsyncSession
12
13 from app.auth.utils import AuthHandler
14 from app.connectors.schema import ConnectorListResponse
@@ -15,6 +17,7 @@ from app.connectors.schema import ConnectorsListResponse
17 from app.connectors.schema import UpdateConnector
18 from app.connectors.schema import VerifyConnectorResponse
19 from app.connectors.services import ConnectorServices
20 +from app.db.db_session import get_session
21
22 connector_router = APIRouter()
23
@@ -25,7 +28,7 @@ connector_router = APIRouter()
28 description="Fetch all available connectors",
29 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
30 )
28 -async def get_connectors() -> ConnectorListResponse:
31 +async def get_connectors(session: AsyncSession = Depends(get_session)) -> ConnectorsListResponse:
32 """
33 Fetch all available connectors from the database.
34
@@ -38,8 +41,8 @@ async def get_connectors() -> ConnectorListResponse:
41 Raises:
42 HTTPException: An exception with a 404 status code is raised if no connectors are found.
43 """
41 -
42 - connectors = ConnectorServices.fetch_all_connectors()
44 + connectors = await ConnectorServices.fetch_all_connectors(session=session)
45 + logger.info(f"Connectors: {connectors}")
46 if connectors:
47 return {"connectors": connectors, "success": True, "message": "Connectors fetched successfully"}
48 else:
@@ -52,7 +55,7 @@ async def get_connectors() -> ConnectorListResponse:
55 description="Fetch a specific connector",
56 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
57 )
55 -async def get_connector(connector_id: int) -> Union[ConnectorResponse, HTTPException]:
58 +async def get_connector(connector_id: int, session: AsyncSession = Depends(get_session)) -> Union[ConnectorResponse, HTTPException]:
59 """
60 Fetch a specific connector by its ID.
61
@@ -67,7 +70,7 @@ async def get_connector(connector_id: int) -> Union[ConnectorResponse, HTTPExcep
70 Raises:
71 HTTPException: An exception with a 404 status code is raised if the connector is not found.
72 """
70 - connector = ConnectorServices.fetch_connector_by_id(connector_id)
73 + connector = await ConnectorServices.fetch_connector_by_id(connector_id, session=session)
74 if connector is not None:
75 return {"connector": connector, "success": True, "message": "Connector fetched successfully"}
76 else:
@@ -80,7 +83,10 @@ async def get_connector(connector_id: int) -> Union[ConnectorResponse, HTTPExcep
83 description="Verify a connector. Makes an API call to the connector to verify it is working.",
84 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
85 )
83 -async def verify_connector(connector_id: int) -> Union[VerifyConnectorResponse, HTTPException]:
86 +async def verify_connector(
87 + connector_id: int,
88 + session: AsyncSession = Depends(get_session),
89 +) -> Union[VerifyConnectorResponse, HTTPException]:
90 """
91 Verify a connector by its ID.
92
@@ -95,7 +101,7 @@ async def verify_connector(connector_id: int) -> Union[VerifyConnectorResponse,
101 Raises:
102 HTTPException: An exception with a 404 status code is raised if the connector is not found.
103 """
98 - connector = ConnectorServices.verify_connector_by_id(connector_id)
104 + connector = await ConnectorServices.verify_connector_by_id(connector_id, session=session)
105 if connector is None:
106 raise HTTPException(status_code=404, detail=f"No connector found for ID: {connector_id}".format(connector_id=connector_id))
107 if connector["connectionSuccessful"] is False:
@@ -109,7 +115,11 @@ async def verify_connector(connector_id: int) -> Union[VerifyConnectorResponse,
115 description="Update a connector",
116 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
117 )
112 -async def update_connector(connector_id: int, connector: UpdateConnector) -> ConnectorListResponse:
118 +async def update_connector(
119 + connector_id: int,
120 + connector: UpdateConnector,
121 + session: AsyncSession = Depends(get_session),
122 +) -> ConnectorListResponse:
123 """
124 Update a connector by its ID.
125
@@ -125,7 +135,7 @@ async def update_connector(connector_id: int, connector: UpdateConnector) -> Con
135 Raises:
136 HTTPException: An exception with a 404 status code is raised if the connector is not found.
137 """
128 - updated_connector = ConnectorServices.update_connector_by_id(connector_id, connector)
138 + updated_connector = await ConnectorServices.update_connector_by_id(connector_id, connector, session=session)
139 if updated_connector is not None:
140 return {"connector": updated_connector, "success": True, "message": "Connector updated successfully"}
141 else:
@@ -137,7 +147,7 @@ async def update_connector(connector_id: int, connector: UpdateConnector) -> Con
147 description="Upload a YAML file for a specific connector",
148 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
149 )
140 -async def upload_yaml_file(connector_id: int, file: UploadFile = File(...)) -> dict:
150 +async def upload_yaml_file(connector_id: int, file: UploadFile = File(...), session: AsyncSession = Depends(get_session)) -> dict:
151 """
152 Upload a YAML file for a specific connector ID.
153
@@ -159,7 +169,7 @@ async def upload_yaml_file(connector_id: int, file: UploadFile = File(...)) -> d
169 if not file.filename.endswith(".yaml"):
170 raise HTTPException(status_code=400, detail="Only .yaml files are allowed.")
171 try:
162 - save_file_result = ConnectorServices.save_file(file)
172 + save_file_result = await ConnectorServices.save_file(file, session=session)
173 if save_file_result:
174 return {"success": True, "message": "File uploaded successfully"}
175 else:
backend/app/connectors/services.py
+302 -144
@@ -5,11 +5,16 @@ from typing import Generator
5 from typing import List
6 from typing import Optional
7 from typing import Type
8 +from typing import Union
9
10 +import aiofiles
11 from fastapi import UploadFile
12 from loguru import logger
13 from pydantic import BaseModel
12 -from sqlmodel import Session
14 +from sqlalchemy.ext.asyncio import AsyncSession
15 +from sqlalchemy.future import select
16 +
17 +# from sqlmodel import Session
18 from sqlmodel import select
19 from werkzeug.utils import secure_filename
20
@@ -23,7 +28,9 @@ from app.connectors.sublime.utils.universal import verify_sublime_connection
28 from app.connectors.velociraptor.utils.universal import verify_velociraptor_connection
29 from app.connectors.wazuh_indexer.utils.universal import verify_wazuh_indexer_connection
30 from app.connectors.wazuh_manager.utils.universal import verify_wazuh_manager_connection
26 -from app.db.db_session import engine # Import the shared engine
31 +
32 +# from app.db.db_session import engine # Import the shared engine
33 +from app.db.db_session import get_session
34
35 UPLOAD_FOLDER = "file-store"
36 UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), UPLOAD_FOLDER)
@@ -32,56 +39,56 @@ ALLOWED_EXTENSIONS = set(["yaml"]) # replace with your allowed file extensions
39
40 # Create an interface for connector services
41 class ConnectorServiceInterface(BaseModel):
35 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
42 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
43 raise NotImplementedError
44
45
46 # Wazuh Manager Service
47 class WazuhManagerService(ConnectorServiceInterface):
41 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
42 - return verify_wazuh_manager_connection(connector.connector_name)
48 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
49 + return await verify_wazuh_manager_connection(connector.connector_name)
50
51
52 # Wazuh Indexer Service
53 class WazuhIndexerService(ConnectorServiceInterface):
47 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
48 - return verify_wazuh_indexer_connection(connector.connector_name)
54 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
55 + return await verify_wazuh_indexer_connection(connector.connector_name)
56
57
58 # Velociraptor Service
59 class VelociraptorService(ConnectorServiceInterface):
53 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
54 - return verify_velociraptor_connection(connector.connector_name)
60 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
61 + return await verify_velociraptor_connection(connector.connector_name)
62
63
64 # Graylog Service
65 class GraylogService(ConnectorServiceInterface):
59 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
60 - return verify_graylog_connection(connector.connector_name)
66 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
67 + return await verify_graylog_connection(connector.connector_name)
68
69
70 # DFIR-IRIS Service
71 class DfirIrisService(ConnectorServiceInterface):
65 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
66 - return verify_dfir_iris_connection(connector.connector_name)
72 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
73 + return await verify_dfir_iris_connection(connector.connector_name)
74
75
76 # Cortex Service
77 class CortexService(ConnectorServiceInterface):
71 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
72 - return verify_cortex_connection(connector.connector_name)
78 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
79 + return await verify_cortex_connection(connector.connector_name)
80
81
82 # Shuffle Service
83 class ShuffleService(ConnectorServiceInterface):
77 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
78 - return verify_shuffle_connection(connector.connector_name)
84 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
85 + return await verify_shuffle_connection(connector.connector_name)
86
87
88 # Sublime Service
89 class SublimeService(ConnectorServiceInterface):
83 - def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
84 - return verify_sublime_connection(connector.connector_name)
90 + async def verify_authentication(self, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
91 + return await verify_sublime_connection(connector.connector_name)
92
93
94 # Factory function to create a service instance based on connector name
@@ -104,121 +111,245 @@ class ConnectorServices:
111 Service class for handling operations related to connectors.
112 """
113
107 - @staticmethod
108 - @contextmanager
109 - def get_session() -> Generator[Session, None, None]:
110 - """
111 - Get a new session for database interaction.
112 -
113 - This method is a context manager, which ensures that the session is closed
114 - once the operations within the context are completed.
115 -
116 - Yields:
117 - Session: The database session object.
118 - """
119 - session = Session(engine)
120 - try:
121 - yield session
122 - finally:
123 - session.close()
124 -
114 + # @staticmethod
115 + # @contextmanager
116 + # def get_session() -> Generator[Session, None, None]:
117 + # """
118 + # Get a new session for database interaction.
119 +
120 + # This method is a context manager, which ensures that the session is closed
121 + # once the operations within the context are completed.
122 +
123 + # Yields:
124 + # Session: The database session object.
125 + # """
126 + # session = Session(engine)
127 + # try:
128 + # yield session
129 + # finally:
130 + # session.close()
131 +
132 + # @classmethod
133 + # def fetch_all_connectors(cls) -> List[ConnectorResponse]:
134 + # """
135 + # Fetch all connectors from the database.
136 +
137 + # This method retrieves all connector records from the database, converts them
138 + # to Pydantic models, and returns them as a list.
139 +
140 + # Returns:
141 + # List[ConnectorResponse]: A list of connectors in their Pydantic representation.
142 + # """
143 + # # Get a new session
144 + # with cls.get_session() as session:
145 + # query = select(Connectors)
146 + # connectors = session.exec(query).all()
147 +
148 + # # Convert the SQLModel object to a Pydantic model
149 + # connector_responses = [ConnectorResponse.from_orm(connector) for connector in connectors]
150 + # return connector_responses
151 +
152 + # @classmethod
153 + # async def fetch_all_connectors(cls) -> List[ConnectorResponse]:
154 + # async with get_db_session() as session:
155 + # result = await session.execute(select(Connectors))
156 + # connectors = result.scalars().all()
157 + # connector_responses = [ConnectorResponse.from_orm(connector) for connector in connectors]
158 + # return connector_responses
159 +
160 + # ! Working Async
161 + # @classmethod
162 + # async def fetch_all_connectors(cls) -> List[ConnectorResponse]:
163 + # async with get_db_session() as session:
164 + # result = await session.execute(select(Connectors))
165 + # connectors = result.scalars().all()
166 + # logger.info(f"Connectors: {connectors}")
167 + # connector_responses = [ConnectorResponse.from_orm(connector) for connector in connectors]
168 + # return connector_responses
169 @classmethod
126 - def fetch_all_connectors(cls) -> List[ConnectorResponse]:
127 - """
128 - Fetch all connectors from the database.
129 -
130 - This method retrieves all connector records from the database, converts them
131 - to Pydantic models, and returns them as a list.
132 -
133 - Returns:
134 - List[ConnectorResponse]: A list of connectors in their Pydantic representation.
135 - """
136 - # Get a new session
137 - with cls.get_session() as session:
138 - query = select(Connectors)
139 - connectors = session.exec(query).all()
140 -
141 - # Convert the SQLModel object to a Pydantic model
142 - connector_responses = [ConnectorResponse.from_orm(connector) for connector in connectors]
143 - return connector_responses
170 + async def fetch_all_connectors(cls, session: AsyncSession) -> List[ConnectorResponse]:
171 + try:
172 + result = await session.execute(select(Connectors))
173 + except Exception as e:
174 + logger.exception(f"Failed to fetch all connectors: {e}")
175 + exit(0)
176 + connectors = result.scalars().all()
177 + return [ConnectorResponse.from_orm(connector) for connector in connectors]
178
179 @classmethod
146 - def fetch_connector_by_id(cls, connector_id: int) -> Optional[ConnectorResponse]:
147 - """
148 - Fetch a connector by its ID from the database.
149 -
150 - Given a connector ID, this method retrieves the corresponding connector
151 - record from the database, if it exists.
152 -
153 - Args:
154 - connector_id (int): The ID of the connector to fetch.
155 -
156 - Returns:
157 - Optional[ConnectorResponse]: The connector in its Pydantic representation, or None if not found.
158 - """
159 - # Get a new session
160 - with cls.get_session() as session:
161 - query = select(Connectors).where(Connectors.id == connector_id)
162 - connector = session.exec(query).first()
163 -
164 - if not connector:
165 - logger.info(f"No connector found for ID: {connector_id}")
166 - return None
167 -
168 - try:
169 - # Convert the SQLModel object to a Pydantic model
170 - connector_response = ConnectorResponse.from_orm(connector)
171 - return connector_response
172 - except Exception as e:
173 - logger.exception(f"Failed to create ConnectorResponse object: {e}")
174 - return None
180 + async def fetch_connector_by_id(cls, connector_id: int, session: AsyncSession) -> Optional[ConnectorResponse]:
181 + result = await session.execute(select(Connectors).where(Connectors.id == connector_id))
182 + connector = result.scalar_one_or_none()
183 + if connector:
184 + return ConnectorResponse.from_orm(connector)
185 + return None
186 +
187 + # @classmethod
188 + # def fetch_connector_by_id(cls, connector_id: int) -> Optional[ConnectorResponse]:
189 + # """
190 + # Fetch a connector by its ID from the database.
191 +
192 + # Given a connector ID, this method retrieves the corresponding connector
193 + # record from the database, if it exists.
194 +
195 + # Args:
196 + # connector_id (int): The ID of the connector to fetch.
197 +
198 + # Returns:
199 + # Optional[ConnectorResponse]: The connector in its Pydantic representation, or None if not found.
200 + # """
201 + # # Get a new session
202 + # with cls.get_session() as session:
203 + # query = select(Connectors).where(Connectors.id == connector_id)
204 + # connector = session.exec(query).first()
205 +
206 + # if not connector:
207 + # logger.info(f"No connector found for ID: {connector_id}")
208 + # return None
209 +
210 + # try:
211 + # # Convert the SQLModel object to a Pydantic model
212 + # connector_response = ConnectorResponse.from_orm(connector)
213 + # return connector_response
214 + # except Exception as e:
215 + # logger.exception(f"Failed to create ConnectorResponse object: {e}")
216 + # return None
217 +
218 + # @classmethod
219 + # def verify_connector_by_id(cls, connector_id: int) -> Optional[ConnectorResponse]:
220 + # """
221 + # Verify a connector by making an API call to it.
222 +
223 + # Given a connector ID, this method retrieves the corresponding connector
224 + # record from the database, if it exists, and makes an API call to the connector.
225 +
226 + # Args:
227 + # connector_id (int): The ID of the connector to verify.
228 +
229 + # Returns:
230 + # Optional[ConnectorResponse]: The connector in its Pydantic representation, or None if not found.
231 + # """
232 + # # Get a new session
233 + # with cls.get_session() as session:
234 + # query = select(Connectors).where(Connectors.id == connector_id)
235 + # connector = session.exec(query).first()
236 +
237 + # if not connector:
238 + # logger.info(f"No connector found for ID: {connector_id}")
239 + # return None
240 +
241 + # try:
242 + # # Convert the SQLModel object to a Pydantic model
243 + # connector_response = ConnectorResponse.from_orm(connector)
244 +
245 + # # Get the appropriate service for this connector
246 + # ServiceClass = get_connector_service(connector_response.connector_name)
247 +
248 + # if ServiceClass is not None:
249 + # service_instance = ServiceClass()
250 + # connector_response = service_instance.verify_authentication(connector_response)
251 + # else:
252 + # logger.error(f"Connector type {connector_response.connector_name} is not supported")
253 + # return None
254 +
255 + # return connector_response
256 + # except Exception as e:
257 + # logger.exception(f"Failed to create ConnectorResponse object: {e}")
258 + # return None
259
260 @classmethod
177 - def verify_connector_by_id(cls, connector_id: int) -> Optional[ConnectorResponse]:
261 + async def verify_connector_by_id(cls, connector_id: int, session: AsyncSession) -> Optional[ConnectorResponse]:
262 """
179 - Verify a connector by making an API call to it.
263 + Verify a connector by making an API call to it asynchronously.
264
265 Given a connector ID, this method retrieves the corresponding connector
266 record from the database, if it exists, and makes an API call to the connector.
267
268 Args:
269 connector_id (int): The ID of the connector to verify.
270 + session (AsyncSession): The SQLAlchemy asynchronous session to use.
271
272 Returns:
273 Optional[ConnectorResponse]: The connector in its Pydantic representation, or None if not found.
274 """
190 - # Get a new session
191 - with cls.get_session() as session:
192 - query = select(Connectors).where(Connectors.id == connector_id)
193 - connector = session.exec(query).first()
275 + query = select(Connectors).where(Connectors.id == connector_id)
276 + connector = (await session.execute(query)).scalars().first()
277
195 - if not connector:
196 - logger.info(f"No connector found for ID: {connector_id}")
197 - return None
278 + if not connector:
279 + logger.info(f"No connector found for ID: {connector_id}")
280 + return None
281
199 - try:
200 - # Convert the SQLModel object to a Pydantic model
201 - connector_response = ConnectorResponse.from_orm(connector)
202 -
203 - # Get the appropriate service for this connector
204 - ServiceClass = get_connector_service(connector_response.connector_name)
282 + try:
283 + # Convert the SQLModel object to a Pydantic model
284 + connector_response = ConnectorResponse.from_orm(connector)
285
206 - if ServiceClass is not None:
207 - service_instance = ServiceClass()
208 - connector_response = service_instance.verify_authentication(connector_response)
209 - else:
210 - logger.error(f"Connector type {connector_response.connector_name} is not supported")
211 - return None
286 + # Get the appropriate service for this connector
287 + ServiceClass = get_connector_service(connector_response.connector_name)
288
213 - return connector_response
214 - except Exception as e:
215 - logger.exception(f"Failed to create ConnectorResponse object: {e}")
289 + if ServiceClass is not None:
290 + service_instance = ServiceClass()
291 + # If verify_authentication is an async function, you will need to await it
292 + connector_response = await service_instance.verify_authentication(connector_response)
293 + else:
294 + logger.error(f"Connector type {connector_response.connector_name} is not supported")
295 return None
296
297 + return connector_response
298 + except Exception as e:
299 + logger.exception(f"Failed to create ConnectorResponse object: {e}")
300 + return None
301 +
302 + # @classmethod
303 + # async def update_connector_by_id(cls, connector_id: int, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
304 + # """
305 + # Update a connector by its ID in the database.
306 +
307 + # Given a connector ID and a Pydantic representation of a connector, this method
308 + # updates the corresponding connector record in the database, if it exists.
309 +
310 + # Args:
311 + # connector_id (int): The ID of the connector to update.
312 + # connector (ConnectorResponse): The updated connector in its Pydantic representation.
313 +
314 + # Returns:
315 + # Optional[ConnectorResponse]: The updated connector in its Pydantic representation, or None if not found.
316 + # """
317 + # # Get a new session
318 + # with cls.get_session() as session:
319 + # query = select(Connectors).where(Connectors.id == connector_id)
320 + # connector_record = session.exec(query).first()
321 +
322 + # if not connector_record:
323 + # logger.info(f"No connector found for ID: {connector_id}")
324 + # return None
325 +
326 + # try:
327 + # # Update the connector record
328 + # connector_record.connector_url = connector.connector_url
329 + # connector_record.connector_username = connector.connector_username
330 + # connector_record.connector_password = connector.connector_password
331 + # connector_record.connector_api_key = connector.connector_api_key
332 + # connector_record.connector_last_updated = datetime.now()
333 +
334 + # # Commit the changes to the database
335 + # session.add(connector_record)
336 + # session.commit()
337 + # # Convert the SQLModel object to a Pydantic model
338 + # connector_response = ConnectorResponse.from_orm(connector_record)
339 + # return connector_response
340 + # except Exception as e:
341 + # logger.exception(f"Failed to update connector: {e}")
342 + # return Exception(f"Failed to update connector: {e}")
343 +
344 @classmethod
219 - def update_connector_by_id(cls, connector_id: int, connector: ConnectorResponse) -> Optional[ConnectorResponse]:
345 + async def update_connector_by_id(
346 + cls,
347 + connector_id: int,
348 + connector: ConnectorResponse,
349 + session: AsyncSession,
350 + ) -> Optional[ConnectorResponse]:
351 """
221 - Update a connector by its ID in the database.
352 + Update a connector by its ID in the database asynchronously.
353
354 Given a connector ID and a Pydantic representation of a connector, this method
355 updates the corresponding connector record in the database, if it exists.
@@ -226,59 +357,86 @@ class ConnectorServices:
357 Args:
358 connector_id (int): The ID of the connector to update.
359 connector (ConnectorResponse): The updated connector in its Pydantic representation.
360 + session (AsyncSession): The SQLAlchemy asynchronous session to use.
361
362 Returns:
363 Optional[ConnectorResponse]: The updated connector in its Pydantic representation, or None if not found.
364 """
233 - # Get a new session
234 - with cls.get_session() as session:
235 - query = select(Connectors).where(Connectors.id == connector_id)
236 - connector_record = session.exec(query).first()
365 + query = select(Connectors).where(Connectors.id == connector_id)
366 + connector_record = (await session.execute(query)).scalars().first()
367
238 - if not connector_record:
239 - logger.info(f"No connector found for ID: {connector_id}")
240 - return None
368 + if not connector_record:
369 + logger.info(f"No connector found for ID: {connector_id}")
370 + return None
371
242 - try:
243 - # Update the connector record
244 - connector_record.connector_url = connector.connector_url
245 - connector_record.connector_username = connector.connector_username
246 - connector_record.connector_password = connector.connector_password
247 - connector_record.connector_api_key = connector.connector_api_key
248 - connector_record.connector_last_updated = datetime.now()
372 + try:
373 + # Update the connector record
374 + connector_record.connector_url = connector.connector_url
375 + connector_record.connector_username = connector.connector_username
376 + connector_record.connector_password = connector.connector_password
377 + connector_record.connector_api_key = connector.connector_api_key
378 + connector_record.connector_last_updated = datetime.now()
379
250 - # Commit the changes to the database
251 - session.add(connector_record)
252 - session.commit()
380 + # Commit the changes to the database
381 + session.add(connector_record)
382 + await session.commit()
383
254 - # Convert the SQLModel object to a Pydantic model
255 - connector_response = ConnectorResponse.from_orm(connector_record)
256 - return connector_response
257 - except Exception as e:
258 - logger.exception(f"Failed to update connector: {e}")
259 - return Exception(f"Failed to update connector: {e}")
384 + # Convert the SQLModel object to a Pydantic model
385 + connector_response = ConnectorResponse.from_orm(connector_record)
386 + return connector_response
387 + except Exception as e:
388 + logger.exception(f"Failed to update connector: {e}")
389 + session.rollback()
390 + return Exception(f"Failed to update connector: {e}")
391
392 @staticmethod
393 def allowed_file(filename):
394 return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
395
396 + # @classmethod
397 + # def save_file(cls, file: UploadFile):
398 + # if file and cls.allowed_file(file.filename):
399 + # filename = secure_filename(file.filename)
400 + # file_path = os.path.join(UPLOAD_FOLDER, filename)
401 +
402 + # # Save the file
403 + # with open(file_path, "wb") as buffer:
404 + # buffer.write(file.file.read())
405 +
406 + # # Update connector
407 + # connector = cls.fetch_connector_by_id(6)
408 + # connector.connector_configured = True
409 + # connector.connector_api_key = file_path
410 + # cls.update_connector_by_id(6, connector)
411 +
412 + # connector_response = ConnectorResponse.from_orm(connector)
413 + # return connector_response
414 + # else:
415 + # return False
416 +
417 @classmethod
266 - def save_file(cls, file: UploadFile):
418 + async def save_file(cls, file: UploadFile, session: AsyncSession) -> Union[ConnectorResponse, bool]:
419 if file and cls.allowed_file(file.filename):
420 filename = secure_filename(file.filename)
421 file_path = os.path.join(UPLOAD_FOLDER, filename)
422
271 - # Save the file
272 - with open(file_path, "wb") as buffer:
273 - buffer.write(file.file.read())
423 + # Save the file asynchronously
424 + async with aiofiles.open(file_path, "wb") as buffer:
425 + await buffer.write(await file.read()) # Assuming file doesn't need to be read in chunks
426
275 - # Update connector
276 - connector = cls.fetch_connector_by_id(6)
277 - connector.connector_configured = True
278 - connector.connector_api_key = file_path
279 - cls.update_connector_by_id(6, connector)
427 + # Update connector using async session and ORM
428 + query = select(Connectors).where(Connectors.id == 6)
429 + connector_record = (await session.execute(query)).scalars().first()
430
281 - connector_response = ConnectorResponse.from_orm(connector)
282 - return connector_response
431 + if connector_record:
432 + connector_record.connector_configured = True
433 + connector_record.connector_api_key = file_path
434 + session.add(connector_record)
435 + await session.commit()
436 +
437 + connector_response = ConnectorResponse.from_orm(connector_record)
438 + return connector_response
439 + else:
440 + return False
441 else:
442 return False
backend/app/connectors/shuffle/routes/workflows.py
+16 -4
@@ -1,7 +1,9 @@
1 from fastapi import APIRouter
2 from fastapi import HTTPException
3 +from fastapi import Security
4 from loguru import logger
5
6 +from app.auth.utils import AuthHandler
7 from app.connectors.shuffle.schema.workflows import WorkflowExecutionBodyModel
8 from app.connectors.shuffle.schema.workflows import WorkflowExecutionResponseModel
9 from app.connectors.shuffle.schema.workflows import WorkflowsResponse
@@ -11,13 +13,23 @@ from app.connectors.shuffle.services.workflows import get_workflows
13 shuffle_workflows_router = APIRouter()
14
15
14 -@shuffle_workflows_router.get("", response_model=WorkflowsResponse, description="Get all workflows")
16 +@shuffle_workflows_router.get(
17 + "",
18 + response_model=WorkflowsResponse,
19 + description="Get all workflows",
20 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
21 +)
22 async def get_all_workflows() -> WorkflowsResponse:
23 logger.info("Fetching all workflows")
17 - return get_workflows()
24 + return await get_workflows()
25
26
20 -@shuffle_workflows_router.get("/executions", response_model=WorkflowExecutionResponseModel, description="Get all workflow executions")
27 +@shuffle_workflows_router.get(
28 + "/executions",
29 + response_model=WorkflowExecutionResponseModel,
30 + description="Get all workflow executions",
31 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
32 +)
33 async def get_all_workflow_executions() -> WorkflowExecutionResponseModel:
34 logger.info("Fetching all workflow executions")
35
@@ -37,7 +49,7 @@ async def get_all_workflow_executions() -> WorkflowExecutionResponseModel:
49 {
50 "workflow_id": workflow["id"],
51 "workflow_name": workflow["name"],
40 - "status": get_workflow_executions(WorkflowExecutionBodyModel(workflow_id=workflow["id"])),
52 + "status": await get_workflow_executions(WorkflowExecutionBodyModel(workflow_id=workflow["id"])),
53 },
54 )
55 return WorkflowExecutionResponseModel(success=True, message="Successfully fetched workflow executions", workflows=workflow_details)
backend/app/connectors/shuffle/services/workflows.py
+43 -15
@@ -1,3 +1,6 @@
1 +from typing import List
2 +
3 +from fastapi import HTTPException
4 from loguru import logger
5
6 from app.connectors.shuffle.schema.workflows import WorkflowExecutionBodyModel
@@ -6,28 +9,53 @@ from app.connectors.shuffle.schema.workflows import WorkflowsResponse
9 from app.connectors.shuffle.utils.universal import send_get_request
10
11
9 -def get_workflows() -> WorkflowsResponse:
12 +def remove_large_images_from_actions(workflows: List) -> List:
13 + """
14 + Removes the `large_image` keys from actions in each workflow.
15 + """
16 + for workflow in workflows:
17 + if "actions" in workflow:
18 + for action in workflow["actions"]:
19 + action.pop("large_image", None) # Use pop to avoid KeyError if 'large_image' does not exist
20 + return workflows
21 +
22 +
23 +async def get_workflows() -> WorkflowsResponse:
24 """
25 Returns a list of workflows.
26 """
27 logger.info("Getting workflows")
14 - response = send_get_request("/api/v1/workflows")
15 - if response is None:
16 - return WorkflowsResponse(success=False, message="Failed to get workflows", workflows=[])
17 - return WorkflowsResponse(success=True, message="Successfully fetched workflows", workflows=response["data"])
28 +
29 + try:
30 + response = await send_get_request("/api/v1/workflows")
31 + if response is None:
32 + return WorkflowsResponse(success=False, message="Failed to get workflows", workflows=[])
33 +
34 + workflows = response.get("data")
35 + workflows_without_large_images = remove_large_images_from_actions(workflows)
36 +
37 + return WorkflowsResponse(success=True, message="Successfully fetched workflows", workflows=workflows_without_large_images)
38 +
39 + except Exception as e:
40 + logger.error(f"Failed to get workflows with error: {e}")
41 + raise HTTPException(status_code=500, detail=f"Failed to get workflows with error: {e}")
42
43
20 -def get_workflow_executions(exection_body: WorkflowExecutionBodyModel) -> WorkflowExecutionStatusResponseModel:
44 +async def get_workflow_executions(exection_body: WorkflowExecutionBodyModel) -> WorkflowExecutionStatusResponseModel:
45 """
46 Returns a list of workflow executions.
47 """
48 logger.info("Getting workflow executions")
25 - response = send_get_request(f"/api/v1/workflows/{exection_body.workflow_id}/executions")
26 - executions = response["data"]
27 - if executions:
28 - status = executions[0]["status"]
29 - if status is None:
30 - status = "Never Ran"
31 - else:
32 - status = "No executions found"
33 - return WorkflowExecutionStatusResponseModel(last_run=status)
49 + response = await send_get_request(f"/api/v1/workflows/{exection_body.workflow_id}/executions")
50 + try:
51 + executions = response["data"]
52 + if executions:
53 + status = executions[0]["status"]
54 + if status is None:
55 + status = "Never Ran"
56 + else:
57 + status = "No executions found"
58 + return WorkflowExecutionStatusResponseModel(last_run=status)
59 + except Exception as e:
60 + logger.error(f"Failed to get workflow executions with error: {e}")
61 + raise HTTPException(status_code=500, detail=f"Failed to get workflow executions with error: {e}")
backend/app/connectors/shuffle/utils/universal.py
+28 -20
@@ -3,12 +3,14 @@ from typing import Dict
3 from typing import Optional
4
5 import requests
6 +from fastapi import HTTPException
7 from loguru import logger
8
9 from app.connectors.utils import get_connector_info_from_db
10 +from app.db.db_session import get_db_session
11
12
11 -def verify_shuffle_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
13 +async def verify_shuffle_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
14 """
15 Verifies the connection to Shuffle service.
16
@@ -47,19 +49,20 @@ def verify_shuffle_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
49 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error: {e}"}
50
51
50 -def verify_shuffle_connection(connector_name: str) -> str:
52 +async def verify_shuffle_connection(connector_name: str) -> str:
53 """
54 Returns if connection to Shuffle service is successful.
55 """
56 logger.info("Getting Shuffle authentication token")
55 - attributes = get_connector_info_from_db(connector_name)
57 + async with get_db_session() as session: # This will correctly enter the context manager
58 + attributes = await get_connector_info_from_db(connector_name, session)
59 if attributes is None:
60 logger.error("No Shuffle connector found in the database")
61 return None
59 - return verify_shuffle_credentials(attributes)
62 + return await verify_shuffle_credentials(attributes)
63
64
62 -def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Shuffle") -> Dict[str, Any]:
65 +async def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Shuffle") -> Dict[str, Any]:
66 """
67 Sends a GET request to the Shuffle service.
68
@@ -72,9 +75,10 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
75 Dict[str, Any]: The response from the GET request.
76 """
77 logger.info(f"Sending GET request to {endpoint}")
75 - attributes = get_connector_info_from_db(connector_name)
78 + async with get_db_session() as session: # This will correctly enter the context manager
79 + attributes = await get_connector_info_from_db(connector_name, session)
80 if attributes is None:
77 - logger.error("No Graylog connector found in the database")
81 + logger.error("No Shuffle connector found in the database")
82 return None
83 try:
84 HEADERS = {
@@ -89,17 +93,18 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
93 return {"data": response.json(), "success": True, "message": "Successfully retrieved data"}
94 except Exception as e:
95 logger.error(f"Failed to send GET request to {endpoint} with error: {e}")
96 + raise HTTPException(status_code=500, detail=f"Failed to send GET request to {endpoint} with error: {e}")
97 return {"success": False, "message": f"Failed to send GET request to {endpoint} with error: {e}"}
98
99
95 -def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
100 +def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name: str = "Shuffle") -> Dict[str, Any]:
101 """
97 - Sends a POST request to the Graylog service.
102 + Sends a POST request to the Shuffle service.
103
104 Args:
105 endpoint (str): The endpoint to send the POST request to.
106 data (Dict[str, Any]): The data to send with the POST request.
102 - connector_name (str, optional): The name of the connector to use. Defaults to "Graylog".
107 + connector_name (str, optional): The name of the connector to use. Defaults to "Shuffle".
108
109 Returns:
110 Dict[str, Any]: The response from the POST request.
@@ -107,8 +112,8 @@ def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name
112 logger.info(f"Sending POST request to {endpoint}")
113 attributes = get_connector_info_from_db(connector_name)
114 if attributes is None:
110 - logger.error("No Graylog connector found in the database")
111 - return {"success": False, "message": "No Graylog connector found in the database"}
115 + logger.error("No Shuffle connector found in the database")
116 + return {"success": False, "message": "No Shuffle connector found in the database"}
117
118 try:
119 HEADERS = {
@@ -136,17 +141,18 @@ def send_post_request(endpoint: str, data: Dict[str, Any] = None, connector_name
141 except Exception as e:
142 logger.debug(f"Response: {response}")
143 logger.error(f"Failed to send POST request to {endpoint} with error: {e}")
144 + raise HTTPException(status_code=500, detail=f"Failed to send POST request to {endpoint} with error: {e}")
145 return {"success": False, "message": f"Failed to send POST request to {endpoint} with error: {e}"}
146
147
142 -def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
148 +def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Shuffle") -> Dict[str, Any]:
149 """
144 - Sends a DELETE request to the Graylog service.
150 + Sends a DELETE request to the Shuffle service.
151
152 Args:
153 endpoint (str): The endpoint to send the DELETE request to.
154 params (Optional[Dict[str, Any]], optional): The parameters to send with the DELETE request. Defaults to None.
149 - connector_name (str, optional): The name of the connector to use. Defaults to "Graylog".
155 + connector_name (str, optional): The name of the connector to use. Defaults to "Shuffle".
156
157 Returns:
158 Dict[str, Any]: The response from the DELETE request.
@@ -154,7 +160,7 @@ def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None,
160 logger.info(f"Sending DELETE request to {endpoint}")
161 attributes = get_connector_info_from_db(connector_name)
162 if attributes is None:
157 - logger.error("No Graylog connector found in the database")
163 + logger.error("No Shuffle connector found in the database")
164 return None
165 try:
166 HEADERS = {
@@ -173,17 +179,18 @@ def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None,
179 return {"data": response.json(), "success": True, "message": "Successfully retrieved data"}
180 except Exception as e:
181 logger.error(f"Failed to send DELETE request to {endpoint} with error: {e}")
182 + raise HTTPException(status_code=500, detail=f"Failed to send DELETE request to {endpoint} with error: {e}")
183 return {"success": False, "message": f"Failed to send DELETE request to {endpoint} with error: {e}"}
184
185
179 -def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, connector_name: str = "Graylog") -> Dict[str, Any]:
186 +def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, connector_name: str = "Shuffle") -> Dict[str, Any]:
187 """
181 - Sends a PUT request to the Graylog service.
188 + Sends a PUT request to the Shuffle service.
189
190 Args:
191 endpoint (str): The endpoint to send the PUT request to.
192 data (Optional[Dict[str, Any]]): The data to send with the PUT request.
186 - connector_name (str, optional): The name of the connector to use. Defaults to "Graylog".
193 + connector_name (str, optional): The name of the connector to use. Defaults to "Shuffle".
194
195 Returns:
196 Dict[str, Any]: The response from the PUT request.
@@ -191,7 +198,7 @@ def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, conne
198 logger.info(f"Sending PUT request to {endpoint}")
199 attributes = get_connector_info_from_db(connector_name)
200 if attributes is None:
194 - logger.error("No Graylog connector found in the database")
201 + logger.error("No Shuffle connector found in the database")
202 return None
203 try:
204 HEADERS = {
@@ -210,4 +217,5 @@ def send_put_request(endpoint: str, data: Optional[Dict[str, Any]] = None, conne
217 return {"data": response.json(), "success": True, "message": "Successfully retrieved data"}
218 except Exception as e:
219 logger.error(f"Failed to send PUT request to {endpoint} with error: {e}")
220 + raise HTTPException(status_code=500, detail=f"Failed to send PUT request to {endpoint} with error: {e}")
221 return {"success": False, "message": f"Failed to send PUT request to {endpoint} with error: {e}"}
backend/app/connectors/sublime/routes/alerts.py
+15 -5
@@ -1,17 +1,22 @@
1 from fastapi import APIRouter
2 +from fastapi import Depends
3 +from fastapi import Security
4 from loguru import logger
5 +from sqlalchemy.ext.asyncio import AsyncSession
6
7 +from app.auth.utils import AuthHandler
8 from app.connectors.sublime.schema.alerts import AlertRequestBody
9 from app.connectors.sublime.schema.alerts import AlertResponseBody
10 from app.connectors.sublime.schema.alerts import SublimeAlertsResponse
11 from app.connectors.sublime.services.alerts import collect_alerts
12 from app.connectors.sublime.services.alerts import store_sublime_alert
13 +from app.db.db_session import get_session
14
15 sublime_alerts_router = APIRouter()
16
17
18 @sublime_alerts_router.post("/alert", description="Receive alert from Sublime and store it in the database")
14 -async def receive_sublime_alert(alert_request_body: AlertRequestBody) -> AlertResponseBody:
19 +async def receive_sublime_alert(alert_request_body: AlertRequestBody, session: AsyncSession = Depends(get_session)) -> AlertResponseBody:
20 """
21 Endpoint to store alert in the `sublimealerts` table.
22 Invoked by the Sublime alert webhook which is configured in the Sublime UI.
@@ -20,11 +25,16 @@ async def receive_sublime_alert(alert_request_body: AlertRequestBody) -> AlertRe
25 jsonify: A JSON response containing if the alert was stored successfully.
26 """
27 logger.info(f"Received alert from Sublime: {alert_request_body}")
23 - return store_sublime_alert(alert_request_body)
28 + return await store_sublime_alert(session, alert_request_body)
29
30
26 -@sublime_alerts_router.get("/alerts", response_model=SublimeAlertsResponse, description="Get all alerts")
27 -async def get_sublime_alerts() -> SublimeAlertsResponse:
31 +@sublime_alerts_router.get(
32 + "/alerts",
33 + response_model=SublimeAlertsResponse,
34 + description="Get all alerts",
35 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
36 +)
37 +async def get_sublime_alerts(session: AsyncSession = Depends(get_session)) -> SublimeAlertsResponse:
38 """
39 Endpoint to retrieve alerts from the `sublimealerts` table.
40
@@ -32,4 +42,4 @@ async def get_sublime_alerts() -> SublimeAlertsResponse:
42 jsonify: A JSON response containing all the alerts stored in the `sublimealerts` table.
43 """
44 logger.info("Fetching all alerts from Sublime")
35 - return collect_alerts()
45 + return await collect_alerts(session)
backend/app/connectors/sublime/services/alerts.py
+47 -30
@@ -3,6 +3,9 @@ from typing import List
3
4 from fastapi import HTTPException
5 from loguru import logger
6 +from sqlalchemy.ext.asyncio import AsyncSession
7 +from sqlalchemy.future import select
8 +from sqlalchemy.orm import selectinload
9
10 from app.connectors.sublime.models.alerts import FlaggedRule
11 from app.connectors.sublime.models.alerts import Mailbox
@@ -15,7 +18,6 @@ from app.connectors.sublime.schema.alerts import AlertResponseBody
18 from app.connectors.sublime.schema.alerts import SublimeAlertsResponse
19 from app.connectors.sublime.schema.alerts import SublimeAlertsSchema
20 from app.connectors.sublime.utils.universal import send_get_request
18 -from app.db.db_session import session
21
22
23 def create_sublime_alert(alert_request_body: AlertRequestBody) -> SublimeAlerts:
@@ -58,17 +60,17 @@ def create_triggered_actions(alert_request_body: AlertRequestBody, sublime_alert
60 return triggered_actions
61
62
61 -def store_sublime_alert(alert_request_body: AlertRequestBody) -> AlertResponseBody:
63 +async def store_sublime_alert(session: AsyncSession, alert_request_body: AlertRequestBody) -> AlertResponseBody:
64 try:
65 sublime_alert = create_sublime_alert(alert_request_body)
66 session.add(sublime_alert)
65 - session.flush()
67 + await session.flush() # Flush to obtain the ID of the new alert
68
69 flagged_rules = create_flagged_rules(alert_request_body, sublime_alert.id)
70 mailbox = create_mailbox(alert_request_body, sublime_alert.id)
71 triggered_actions = create_triggered_actions(alert_request_body, sublime_alert.id)
70 - sender = create_sender(alert_request_body, sublime_alert.id)
71 - recipient = create_recipient(alert_request_body, sublime_alert.id)
72 + sender = await create_sender(alert_request_body, sublime_alert.id)
73 + recipient = await create_recipient(alert_request_body, sublime_alert.id)
74
75 session.add_all(flagged_rules)
76 session.add(mailbox)
@@ -77,29 +79,31 @@ def store_sublime_alert(alert_request_body: AlertRequestBody) -> AlertResponseBo
79 session.add(recipient)
80
81 logger.info(f"Preparing to store: {sublime_alert}")
80 - session.commit()
82 + await session.commit() # Commit the changes asynchronously
83 logger.info(f"Alert {alert_request_body.id} stored in the database")
84
85 return AlertResponseBody(success=True, message=f"Alert {alert_request_body.id} stored in the database")
86 except Exception as e:
87 + # Rollback in case of error
88 + await session.rollback()
89 logger.error(f"Failed to store alert {alert_request_body.id} in the database: {e}")
90 raise HTTPException(status_code=500, detail=f"Failed to store alert {alert_request_body.id} in the database: {e}")
91
92
89 -def create_sender(alert_request_body: AlertRequestBody, sublime_alert_id: int) -> Sender:
90 - return Sender(email=collect_sender(alert_request_body.data.message.id), name="n/a", sublime_alert_id=sublime_alert_id)
93 +async def create_sender(alert_request_body: AlertRequestBody, sublime_alert_id: int) -> Sender:
94 + return Sender(email=await collect_sender(alert_request_body.data.message.id), name="n/a", sublime_alert_id=sublime_alert_id)
95
96
93 -def create_recipient(alert_request_body: AlertRequestBody, sublime_alert_id: int) -> Recipient:
94 - return Recipient(email=collect_recipient(alert_request_body.data.message.id), name="n/a", sublime_alert_id=sublime_alert_id)
97 +async def create_recipient(alert_request_body: AlertRequestBody, sublime_alert_id: int) -> Recipient:
98 + return Recipient(email=await collect_recipient(alert_request_body.data.message.id), name="n/a", sublime_alert_id=sublime_alert_id)
99
100
97 -def collect_sender(message_id: str) -> Sender:
101 +async def collect_sender(message_id: str) -> Sender:
102 """
103 Get a single Sublime Alert from the database
104 """
105 logger.info(f"Getting Sublime Alert with message_id {message_id}")
102 - message_details = send_get_request(f"/v0/messages/{message_id}")
106 + message_details = await send_get_request(f"/v0/messages/{message_id}")
107 if not message_details["success"]:
108 logger.error(f"Failed to get Sublime Alert with message_id {message_id}: {message_details['message']}")
109 raise HTTPException(
@@ -110,12 +114,12 @@ def collect_sender(message_id: str) -> Sender:
114 return message_details["data"]["sender"]["email"]
115
116
113 -def collect_recipient(message_id: str) -> Recipient:
117 +async def collect_recipient(message_id: str) -> Recipient:
118 """
119 Get a single Sublime Alert from the database
120 """
121 logger.info(f"Getting Sublime Alert with message_id {message_id}")
118 - message_details = send_get_request(f"/v0/messages/{message_id}")
122 + message_details = await send_get_request(f"/v0/messages/{message_id}")
123 if not message_details["success"]:
124 logger.error(f"Failed to get Sublime Alert with message_id {message_id}: {message_details['message']}")
125 raise HTTPException(
@@ -126,22 +130,35 @@ def collect_recipient(message_id: str) -> Recipient:
130 return message_details["data"]["recipients"][0]["email"]
131
132
129 -def collect_alerts() -> List[SublimeAlertsResponse]:
133 +async def collect_alerts(session: AsyncSession) -> List[SublimeAlertsResponse]:
134 """
131 - Get all Sublime Alerts from the database
135 + Get all Sublime Alerts from the database asynchronously.
136 +
137 + Args:
138 + session (AsyncSession): The database session.
139 +
140 + Returns:
141 + List[SublimeAlertsResponse]: A list of SublimeAlertsResponse objects.
142 """
143 logger.info("Getting all Sublime Alerts")
134 - alerts = session.query(SublimeAlerts).all()
135 - # Also add the relationships
136 - for alert in alerts:
137 - alert.flagged_rules = session.query(FlaggedRule).filter(FlaggedRule.sublime_alert_id == alert.id).all()
138 - alert.mailbox = [session.query(Mailbox).filter(Mailbox.sublime_alert_id == alert.id).first()]
139 - alert.triggered_actions = session.query(TriggeredAction).filter(TriggeredAction.sublime_alert_id == alert.id).all()
140 - alert.sender = [session.query(Sender).filter(Sender.sublime_alert_id == alert.id).first()]
141 - alert.recipients = session.query(Recipient).filter(Recipient.sublime_alert_id == alert.id).all()
142 - logger.info("Successfully retrieved all Sublime Alerts")
143 - return SublimeAlertsResponse(
144 - success=True,
145 - message="Successfully retrieved all Sublime Alerts",
146 - sublime_alerts=[SublimeAlertsSchema.from_orm(alert) for alert in alerts],
147 - )
144 + try:
145 + # Asynchronous query to load all alerts and their related objects
146 + stmt = select(SublimeAlerts).options(
147 + selectinload(SublimeAlerts.flagged_rules),
148 + selectinload(SublimeAlerts.mailbox),
149 + selectinload(SublimeAlerts.triggered_actions),
150 + selectinload(SublimeAlerts.sender),
151 + selectinload(SublimeAlerts.recipients),
152 + )
153 + result = await session.execute(stmt)
154 + alerts = result.scalars().all()
155 +
156 + logger.info("Successfully retrieved all Sublime Alerts")
157 + return SublimeAlertsResponse(
158 + success=True,
159 + message="Successfully retrieved all Sublime Alerts",
160 + sublime_alerts=[SublimeAlertsSchema.from_orm(alert) for alert in alerts],
161 + )
162 + except Exception as e:
163 + logger.error(f"Failed to get all Sublime Alerts with error: {e}")
164 + raise HTTPException(status_code=500, detail=f"Failed to get all Sublime Alerts with error: {e}")
backend/app/connectors/sublime/utils/universal.py
+9 -6
@@ -6,9 +6,10 @@ import requests
6 from loguru import logger
7
8 from app.connectors.utils import get_connector_info_from_db
9 +from app.db.db_session import get_db_session
10
11
11 -def verify_sublime_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
12 +async def verify_sublime_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
13 """
14 Verifies the connection to Sublime service.
15
@@ -52,19 +53,20 @@ def verify_sublime_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
53 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error: {e}"}
54
55
55 -def verify_sublime_connection(connector_name: str) -> str:
56 +async def verify_sublime_connection(connector_name: str) -> str:
57 """
58 Returns if connection to Sublime service is successful.
59 """
60 logger.info("Getting Sublime authentication token")
60 - attributes = get_connector_info_from_db(connector_name)
61 + async with get_db_session() as session: # This will correctly enter the context manager
62 + attributes = await get_connector_info_from_db(connector_name, session)
63 if attributes is None:
64 logger.error("No Sublime connector found in the database")
65 return None
64 - return verify_sublime_credentials(attributes)
66 + return await verify_sublime_credentials(attributes)
67
68
67 -def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Sublime") -> Dict[str, Any]:
69 +async def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Sublime") -> Dict[str, Any]:
70 """
71 Sends a GET request to the Sublime service.
72
@@ -77,7 +79,8 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
79 Dict[str, Any]: The response from the GET request.
80 """
81 logger.info(f"Sending GET request to {endpoint}")
80 - attributes = get_connector_info_from_db(connector_name)
82 + async with get_db_session() as session: # This will correctly enter the context manager
83 + attributes = await get_connector_info_from_db(connector_name, session)
84 if attributes is None:
85 logger.error("No Sublime connector found in the database")
86 return None
backend/app/connectors/utils.py
+28 -11
@@ -1,7 +1,10 @@
1 from typing import Any
2 from typing import Dict
3 +from typing import Optional
4
5 from loguru import logger
6 +from sqlalchemy.ext.asyncio import AsyncSession
7 +from sqlalchemy.future import select
8 from sqlmodel import Session
9 from sqlmodel import select
10
@@ -9,15 +12,29 @@ from app.connectors.models import Connectors
12 from app.connectors.schema import ConnectorResponse
13 from app.db.db_session import engine # Import the shared engine
14
15 +# ! Old without Async
16 +# def get_connector_info_from_db(connector_name: str) -> Dict[str, Any]:
17 +# with Session(engine) as session:
18 +# query = select(Connectors).where(Connectors.connector_name == connector_name)
19 +# connector = session.exec(query).first()
20 +# if connector:
21 +# connector_pydantic = ConnectorResponse.from_orm(connector)
22 +# connector_dict = connector_pydantic.dict()
23 +# return connector_dict
24 +# else:
25 +# logger.warning("No connector found.")
26 +# return None
27
13 -def get_connector_info_from_db(connector_name: str) -> Dict[str, Any]:
14 - with Session(engine) as session:
15 - query = select(Connectors).where(Connectors.connector_name == connector_name)
16 - connector = session.exec(query).first()
17 - if connector:
18 - connector_pydantic = ConnectorResponse.from_orm(connector)
19 - connector_dict = connector_pydantic.dict()
20 - return connector_dict
21 - else:
22 - logger.warning("No connector found.")
23 - return None
28 +
29 +# ! New with Async
30 +async def get_connector_info_from_db(connector_name: str, db: AsyncSession) -> Optional[Dict[str, Any]]:
31 + logger.info(f"Fetching connector {connector_name} from database")
32 + query = select(Connectors).where(Connectors.connector_name == connector_name)
33 + result = await db.execute(query)
34 + connector = result.scalars().first()
35 + if connector:
36 + connector_pydantic = ConnectorResponse.from_orm(connector)
37 + return connector_pydantic.dict()
38 + else:
39 + logger.warning("No connector found.")
40 + return None
backend/app/connectors/velociraptor/routes/artifacts.py
+80 -28
@@ -5,6 +5,8 @@ from fastapi import Depends
5 from fastapi import HTTPException
6 from fastapi import Security
7 from loguru import logger
8 +from sqlalchemy.ext.asyncio import AsyncSession
9 +from sqlalchemy.future import select
10
11 from app.auth.utils import AuthHandler
12 from app.connectors.velociraptor.schema.artifacts import ArtifactsResponse
@@ -20,7 +22,7 @@ from app.connectors.velociraptor.services.artifacts import get_artifacts
22 from app.connectors.velociraptor.services.artifacts import quarantine_host
23 from app.connectors.velociraptor.services.artifacts import run_artifact_collection
24 from app.connectors.velociraptor.services.artifacts import run_remote_command
23 -from app.db.db_session import session
25 +from app.db.db_session import get_session
26 from app.db.universal_models import Agents
27
28 # App specific imports
@@ -55,18 +57,33 @@ def get_os_prefix_from_os_name(os_name: str) -> str:
57 return result
58
59
58 -def get_velociraptor_id(hostname: str) -> str:
59 - # Get the velociraptor_id from the hostname
60 +# def get_velociraptor_id(hostname: str) -> str:
61 +# # Get the velociraptor_id from the hostname
62 +# logger.info(f"Getting velociraptor_id from hostname {hostname}")
63 +# agent = session.query(Agents).filter(Agents.hostname == hostname).first()
64 +# if not agent:
65 +# raise HTTPException(status_code=404, detail=f"Agent with hostname {hostname} not found")
66 +# velociraptor_id = agent.velociraptor_id
67 +# # If the velociraptor_id is `n/a`, raise an error
68 +# if velociraptor_id == "n/a":
69 +# raise HTTPException(status_code=404, detail=f"Velociraptor ID for hostname {hostname} is not available")
70 +# logger.info(f"velociraptor_id for hostname {hostname} is {velociraptor_id}")
71 +# return velociraptor_id
72 +
73 +
74 +async def get_velociraptor_id(session: AsyncSession, hostname: str) -> str:
75 logger.info(f"Getting velociraptor_id from hostname {hostname}")
61 - agent = session.query(Agents).filter(Agents.hostname == hostname).first()
76 + result = await session.execute(select(Agents).filter(Agents.hostname == hostname))
77 + agent = result.scalars().first()
78 +
79 if not agent:
80 raise HTTPException(status_code=404, detail=f"Agent with hostname {hostname} not found")
64 - velociraptor_id = agent.velociraptor_id
65 - # If the velociraptor_id is `n/a`, raise an error
66 - if velociraptor_id == "n/a":
81 +
82 + if agent.velociraptor_id == "n/a":
83 raise HTTPException(status_code=404, detail=f"Velociraptor ID for hostname {hostname} is not available")
68 - logger.info(f"velociraptor_id for hostname {hostname} is {velociraptor_id}")
69 - return velociraptor_id
84 +
85 + logger.info(f"velociraptor_id for hostname {hostname} is {agent.velociraptor_id}")
86 + return agent.velociraptor_id
87
88
89 @velociraptor_artifacts_router.get(
@@ -77,7 +94,7 @@ def get_velociraptor_id(hostname: str) -> str:
94 )
95 async def get_all_artifacts() -> ArtifactsResponse:
96 logger.info("Fetching all artifacts")
80 - return get_artifacts()
97 + return await get_artifacts()
98
99
100 @velociraptor_artifacts_router.get(
@@ -89,7 +106,8 @@ async def get_all_artifacts() -> ArtifactsResponse:
106 async def get_all_artifacts_for_os_prefix(os_prefix: str = Depends(verify_os_prefix_exists)) -> ArtifactsResponse:
107 logger.info(f"Fetching all artifacts for OS prefix {os_prefix}")
108 # Get all the artifacts names that begin with the OS prefix
92 - artifacts = get_artifacts().artifacts
109 + artifacts = await get_artifacts()
110 + artifacts = artifacts.artifacts
111 artifacts_for_os_prefix = [artifact for artifact in artifacts if artifact.name.startswith(os_prefix)]
112 return ArtifactsResponse(success=True, message=f"All artifacts for OS prefix {os_prefix} retrieved", artifacts=artifacts_for_os_prefix)
113
@@ -100,15 +118,23 @@ async def get_all_artifacts_for_os_prefix(os_prefix: str = Depends(verify_os_pre
118 description="Get all artifacts for a specific host's OS prefix",
119 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
120 )
103 -async def get_all_artifacts_for_hostname(hostname: str) -> ArtifactsResponse:
121 +async def get_all_artifacts_for_hostname(hostname: str, session: AsyncSession = Depends(get_session)) -> ArtifactsResponse:
122 logger.info(f"Fetching all artifacts for hostname {hostname}")
105 - agent = session.query(Agents).filter(Agents.hostname == hostname).first()
123 +
124 + # Asynchronous query to find the agent
125 + agent_result = await session.execute(select(Agents).filter(Agents.hostname == hostname))
126 + agent = agent_result.scalars().first()
127 +
128 if not agent:
129 raise HTTPException(status_code=404, detail=f"Agent with hostname {hostname} not found")
130 +
131 os_prefix = get_os_prefix_from_os_name(os_name=agent.os.lower())
132 if not os_prefix:
133 raise HTTPException(status_code=404, detail=f"OS prefix of {agent.os.lower()} for hostname {hostname} not found")
134 +
135 + # Assuming get_all_artifacts_for_os_prefix is an async function
136 result = await get_all_artifacts_for_os_prefix(os_prefix)
137 +
138 return ArtifactsResponse(
139 success=True,
140 message=f"All available artifacts that can be ran for hostname {hostname} retrieved",
@@ -116,26 +142,52 @@ async def get_all_artifacts_for_hostname(hostname: str) -> ArtifactsResponse:
142 )
143
144
145 +# @velociraptor_artifacts_router.post(
146 +# "/collect",
147 +# response_model=CollectArtifactResponse,
148 +# description="Run an analyzer",
149 +# dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
150 +# )
151 +# async def collect_artifact(collect_artifact_body: CollectArtifactBody) -> CollectArtifactResponse:
152 +# logger.info(f"Received request to collect artifact {collect_artifact_body}")
153 +# # Check that provided artifact name applies for the provided hostname and use the `get_all_artifacts_for_hostname` function to get the list of artifacts
154 +# result = await get_all_artifacts_for_hostname(collect_artifact_body.hostname)
155 +# artifact_names = [artifact.name for artifact in result.artifacts]
156 +# if collect_artifact_body.artifact_name not in artifact_names:
157 +# raise HTTPException(
158 +# status_code=400,
159 +# detail=f"Artifact name {collect_artifact_body.artifact_name} does not apply for hostname {collect_artifact_body.hostname} or does not exist",
160 +# )
161 +# # Add the velociraptor_id to the run_analyzer_body object
162 +# collect_artifact_body.velociraptor_id = get_velociraptor_id(collect_artifact_body.hostname)
163 +# # Run the analyzer
164 +# return run_artifact_collection(collect_artifact_body)
165 +
166 +
167 @velociraptor_artifacts_router.post(
168 "/collect",
169 response_model=CollectArtifactResponse,
170 description="Run an analyzer",
171 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
172 )
125 -async def collect_artifact(collect_artifact_body: CollectArtifactBody) -> CollectArtifactResponse:
173 +async def collect_artifact(
174 + collect_artifact_body: CollectArtifactBody,
175 + session: AsyncSession = Depends(get_session),
176 +) -> CollectArtifactResponse:
177 logger.info(f"Received request to collect artifact {collect_artifact_body}")
127 - # Check that provided artifact name applies for the provided hostname and use the `get_all_artifacts_for_hostname` function to get the list of artifacts
128 - result = await get_all_artifacts_for_hostname(collect_artifact_body.hostname)
178 + result = await get_all_artifacts_for_hostname(collect_artifact_body.hostname, session)
179 artifact_names = [artifact.name for artifact in result.artifacts]
180 +
181 if collect_artifact_body.artifact_name not in artifact_names:
182 raise HTTPException(
183 status_code=400,
184 detail=f"Artifact name {collect_artifact_body.artifact_name} does not apply for hostname {collect_artifact_body.hostname} or does not exist",
185 )
135 - # Add the velociraptor_id to the run_analyzer_body object
136 - collect_artifact_body.velociraptor_id = get_velociraptor_id(collect_artifact_body.hostname)
137 - # Run the analyzer
138 - return run_artifact_collection(collect_artifact_body)
186 +
187 + collect_artifact_body.velociraptor_id = await get_velociraptor_id(session, collect_artifact_body.hostname)
188 +
189 + # Assuming run_artifact_collection is an async function and takes a session as a parameter
190 + return await run_artifact_collection(collect_artifact_body)
191
192
193 @velociraptor_artifacts_router.post(
@@ -144,9 +196,9 @@ async def collect_artifact(collect_artifact_body: CollectArtifactBody) -> Collec
196 description="Run a remote command",
197 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
198 )
147 -async def run_command(run_command_body: RunCommandBody) -> RunCommandResponse:
199 +async def run_command(run_command_body: RunCommandBody, session: AsyncSession = Depends(get_session)) -> RunCommandResponse:
200 logger.info(f"Received request to run command {run_command_body}")
149 - result = await get_all_artifacts_for_hostname(run_command_body.hostname)
201 + result = await get_all_artifacts_for_hostname(run_command_body.hostname, session)
202 artifact_names = [artifact.name for artifact in result.artifacts]
203 if run_command_body.artifact_name not in artifact_names:
204 raise HTTPException(
@@ -154,9 +206,9 @@ async def run_command(run_command_body: RunCommandBody) -> RunCommandResponse:
206 detail=f"Artifact name {run_command_body.artifact_name.value} does not apply for hostname {run_command_body.hostname} or does not exist",
207 )
208 # Add the velociraptor_id to the run_command_body object
157 - run_command_body.velociraptor_id = get_velociraptor_id(run_command_body.hostname)
209 + run_command_body.velociraptor_id = await get_velociraptor_id(session, run_command_body.hostname)
210 # Run the command
159 - return run_remote_command(run_command_body)
211 + return await run_remote_command(run_command_body)
212
213
214 @velociraptor_artifacts_router.post(
@@ -165,9 +217,9 @@ async def run_command(run_command_body: RunCommandBody) -> RunCommandResponse:
217 description="Quarantine a host",
218 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
219 )
168 -async def quarantine(quarantine_body: QuarantineBody) -> QuarantineResponse:
220 +async def quarantine(quarantine_body: QuarantineBody, session: AsyncSession = Depends(get_session)) -> QuarantineResponse:
221 logger.info(f"Received request to quarantine host {quarantine_body}")
170 - result = await get_all_artifacts_for_hostname(quarantine_body.hostname)
222 + result = await get_all_artifacts_for_hostname(quarantine_body.hostname, session)
223 artifact_names = [artifact.name for artifact in result.artifacts]
224 if quarantine_body.artifact_name not in artifact_names:
225 raise HTTPException(
@@ -176,6 +228,6 @@ async def quarantine(quarantine_body: QuarantineBody) -> QuarantineResponse:
228 )
229 # Add the velociraptor_id to the run_command_body object
230 # Add the velociraptor_id to the quarantine_body object
179 - quarantine_body.velociraptor_id = get_velociraptor_id(quarantine_body.hostname)
231 + quarantine_body.velociraptor_id = await get_velociraptor_id(session, quarantine_body.hostname)
232 # Quarantine the host
181 - return quarantine_host(quarantine_body)
233 + return await quarantine_host(quarantine_body)
backend/app/connectors/velociraptor/services/artifacts.py
+19 -15
@@ -11,7 +11,7 @@ from app.connectors.velociraptor.schema.artifacts import RunCommandBody
11 from app.connectors.velociraptor.schema.artifacts import RunCommandResponse
12 from app.connectors.velociraptor.utils.universal import UniversalService
13
14 -universal_service = UniversalService()
14 +# universal_service = UniversalService()
15
16
17 def create_query(query: str) -> str:
@@ -53,7 +53,7 @@ def get_artifact_key(analyzer_body: CollectArtifactBody) -> str:
53 return f"collect_client(client_id='{analyzer_body.velociraptor_id}', artifacts=['{analyzer_body.artifact_name}'])"
54
55
56 -def get_artifacts() -> ArtifactsResponse:
56 +async def get_artifacts() -> ArtifactsResponse:
57 """
58 Get all artifacts from Velociraptor.
59
@@ -61,8 +61,9 @@ def get_artifacts() -> ArtifactsResponse:
61 ArtifactsResponse: A dictionary containing the artifacts.
62 """
63 logger.info("Fetching artifacts from Velociraptor")
64 + velociraptor_service = await UniversalService.create("Velociraptor")
65 query = create_query("SELECT name,description FROM artifact_definitions()")
65 - all_artifacts = universal_service.execute_query(query)
66 + all_artifacts = velociraptor_service.execute_query(query)
67 try:
68 if all_artifacts["success"]:
69 artifacts = [Artifacts(**artifact) for artifact in all_artifacts["results"]]
@@ -74,7 +75,7 @@ def get_artifacts() -> ArtifactsResponse:
75 raise HTTPException(status_code=500, detail=f"Failed to get all artifacts: {err}")
76
77
77 -def run_artifact_collection(collect_artifact_body: CollectArtifactBody) -> CollectArtifactResponse:
78 +async def run_artifact_collection(collect_artifact_body: CollectArtifactBody) -> CollectArtifactResponse:
79 """
80 Run an artifact collection on a client.
81
@@ -84,11 +85,12 @@ def run_artifact_collection(collect_artifact_body: CollectArtifactBody) -> Colle
85 Returns:
86 RunAnalyzerResponse: A dictionary containing the success status and a message.
87 """
88 + velociraptor_service = await UniversalService.create("Velociraptor")
89 try:
90 query = create_query(
91 f"SELECT collect_client(client_id='{collect_artifact_body.velociraptor_id}', artifacts=['{collect_artifact_body.artifact_name}']) FROM scope()",
92 )
91 - flow = universal_service.execute_query(query)
93 + flow = velociraptor_service.execute_query(query)
94 logger.info(f"Successfully ran artifact collection on {flow}")
95
96 artifact_key = get_artifact_key(analyzer_body=collect_artifact_body)
@@ -96,10 +98,10 @@ def run_artifact_collection(collect_artifact_body: CollectArtifactBody) -> Colle
98 flow_id = flow["results"][0][artifact_key]["flow_id"]
99 logger.info(f"Extracted flow_id: {flow_id}")
100
99 - completed = universal_service.watch_flow_completion(flow_id)
101 + completed = velociraptor_service.watch_flow_completion(flow_id)
102 logger.info(f"Successfully watched flow completion on {completed}")
103
102 - results = universal_service.read_collection_results(
104 + results = velociraptor_service.read_collection_results(
105 client_id=collect_artifact_body.velociraptor_id,
106 flow_id=flow_id,
107 artifact=collect_artifact_body.artifact_name,
@@ -116,7 +118,7 @@ def run_artifact_collection(collect_artifact_body: CollectArtifactBody) -> Colle
118 raise HTTPException(status_code=500, detail=f"Failed to run artifact collection on {collect_artifact_body}: {err}")
119
120
119 -def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResponse:
121 +async def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResponse:
122 """
123 Run a remote command on a client.
124
@@ -126,6 +128,7 @@ def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResponse:
128 Returns:
129 RunAnalyzerResponse: A dictionary containing the success status and a message.
130 """
131 + velociraptor_service = await UniversalService.create("Velociraptor")
132 try:
133 run_command_body.artifact_name = run_command_body.artifact_name.value
134 logger.info(f"Running remote command on {run_command_body}")
@@ -133,7 +136,7 @@ def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResponse:
136 f"SELECT collect_client(client_id='{run_command_body.velociraptor_id}', urgent=true, artifacts=['{run_command_body.artifact_name}'], env=dict(Command='{run_command_body.command}')) "
137 "FROM scope()",
138 )
136 - flow = universal_service.execute_query(query)
139 + flow = velociraptor_service.execute_query(query)
140 logger.info(f"Successfully ran artifact collection on {flow}")
141
142 artifact_key = get_artifact_key(analyzer_body=run_command_body)
@@ -141,10 +144,10 @@ def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResponse:
144 flow_id = flow["results"][0][artifact_key]["flow_id"]
145 logger.info(f"Extracted flow_id: {flow_id}")
146
144 - completed = universal_service.watch_flow_completion(flow_id)
147 + completed = velociraptor_service.watch_flow_completion(flow_id)
148 logger.info(f"Successfully watched flow completion on {completed}")
149
147 - results = universal_service.read_collection_results(
150 + results = velociraptor_service.read_collection_results(
151 client_id=run_command_body.velociraptor_id,
152 flow_id=flow_id,
153 artifact=run_command_body.artifact_name,
@@ -158,7 +161,7 @@ def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResponse:
161 raise HTTPException(status_code=500, detail=f"Failed to run artifact collection on {run_command_body}: {err}")
162
163
161 -def quarantine_host(quarantine_body: QuarantineBody) -> QuarantineResponse:
164 +async def quarantine_host(quarantine_body: QuarantineBody) -> QuarantineResponse:
165 """
166 Quarantine a host.
167
@@ -168,6 +171,7 @@ def quarantine_host(quarantine_body: QuarantineBody) -> QuarantineResponse:
171 Returns:
172 QuarantineResponse: A dictionary containing the success status and a message.
173 """
174 + velociraptor_service = await UniversalService.create("Velociraptor")
175 try:
176 quarantine_body.artifact_name = quarantine_body.artifact_name.value
177 quarantine_body.action = quarantine_body.action.value
@@ -179,7 +183,7 @@ def quarantine_host(quarantine_body: QuarantineBody) -> QuarantineResponse:
183 query = create_query(
184 f'SELECT collect_client(client_id="{quarantine_body.velociraptor_id}", artifacts=["{quarantine_body.artifact_name}"], spec=dict(`{quarantine_body.artifact_name}`=dict(`RemovePolicy`="Y"))) FROM scope()',
185 )
182 - flow = universal_service.execute_query(query)
186 + flow = velociraptor_service.execute_query(query)
187 logger.info(f"Successfully ran artifact collection on {flow}")
188
189 artifact_key = get_artifact_key(analyzer_body=quarantine_body)
@@ -187,10 +191,10 @@ def quarantine_host(quarantine_body: QuarantineBody) -> QuarantineResponse:
191 flow_id = flow["results"][0][artifact_key]["flow_id"]
192 logger.info(f"Extracted flow_id: {flow_id}")
193
190 - completed = universal_service.watch_flow_completion(flow_id)
194 + completed = velociraptor_service.watch_flow_completion(flow_id)
195 logger.info(f"Successfully watched flow completion on {completed}")
196
193 - results = universal_service.read_collection_results(
197 + results = velociraptor_service.read_collection_results(
198 client_id=quarantine_body.velociraptor_id,
199 flow_id=flow_id,
200 artifact=quarantine_body.artifact_name,
backend/app/connectors/velociraptor/utils/universal.py
+69 -28
@@ -14,9 +14,11 @@ from pyvelociraptor import api_pb2
14 from pyvelociraptor import api_pb2_grpc
15
16 from app.connectors.utils import get_connector_info_from_db
17 +from app.db.db_session import AsyncSessionLocal
18 +from app.db.db_session import get_db_session
19
20
19 -def verify_velociraptor_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
21 +async def verify_velociraptor_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
22 """
23 Verifies the connection to Velociraptor service.
24
@@ -70,16 +72,45 @@ def verify_velociraptor_credentials(attributes: Dict[str, Any]) -> Dict[str, Any
72 return {"connectionSuccessful": False, "message": f"Failed to get connector_api_key from the database: {e}"}
73
74
73 -def verify_velociraptor_connection(connector_name: str) -> str:
75 +async def verify_velociraptor_connection(connector_name: str) -> str:
76 """
77 Verifies the connection to Velociraptor service.
78 """
79 logger.info(f"Verifying the Velociraptor connection for connector: {connector_name}")
78 - attributes = get_connector_info_from_db(connector_name)
80 + async with get_db_session() as session: # This will correctly enter the context manager
81 + attributes = await get_connector_info_from_db(connector_name, session)
82 if attributes is None:
83 logger.error("No Velociraptor connector found in the database")
84 return None
82 - return verify_velociraptor_credentials(attributes)
85 + return await verify_velociraptor_credentials(attributes)
86 +
87 +
88 +# class UniversalService:
89 +# """
90 +# A service class that encapsulates the logic for polling messages from Velociraptor.
91 +# """
92 +
93 +# def __init__(self) -> None:
94 +# self.setup_velociraptor_connector("Velociraptor")
95 +# self.setup_grpc_channel_and_stub()
96 +
97 +# # def setup_velociraptor_connector(self, connector_name: str):
98 +# # """
99 +# # Collects the details of the Velociraptor connector and sets them up.
100 +
101 +# # Args:
102 +# # connector_name (str): The name of the Velociraptor connector.
103 +# # """
104 +# # attributes = get_connector_info_from_db(connector_name)
105 +# async def setup_velociraptor_connector(self, connector_name: str):
106 +# # Start the session asynchronously
107 +# async with AsyncSessionLocal() as session:
108 +# attributes = await get_connector_info_from_db(connector_name, session)
109 +# if attributes is None:
110 +# logger.error("No Velociraptor connector found in the database")
111 +# return None
112 +# self.connector_api_key = attributes["connector_api_key"]
113 +# self.config = pyvelociraptor.LoadConfigFile(self.connector_api_key)
114
115
116 class UniversalService:
@@ -87,25 +118,23 @@ class UniversalService:
118 A service class that encapsulates the logic for polling messages from Velociraptor.
119 """
120
121 + #! Modify this to use AsyncSessionLocal Begin - ALSO SEE BELOW CLASS METHOD
122 def __init__(self) -> None:
91 - self.setup_velociraptor_connector("Velociraptor")
92 - self.setup_grpc_channel_and_stub()
93 -
94 - def setup_velociraptor_connector(self, connector_name: str):
95 - """
96 - Collects the details of the Velociraptor connector and sets them up.
123 + self.connector_api_key = None
124 + self.config = None
125
98 - Args:
99 - connector_name (str): The name of the Velociraptor connector.
100 - """
101 - attributes = get_connector_info_from_db(connector_name)
126 + async def setup_velociraptor_connector(self, connector_name: str):
127 + async with AsyncSessionLocal() as session:
128 + attributes = await get_connector_info_from_db(connector_name, session)
129 if attributes is None:
130 logger.error("No Velociraptor connector found in the database")
131 return None
132 self.connector_api_key = attributes["connector_api_key"]
133 self.config = pyvelociraptor.LoadConfigFile(self.connector_api_key)
134
108 - def setup_grpc_channel_and_stub(self):
135 + #! Modify this to use AsyncSessionLocal End
136 +
137 + async def setup_grpc_channel_and_stub(self):
138 """
139 Sets up the gRPC channel and stub for Velociraptor.
140 """
@@ -122,6 +151,16 @@ class UniversalService:
151 )
152 self.stub = api_pb2_grpc.APIStub(self.channel)
153
154 + #! Modify this to use AsyncSessionLocal Begin
155 + @classmethod
156 + async def create(cls, connector_name: str):
157 + instance = cls()
158 + await instance.setup_velociraptor_connector(connector_name)
159 + await instance.setup_grpc_channel_and_stub()
160 + return instance
161 +
162 + #! Modify this to use AsyncSessionLocal End
163 +
164 def create_vql_request(self, vql: str):
165 """
166 Creates a VQLCollectorArgs object with given VQL query.
@@ -152,7 +191,10 @@ class UniversalService:
191 Returns:
192 dict: A dictionary with the success status, a message, and potentially the results.
193 """
194 + logger.info(f"Executing query: {vql}")
195 +
196 client_request = self.create_vql_request(vql)
197 +
198 try:
199 results = []
200 for response in self.stub.Query(client_request, timeout=30):
@@ -211,7 +253,7 @@ class UniversalService:
253 vql = f"SELECT * FROM source(client_id='{client_id}', flow_id='{flow_id}', artifact='{artifact}')"
254 return self.execute_query(vql)
255
214 - def get_client_id(self, client_name: str):
256 + async def get_client_id(self, client_name: str):
257 """
258 Get the client_id associated with a given client_name.
259
@@ -227,16 +269,15 @@ class UniversalService:
269 vql_last_seen_at = f"select last_seen_at from clients(search='host:{client_name}')"
270
271 # Get the last seen timestamp
230 - last_seen_at = self._get_last_seen_timestamp(vql_last_seen_at)
272 + logger.info(f"Getting last seen at timestamp for {client_name}")
273 +
274 + last_seen_at = await self._get_last_seen_timestamp(vql_last_seen_at)
275 +
276 + logger.info(f"Last seen at timestamp for {client_name}: {last_seen_at}")
277
278 # if last_seen_at is longer than 30 seconds from now, return False
233 - if self._is_offline(last_seen_at):
234 - return {
235 - "success": False,
236 - "message": f"{client_name} has not been seen in the last 30 seconds and "
237 - "may not be online with the Velociraptor server.",
238 - "results": [{"client_id": None}],
239 - }
279 + if await self._is_offline(last_seen_at):
280 + return self.execute_query(vql_client_id)
281
282 return self.execute_query(vql_client_id)
283 except Exception as e:
@@ -246,7 +287,7 @@ class UniversalService:
287 "results": [{"client_id": None}],
288 }
289
249 - def _get_last_seen_timestamp(self, vql: str):
290 + async def _get_last_seen_timestamp(self, vql: str):
291 """
292 Executes the VQL query and returns the last_seen_at timestamp.
293
@@ -258,7 +299,7 @@ class UniversalService:
299 """
300 return self.execute_query(vql)["results"][0]["last_seen_at"]
301
261 - def _get_client_version(self, vql: str):
302 + async def _get_client_version(self, vql: str):
303 """
304 Executes the VQL query and returns the `agent_information``version` field
305
@@ -270,7 +311,7 @@ class UniversalService:
311 """
312 return self.execute_query(vql)["results"][0]["agent_information"]["version"]
313
273 - def _get_server_version(self, vql: str):
314 + async def _get_server_version(self, vql: str):
315 """
316 Executes the VQL query and returns the velociraptor server version.
317
@@ -285,7 +326,7 @@ class UniversalService:
326 except IndexError as e:
327 raise HTTPException(status_code=500, detail=f"Failed to get server version: {e}")
328
288 - def _is_offline(self, last_seen_at: float):
329 + async def _is_offline(self, last_seen_at: float):
330 """
331 Determines if the client is offline based on the last_seen_at timestamp.
332
backend/app/connectors/wazuh_indexer/routes/alerts.py
+10 -10
@@ -31,16 +31,16 @@ from app.connectors.wazuh_indexer.utils.universal import collect_indices
31 wazuh_indexer_alerts_router = APIRouter()
32
33
34 -def get_index_names() -> List[str]:
35 - indices = collect_indices()
34 +async def get_index_names() -> List[str]:
35 + indices = await collect_indices()
36 return indices.indices_list
37
38
39 -def verify_index_name(index_alerts_search_body: IndexAlertsSearchBody) -> IndexAlertsSearchBody:
39 +async def verify_index_name(index_alerts_search_body: IndexAlertsSearchBody) -> IndexAlertsSearchBody:
40 # Remove any extra spaces from index_name
41 index_alerts_search_body.index_name = index_alerts_search_body.index_name.strip()
42
43 - managed_index_names = get_index_names()
43 + managed_index_names = await get_index_names()
44 if index_alerts_search_body.index_name not in managed_index_names:
45 raise HTTPException(
46 status_code=400,
@@ -57,7 +57,7 @@ def verify_index_name(index_alerts_search_body: IndexAlertsSearchBody) -> IndexA
57 )
58 async def get_all_alerts(alerts_search_body: AlertsSearchBody) -> AlertsSearchResponse:
59 logger.info("Fetching all alerts")
60 - return get_alerts(alerts_search_body)
60 + return await get_alerts(alerts_search_body)
61
62
63 @wazuh_indexer_alerts_router.post(
@@ -68,7 +68,7 @@ async def get_all_alerts(alerts_search_body: AlertsSearchBody) -> AlertsSearchRe
68 )
69 async def get_all_alerts_for_host(host_alerts_search_body: HostAlertsSearchBody) -> HostAlertsSearchResponse:
70 logger.info(f"Fetching all alerts for host {host_alerts_search_body.agent_name}")
71 - return get_host_alerts(host_alerts_search_body)
71 + return await get_host_alerts(host_alerts_search_body)
72
73
74 @wazuh_indexer_alerts_router.post(
@@ -81,7 +81,7 @@ async def get_all_alerts_for_index(
81 index_alerts_search_body: IndexAlertsSearchBody = Depends(verify_index_name),
82 ) -> IndexAlertsSearchResponse:
83 logger.info(f"Fetching all alerts for index {index_alerts_search_body.index_name}")
84 - return get_index_alerts(index_alerts_search_body)
84 + return await get_index_alerts(index_alerts_search_body)
85
86
87 @wazuh_indexer_alerts_router.post(
@@ -92,7 +92,7 @@ async def get_all_alerts_for_index(
92 )
93 async def get_all_alerts_by_host(alerts_search_body: AlertsSearchBody) -> AlertsByHostResponse:
94 logger.info("Fetching number of all alerts for all hosts")
95 - return get_alerts_by_host(alerts_search_body)
95 + return await get_alerts_by_host(alerts_search_body)
96
97
98 @wazuh_indexer_alerts_router.post(
@@ -103,7 +103,7 @@ async def get_all_alerts_by_host(alerts_search_body: AlertsSearchBody) -> Alerts
103 )
104 async def get_all_alerts_by_rule(alerts_search_body: AlertsSearchBody) -> AlertsByRuleResponse:
105 logger.info("Fetching number of all alerts for all rules")
106 - return get_alerts_by_rule(alerts_search_body)
106 + return await get_alerts_by_rule(alerts_search_body)
107
108
109 @wazuh_indexer_alerts_router.post(
@@ -123,4 +123,4 @@ async def get_all_alerts_by_rule_per_host(alerts_search_body: AlertsSearchBody)
123 AlertsByRulePerHostResponse: _description_
124 """
125 logger.info("Fetching number of all alerts for all rules per host")
126 - return get_alerts_by_rule_per_host(alerts_search_body)
126 + return await get_alerts_by_rule_per_host(alerts_search_body)
backend/app/connectors/wazuh_indexer/routes/monitoring.py
+4 -4
@@ -37,7 +37,7 @@ async def get_cluster_health() -> Union[ClusterHealthResponse, HTTPException]:
37 Raises:
38 HTTPException: An exception with a 500 status code is raised if the cluster health cannot be retrieved.
39 """
40 - cluster_health = cluster_healthcheck()
40 + cluster_health = await cluster_healthcheck()
41 if cluster_health is not None:
42 return cluster_health
43 else:
@@ -62,7 +62,7 @@ async def get_node_allocation() -> Union[NodeAllocationResponse, HTTPException]:
62 Raises:
63 HTTPException: An exception with a 500 status code is raised if the node allocation cannot be retrieved.
64 """
65 - node_allocation_response = node_allocation()
65 + node_allocation_response = await node_allocation()
66 if node_allocation_response is not None:
67 return node_allocation_response
68 else:
@@ -87,7 +87,7 @@ async def get_indices_stats() -> Union[IndicesStatsResponse, HTTPException]:
87 Raises:
88 HTTPException: An exception with a 500 status code is raised if the indices stats cannot be retrieved.
89 """
90 - indices_stats_response = indices_stats()
90 + indices_stats_response = await indices_stats()
91 if indices_stats_response is not None:
92 return indices_stats_response
93 else:
@@ -112,7 +112,7 @@ async def get_shards() -> Union[ShardsResponse, HTTPException]:
112 Raises:
113 HTTPException: An exception with a 500 status code is raised if the shards cannot be retrieved.
114 """
115 - shards_response = shards()
115 + shards_response = await shards()
116 if shards_response is not None:
117 return shards_response
118 else:
backend/app/connectors/wazuh_indexer/services/alerts.py
+20 -20
@@ -25,13 +25,13 @@ from app.connectors.wazuh_indexer.utils.universal import collect_indices
25 from app.connectors.wazuh_indexer.utils.universal import create_wazuh_indexer_client
26
27
28 -def collect_and_aggregate_alerts(field_names: List[str], search_body: AlertsSearchBody) -> Dict[str, int]:
29 - indices = collect_indices()
28 +async def collect_and_aggregate_alerts(field_names: List[str], search_body: AlertsSearchBody) -> Dict[str, int]:
29 + indices = await collect_indices()
30 aggregated_alerts_dict = {}
31
32 for index_name in indices.indices_list:
33 try:
34 - alerts_response = collect_alerts_generic(index_name, body=search_body)
34 + alerts_response = await collect_alerts_generic(index_name, body=search_body)
35 if alerts_response.success:
36 for alert in alerts_response.alerts:
37 composite_key = tuple(alert["_source"][field] for field in field_names)
@@ -48,8 +48,8 @@ def collect_and_aggregate_alerts(field_names: List[str], search_body: AlertsSear
48 return aggregated_alerts_dict
49
50
51 -def collect_alerts_generic(index_name: str, body: AlertsSearchBody, is_host_specific: bool = False) -> CollectAlertsResponse:
52 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
51 +async def collect_alerts_generic(index_name: str, body: AlertsSearchBody, is_host_specific: bool = False) -> CollectAlertsResponse:
52 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
53 query_builder = AlertsQueryBuilder()
54 query_builder.add_time_range(timerange=body.timerange, timestamp_field=body.timestamp_field)
55 query_builder.add_matches(matches=[(body.alert_field, body.alert_value)])
@@ -71,15 +71,15 @@ def collect_alerts_generic(index_name: str, body: AlertsSearchBody, is_host_spec
71 raise HTTPException(status_code=500, detail=f"An error occurred while collecting alerts: {e}")
72
73
74 -def get_alerts_generic(search_body: Type[AlertsSearchBody], is_host_specific: bool = False, index_name: Optional[str] = None):
74 +async def get_alerts_generic(search_body: Type[AlertsSearchBody], is_host_specific: bool = False, index_name: Optional[str] = None):
75 logger.info(f"Collecting Wazuh Indexer alerts for host {search_body.agent_name if is_host_specific else ''}")
76 alerts_summary = []
77 - indices = collect_indices()
77 + indices = await collect_indices()
78 index_list = [index_name] if index_name else indices.indices_list # Use the provided index_name or get all indices
79
80 for index_name in index_list:
81 try:
82 - alerts = collect_alerts_generic(index_name, body=search_body, is_host_specific=is_host_specific)
82 + alerts = await collect_alerts_generic(index_name, body=search_body, is_host_specific=is_host_specific)
83 if alerts.success and len(alerts.alerts) > 0:
84 alerts_summary.append(
85 {
@@ -105,23 +105,23 @@ def get_alerts_generic(search_body: Type[AlertsSearchBody], is_host_specific: bo
105 return {"alerts_summary": alerts_summary, "success": len(alerts_summary) > 0, "message": message}
106
107
108 -def get_alerts(search_body: AlertsSearchBody) -> AlertsSearchResponse:
109 - result = get_alerts_generic(search_body)
108 +async def get_alerts(search_body: AlertsSearchBody) -> AlertsSearchResponse:
109 + result = await get_alerts_generic(search_body)
110 return AlertsSearchResponse(**result)
111
112
113 -def get_host_alerts(search_body: HostAlertsSearchBody) -> HostAlertsSearchResponse:
114 - result = get_alerts_generic(search_body, is_host_specific=True)
113 +async def get_host_alerts(search_body: HostAlertsSearchBody) -> HostAlertsSearchResponse:
114 + result = await get_alerts_generic(search_body, is_host_specific=True)
115 return HostAlertsSearchResponse(**result)
116
117
118 -def get_index_alerts(search_body: IndexAlertsSearchBody) -> IndexAlertsSearchResponse:
119 - result = get_alerts_generic(search_body, index_name=search_body.index_name)
118 +async def get_index_alerts(search_body: IndexAlertsSearchBody) -> IndexAlertsSearchResponse:
119 + result = await get_alerts_generic(search_body, index_name=search_body.index_name)
120 return IndexAlertsSearchResponse(**result)
121
122
123 -def get_alerts_by_host(search_body: AlertsSearchBody) -> AlertsByHostResponse:
124 - aggregated_by_host = collect_and_aggregate_alerts(["agent_name"], search_body)
123 +async def get_alerts_by_host(search_body: AlertsSearchBody) -> AlertsByHostResponse:
124 + aggregated_by_host = await collect_and_aggregate_alerts(["agent_name"], search_body)
125 alerts_by_host_list: List[AlertsByHost] = [
126 AlertsByHost(agent_name=host[0], number_of_alerts=count) # host[0] because host is now a tuple
127 for host, count in aggregated_by_host.items()
@@ -133,8 +133,8 @@ def get_alerts_by_host(search_body: AlertsSearchBody) -> AlertsByHostResponse:
133 )
134
135
136 -def get_alerts_by_rule(search_body: AlertsSearchBody) -> AlertsByRuleResponse:
137 - aggregated_by_rule = collect_and_aggregate_alerts(["rule_description"], search_body)
136 +async def get_alerts_by_rule(search_body: AlertsSearchBody) -> AlertsByRuleResponse:
137 + aggregated_by_rule = await collect_and_aggregate_alerts(["rule_description"], search_body)
138 alerts_by_rule_list: List[AlertsByRule] = [
139 AlertsByRule(rule=rule[0], number_of_alerts=count) # rule[0] because rule is now a tuple
140 for rule, count in aggregated_by_rule.items()
@@ -146,8 +146,8 @@ def get_alerts_by_rule(search_body: AlertsSearchBody) -> AlertsByRuleResponse:
146 )
147
148
149 -def get_alerts_by_rule_per_host(search_body: AlertsSearchBody) -> AlertsByRulePerHostResponse:
150 - aggregated_by_rule_per_host = collect_and_aggregate_alerts(["agent_name", "rule_description"], search_body)
149 +async def get_alerts_by_rule_per_host(search_body: AlertsSearchBody) -> AlertsByRulePerHostResponse:
150 + aggregated_by_rule_per_host = await collect_and_aggregate_alerts(["agent_name", "rule_description"], search_body)
151 alerts_by_rule_per_host_list: List[AlertsByRulePerHost] = [
152 AlertsByRulePerHost(agent_name=agent_name, rule=rule, number_of_alerts=count)
153 for (agent_name, rule), count in aggregated_by_rule_per_host.items()
backend/app/connectors/wazuh_indexer/services/monitoring.py
+11 -11
@@ -18,7 +18,7 @@ from app.connectors.wazuh_indexer.utils.universal import format_node_allocation
18 from app.connectors.wazuh_indexer.utils.universal import format_shards
19
20
21 -def cluster_healthcheck() -> Union[ClusterHealthResponse, Dict[str, str]]:
21 +async def cluster_healthcheck() -> Union[ClusterHealthResponse, Dict[str, str]]:
22 """
23 Returns the cluster health of the Wazuh Indexer service.
24
@@ -29,7 +29,7 @@ def cluster_healthcheck() -> Union[ClusterHealthResponse, Dict[str, str]]:
29 Exception: An exception is raised if the cluster health cannot be retrieved.
30 """
31 logger.info("Collecting Wazuh Indexer healthcheck")
32 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
32 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
33 try:
34 cluster_health_data = es_client.cluster.health()
35 cluster_health_model = ClusterHealth(**cluster_health_data)
@@ -43,7 +43,7 @@ def cluster_healthcheck() -> Union[ClusterHealthResponse, Dict[str, str]]:
43 raise Exception(str(e))
44
45
46 -def node_allocation() -> Union[NodeAllocationResponse, Dict[str, bool]]:
46 +async def node_allocation() -> Union[NodeAllocationResponse, Dict[str, bool]]:
47 """
48 Returns the node allocation of the Wazuh Indexer service.
49
@@ -54,12 +54,12 @@ def node_allocation() -> Union[NodeAllocationResponse, Dict[str, bool]]:
54 Exception: An exception is raised if the node allocation cannot be retrieved.
55 """
56 logger.info("Collecting Wazuh Indexer node allocation")
57 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
57 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
58 try:
59 raw_node_allocation_data = es_client.cat.allocation(format="json")
60 logger.info(raw_node_allocation_data)
61
62 - formatted_node_allocation_data = format_node_allocation(raw_node_allocation_data)
62 + formatted_node_allocation_data = await format_node_allocation(raw_node_allocation_data)
63
64 node_allocation_models = [NodeAllocation(**node) for node in formatted_node_allocation_data]
65
@@ -73,7 +73,7 @@ def node_allocation() -> Union[NodeAllocationResponse, Dict[str, bool]]:
73 raise Exception(str(e))
74
75
76 -def indices_stats() -> Union[IndicesStatsResponse, Dict[str, str]]:
76 +async def indices_stats() -> Union[IndicesStatsResponse, Dict[str, str]]:
77 """
78 Returns the indices stats of the Wazuh Indexer service.
79
@@ -84,11 +84,11 @@ def indices_stats() -> Union[IndicesStatsResponse, Dict[str, str]]:
84 Exception: An exception is raised if the indices stats cannot be retrieved.
85 """
86 logger.info("Collecting Wazuh Indexer indices stats")
87 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
87 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
88 try:
89 raw_indices_stats_data = es_client.cat.indices(format="json")
90
91 - formatted_indices_stats_data = format_indices_stats(raw_indices_stats_data)
91 + formatted_indices_stats_data = await format_indices_stats(raw_indices_stats_data)
92
93 indices_stats_models = [IndicesStats(**index) for index in formatted_indices_stats_data]
94
@@ -102,7 +102,7 @@ def indices_stats() -> Union[IndicesStatsResponse, Dict[str, str]]:
102 raise Exception(str(e))
103
104
105 -def shards() -> Union[ShardsResponse, Dict[str, str]]:
105 +async def shards() -> Union[ShardsResponse, Dict[str, str]]:
106 """
107 Returns the shards of the Wazuh Indexer service.
108
@@ -113,11 +113,11 @@ def shards() -> Union[ShardsResponse, Dict[str, str]]:
113 Exception: An exception is raised if the shards cannot be retrieved.
114 """
115 logger.info("Collecting Wazuh Indexer shards")
116 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
116 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
117 try:
118 raw_shards_data = es_client.cat.shards(format="json")
119
120 - formatted_shards_data = format_shards(raw_shards_data)
120 + formatted_shards_data = await format_shards(raw_shards_data)
121
122 shard_models = [Shards(**shard) for shard in formatted_shards_data]
123
backend/app/connectors/wazuh_indexer/utils/universal.py
+16 -11
@@ -12,9 +12,10 @@ from loguru import logger
12 from app.connectors.utils import get_connector_info_from_db
13 from app.connectors.wazuh_indexer.schema.indices import IndexConfigModel
14 from app.connectors.wazuh_indexer.schema.indices import Indices
15 +from app.db.db_session import get_db_session
16
17
17 -def verify_wazuh_indexer_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
18 +async def verify_wazuh_indexer_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
19 """
20 Verifies the connection to Wazuh Indexer service.
21
@@ -40,28 +41,32 @@ def verify_wazuh_indexer_credentials(attributes: Dict[str, Any]) -> Dict[str, An
41 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error: {e}"}
42
43
43 -def verify_wazuh_indexer_connection(connector_name: str) -> str:
44 +async def verify_wazuh_indexer_connection(connector_name: str) -> str:
45 """
46 Returns the authentication token for the Wazuh Indexer service.
47
48 Returns:
49 str: Authentication token for the Wazuh Indexer service.
50 """
50 - attributes = get_connector_info_from_db(connector_name)
51 + async with get_db_session() as session: # This will correctly enter the context manager
52 + attributes = await get_connector_info_from_db(connector_name, session)
53 + logger.info(f"Verifying the wazuh-indexer connection to {attributes['connector_url']}")
54 if attributes is None:
55 logger.error("No Wazuh Indexer connector found in the database")
56 return None
54 - return verify_wazuh_indexer_credentials(attributes)
57 + return await verify_wazuh_indexer_credentials(attributes)
58
59
57 -def create_wazuh_indexer_client(connector_name: str) -> Elasticsearch:
60 +async def create_wazuh_indexer_client(connector_name: str) -> Elasticsearch:
61 """
62 Returns an Elasticsearch client for the Wazuh Indexer service.
63
64 Returns:
65 Elasticsearch: Elasticsearch client for the Wazuh Indexer service.
66 """
64 - attributes = get_connector_info_from_db(connector_name)
67 + # attributes = get_connector_info_from_db(connector_name)
68 + async with get_db_session() as session: # This will correctly enter the context manager
69 + attributes = await get_connector_info_from_db(connector_name, session)
70 if attributes is None:
71 raise HTTPException(status_code=500, detail=f"No {connector_name} connector found in the database")
72 try:
@@ -77,7 +82,7 @@ def create_wazuh_indexer_client(connector_name: str) -> Elasticsearch:
82 raise HTTPException(status_code=500, detail=f"Failed to create Elasticsearch client: {e}")
83
84
80 -def format_node_allocation(node_allocation):
85 +async def format_node_allocation(node_allocation):
86 """
87 Format the node allocation details into a list of dictionaries. Each dictionary contains disk used, disk available, total disk, disk
88 usage percentage, and node name.
@@ -100,7 +105,7 @@ def format_node_allocation(node_allocation):
105 ]
106
107
103 -def format_indices_stats(indices_stats):
108 +async def format_indices_stats(indices_stats):
109 """
110 Format the indices stats details into a list of dictionaries. Each dictionary contains the index name, the number of documents in the index,
111 the size of the index, and the number of shards in the index.
@@ -123,7 +128,7 @@ def format_indices_stats(indices_stats):
128 ]
129
130
126 -def format_shards(shards):
131 +async def format_shards(shards):
132 """
133 Format the shards details into a list of dictionaries. Each dictionary contains the index name, the shard number, the shard state, the shard
134 size, and the node name.
@@ -146,7 +151,7 @@ def format_shards(shards):
151 ]
152
153
149 -def collect_indices() -> Indices:
154 +async def collect_indices() -> Indices:
155 """
156 Collects the indices from Elasticsearch.
157
@@ -154,7 +159,7 @@ def collect_indices() -> Indices:
159 dict: A dictionary containing the indices, shards, and indices stats.
160 """
161 logger.info("Collecting indices from Elasticsearch")
157 - es = create_wazuh_indexer_client("Wazuh-Indexer")
162 + es = await create_wazuh_indexer_client("Wazuh-Indexer")
163 try:
164 indices_dict = es.indices.get_alias("*")
165 indices_list = list(indices_dict.keys())
backend/app/connectors/wazuh_manager/routes/rules.py
+81 -17
@@ -2,6 +2,9 @@ from fastapi import APIRouter
2 from fastapi import Depends
3 from fastapi import HTTPException
4 from fastapi import Security
5 +from loguru import logger
6 +from sqlalchemy.ext.asyncio import AsyncSession
7 +from sqlalchemy.future import select
8
9 # App specific imports
10 from app.auth.routes.auth import AuthHandler
@@ -16,10 +19,10 @@ from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
19 from app.connectors.wazuh_manager.services.rules import disable_rule
20 from app.connectors.wazuh_manager.services.rules import enable_rule
21 from app.connectors.wazuh_manager.services.rules import exclude_rule
19 -from app.db.db_session import session
22 +from app.db.db_session import get_session
23
24 NEW_LEVEL = "1"
22 -wazuh_manager_router = APIRouter()
25 +wazuh_manager_rules_router = APIRouter()
26 auth_handler = AuthHandler()
27
28
@@ -27,28 +30,63 @@ def query_disabled_rule(rule_id: str):
30 return session.query(DisabledRule).filter(DisabledRule.rule_id == rule_id).first()
31
32
30 -@wazuh_manager_router.get(
33 +@wazuh_manager_rules_router.get(
34 "/rule/disabled",
35 response_model=AllDisabledRuleResponse,
36 description="Get all disabled rules",
37 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
38 )
36 -async def get_disabled_rules() -> AllDisabledRuleResponse:
37 - disabled_rules = session.query(DisabledRule).all()
39 +async def get_disabled_rules(session: AsyncSession = Depends(get_session)) -> AllDisabledRuleResponse:
40 + result = await session.execute(select(DisabledRule))
41 + disabled_rules = result.scalars().all()
42 return AllDisabledRuleResponse(disabled_rules=disabled_rules, success=True, message="Successfully fetched all disabled rules")
43
44
41 -@wazuh_manager_router.post(
45 +# @wazuh_manager_rules_router.post(
46 +# "/rule/disable",
47 +# response_model=RuleDisableResponse,
48 +# description="Disable a Wazuh Rule",
49 +# dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
50 +# )
51 +# async def disable_wazuh_rule(rule: RuleDisable, username: str = Depends(auth_handler.get_current_user)) -> RuleDisableResponse:
52 +# if query_disabled_rule(rule.rule_id):
53 +# raise HTTPException(status_code=500, detail="Rule is already disabled")
54 +
55 +# rule_disabled = disable_rule(rule)
56 +# if rule_disabled:
57 +# new_disabled_rule = DisabledRule(
58 +# rule_id=rule.rule_id,
59 +# previous_level=rule_disabled.previous_level,
60 +# new_level=NEW_LEVEL,
61 +# reason_for_disabling=rule.reason_for_disabling,
62 +# length_of_time=rule.length_of_time,
63 +# disabled_by=username.username,
64 +# )
65 +# session.add(new_disabled_rule)
66 +# session.commit()
67 +# return rule_disabled
68 +# else:
69 +# raise HTTPException(status_code=404, detail="Was not able to disable rule")
70 +
71 +
72 +@wazuh_manager_rules_router.post(
73 "/rule/disable",
74 response_model=RuleDisableResponse,
75 description="Disable a Wazuh Rule",
76 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
77 )
47 -async def disable_wazuh_rule(rule: RuleDisable, username: str = Depends(auth_handler.get_current_user)) -> RuleDisableResponse:
48 - if query_disabled_rule(rule.rule_id):
78 +async def disable_wazuh_rule(
79 + rule: RuleDisable,
80 + session: AsyncSession = Depends(get_session),
81 + username: str = Depends(AuthHandler().get_current_user),
82 +) -> RuleDisableResponse:
83 + # Asynchronously check if the rule is already disabled
84 + result = await session.execute(select(DisabledRule).where(DisabledRule.rule_id == rule.rule_id))
85 + if result.scalars().first():
86 raise HTTPException(status_code=500, detail="Rule is already disabled")
87
51 - rule_disabled = disable_rule(rule)
88 + # This should be converted to an async operation if it's not already
89 + rule_disabled = await disable_rule(rule)
90 if rule_disabled:
91 new_disabled_rule = DisabledRule(
92 rule_id=rule.rule_id,
@@ -59,29 +97,55 @@ async def disable_wazuh_rule(rule: RuleDisable, username: str = Depends(auth_han
97 disabled_by=username.username,
98 )
99 session.add(new_disabled_rule)
62 - session.commit()
100 + await session.commit()
101 return rule_disabled
102 else:
103 raise HTTPException(status_code=404, detail="Was not able to disable rule")
104
105
68 -@wazuh_manager_router.post(
106 +# @wazuh_manager_rules_router.post(
107 +# "/rule/enable",
108 +# response_model=RuleEnableResponse,
109 +# description="Enable a Wazuh Rule",
110 +# dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
111 +# )
112 +# async def enable_wazuh_rule(rule: RuleEnable) -> RuleEnableResponse:
113 +# disabled_rule = query_disabled_rule(rule.rule_id)
114 +# if not disabled_rule:
115 +# raise HTTPException(status_code=404, detail="Rule is already enabled")
116 +
117 +# previous_level = disabled_rule.previous_level
118 +# rule_enabled = enable_rule(rule, previous_level)
119 +
120 +# if rule_enabled:
121 +# session.delete(disabled_rule)
122 +# session.commit()
123 +# return rule_enabled
124 +# else:
125 +# raise HTTPException(status_code=404, detail="Was not able to enable rule")
126 +
127 +
128 +@wazuh_manager_rules_router.post(
129 "/rule/enable",
130 response_model=RuleEnableResponse,
131 description="Enable a Wazuh Rule",
132 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
133 )
74 -async def enable_wazuh_rule(rule: RuleEnable) -> RuleEnableResponse:
75 - disabled_rule = query_disabled_rule(rule.rule_id)
134 +async def enable_wazuh_rule(rule: RuleEnable, session: AsyncSession = Depends(get_session)) -> RuleEnableResponse:
135 + # Asynchronously fetch the disabled rule
136 + logger.info(f"rule: {rule}")
137 + result = await session.execute(select(DisabledRule).where(DisabledRule.rule_id == rule.rule_id))
138 + disabled_rule = result.scalars().first()
139 +
140 if not disabled_rule:
141 raise HTTPException(status_code=404, detail="Rule is already enabled")
142
79 - previous_level = disabled_rule.previous_level
80 - rule_enabled = enable_rule(rule, previous_level)
143 + # This should be converted to an async operation if it's not already
144 + rule_enabled = await enable_rule(rule, disabled_rule.previous_level)
145
146 if rule_enabled:
83 - session.delete(disabled_rule)
84 - session.commit()
147 + await session.delete(disabled_rule)
148 + await session.commit()
149 return rule_enabled
150 else:
151 raise HTTPException(status_code=404, detail="Was not able to enable rule")
backend/app/connectors/wazuh_manager/services/rules.py
+31 -21
@@ -20,18 +20,18 @@ from app.connectors.wazuh_manager.utils.universal import send_get_request
20 from app.connectors.wazuh_manager.utils.universal import send_put_request
21
22
23 -def fetch_filename(rule_id: str) -> str:
23 +async def fetch_filename(rule_id: str) -> str:
24 endpoint = "rules"
25 params = {"rule_ids": rule_id}
26 - filename_data = send_get_request(endpoint=endpoint, params=params)
26 + filename_data = await send_get_request(endpoint=endpoint, params=params)
27 if filename_data["data"]["data"]["total_affected_items"] == 0:
28 raise HTTPException(status_code=404, detail=f"Rule {rule_id} not found. Make sure the rule ID is correct within the Wazuh Manager.")
29 return filename_data["data"]["data"]["affected_items"][0]["filename"]
30
31
32 -def fetch_file_content(filename: str) -> str:
32 +async def fetch_file_content(filename: str) -> str:
33 endpoint = f"rules/files/{filename}"
34 - file_content_data = send_get_request(endpoint=endpoint)
34 + file_content_data = await send_get_request(endpoint=endpoint)
35 if file_content_data["data"]["data"]["total_affected_items"] == 0:
36 raise HTTPException(
37 status_code=404,
@@ -40,7 +40,7 @@ def fetch_file_content(filename: str) -> str:
40 return file_content_data["data"]["data"]["affected_items"][0]["group"]
41
42
43 -def set_rule_level(file_content: Any, rule_id: str, new_level: str) -> Tuple[str, Any]:
43 +async def set_rule_level(file_content: Any, rule_id: str, new_level: str) -> Tuple[str, Any]:
44 previous_level = None
45 try:
46 if isinstance(file_content, dict):
@@ -60,7 +60,7 @@ def set_rule_level(file_content: Any, rule_id: str, new_level: str) -> Tuple[str
60 return previous_level, file_content
61
62
63 -def convert_to_xml(updated_file_content: Union[Dict[str, str], List[Dict[str, str]]]) -> str:
63 +async def convert_to_xml(updated_file_content: Union[Dict[str, str], List[Dict[str, str]]]) -> str:
64 xml_content_list = []
65 try:
66 for group in updated_file_content:
@@ -75,8 +75,8 @@ def convert_to_xml(updated_file_content: Union[Dict[str, str], List[Dict[str, st
75 return xml_content
76
77
78 -def upload_updated_rule(filename: str, xml_content: str):
79 - response = send_put_request(
78 +async def upload_updated_rule(filename: str, xml_content: str):
79 + response = await send_put_request(
80 endpoint=f"rules/files/{filename}",
81 data=xml_content,
82 params={"overwrite": "true"},
@@ -87,12 +87,12 @@ def upload_updated_rule(filename: str, xml_content: str):
87 return response
88
89
90 -def process_rule(rule, rule_action_func, ResponseModel):
91 - filename, file_content = fetch_filename_and_content(rule.rule_id)
92 - previous_level, updated_file_content = rule_action_func(file_content, rule.rule_id)
93 - xml_content = convert_to_xml(updated_file_content)
94 - upload_updated_rule(filename, xml_content)
95 - restart_service()
90 +async def process_rule(rule, rule_action_func, ResponseModel):
91 + filename, file_content = await fetch_filename_and_content(rule.rule_id)
92 + previous_level, updated_file_content = await rule_action_func(file_content, rule.rule_id)
93 + xml_content = await convert_to_xml(updated_file_content)
94 + await upload_updated_rule(filename, xml_content)
95 + await restart_service()
96 return ResponseModel(
97 previous_level=previous_level,
98 success=True,
@@ -100,18 +100,28 @@ def process_rule(rule, rule_action_func, ResponseModel):
100 )
101
102
103 -def fetch_filename_and_content(rule_id: str) -> Tuple[str, str]:
104 - filename = fetch_filename(rule_id)
105 - file_content = fetch_file_content(filename)
103 +async def fetch_filename_and_content(rule_id: str) -> Tuple[str, str]:
104 + filename = await fetch_filename(rule_id)
105 + file_content = await fetch_file_content(filename)
106 return filename, file_content
107
108
109 -def disable_rule(rule: RuleDisable) -> RuleDisableResponse:
110 - return process_rule(rule, lambda fc, rid: set_rule_level(fc, rid, "1"), RuleDisableResponse)
109 +# async def disable_rule(rule: RuleDisable) -> RuleDisableResponse:
110 +# return await process_rule(rule, lambda fc, rid: set_rule_level(fc, rid, "1"), RuleDisableResponse)
111 +async def disable_rule(rule: RuleDisable) -> RuleDisableResponse:
112 + async def process(fc, rid):
113 + return await set_rule_level(fc, rid, "1")
114
115 + return await process_rule(rule, process, RuleDisableResponse)
116
113 -def enable_rule(rule: RuleEnable, previous_level: str) -> RuleEnableResponse:
114 - return process_rule(rule, lambda fc, rid: set_rule_level(fc, rid, previous_level), RuleEnableResponse)
117 +
118 +# async def enable_rule(rule: RuleEnable, previous_level: str) -> RuleEnableResponse:
119 +# return await process_rule(rule, lambda fc, rid: set_rule_level(fc, rid, previous_level), RuleEnableResponse)
120 +async def enable_rule(rule: RuleEnable, previous_level: str) -> RuleEnableResponse:
121 + async def process(fc, rid):
122 + return await set_rule_level(fc, rid, previous_level)
123 +
124 + return await process_rule(rule, process, RuleEnableResponse)
125
126
127 ################# ! EXCLUDE RULE ! #################
backend/app/connectors/wazuh_manager/utils/universal.py
+38 -21
@@ -6,9 +6,11 @@ import requests
6 from loguru import logger
7
8 from app.connectors.utils import get_connector_info_from_db
9 +from app.db.db_session import AsyncSessionLocal
10 +from app.db.db_session import get_db_session
11
12
11 -def verify_wazuh_manager_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
13 +async def verify_wazuh_manager_credentials(attributes: Dict[str, Any]) -> Dict[str, Any]:
14 """
15 Verifies the connection to Wazuh manager service.
16
@@ -40,7 +42,7 @@ def verify_wazuh_manager_credentials(attributes: Dict[str, Any]) -> Dict[str, An
42 return {"connectionSuccessful": False, "message": f"Connection to {attributes['connector_url']} failed with error."}
43
44
43 -def verify_wazuh_manager_connection(connector_name: str) -> str:
45 +async def verify_wazuh_manager_connection(connector_name: str) -> str:
46 """
47 Returns the authentication token for the Wazuh manager service.
48
@@ -48,14 +50,15 @@ def verify_wazuh_manager_connection(connector_name: str) -> str:
50 str: Authentication token for the Wazuh manager service.
51 """
52 logger.info("Getting Wazuh Manager authentication token")
51 - attributes = get_connector_info_from_db(connector_name)
53 + async with get_db_session() as session: # This will correctly enter the context manager
54 + attributes = await get_connector_info_from_db(connector_name, session)
55 if attributes is None:
56 logger.error("No Wazuh Manager connector found in the database")
57 return None
55 - return verify_wazuh_manager_credentials(attributes)
58 + return await verify_wazuh_manager_credentials(attributes)
59
60
58 -def create_wazuh_manager_client(connector_name: str) -> str:
61 +async def create_wazuh_manager_client(connector_name: str) -> str:
62 """
63 Returns the authentication token for the Wazuh manager service.
64
@@ -63,10 +66,13 @@ def create_wazuh_manager_client(connector_name: str) -> str:
66 str: Authentication token for the Wazuh manager service.
67 """
68 logger.info("Getting Wazuh Manager authentication token")
66 - attributes = get_connector_info_from_db(connector_name)
69 + # attributes = get_connector_info_from_db(connector_name)
70 + async with AsyncSessionLocal() as session:
71 + attributes = await get_connector_info_from_db(connector_name, session)
72 if attributes is None:
73 logger.error("No Wazuh Manager connector found in the database")
74 return None
75 + logger.info(f"Verifying the wazuh-manager connection to {attributes['connector_url']}")
76 try:
77 wazuh_auth_token = requests.get(
78 f"{attributes['connector_url']}/security/user/authenticate",
@@ -93,7 +99,7 @@ def create_wazuh_manager_client(connector_name: str) -> str:
99 return None
100
101
96 -def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
102 +async def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
103 """
104 Sends a GET request to the Wazuh Manager service.
105
@@ -106,8 +112,11 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
112 Dict[str, Any]: The response from the GET request.
113 """
114 logger.info(f"Sending GET request to {endpoint}")
109 - wazuh_manager_client = create_wazuh_manager_client(connector_name)
110 - attributes = get_connector_info_from_db(connector_name)
115 + wazuh_manager_client = await create_wazuh_manager_client(connector_name)
116 + # attributes = get_connector_info_from_db(connector_name)
117 + async with AsyncSessionLocal() as session:
118 + attributes = await get_connector_info_from_db(connector_name, session)
119 +
120 if attributes is None:
121 logger.error("No Wazuh Manager connector found in the database")
122 return None
@@ -125,7 +134,7 @@ def send_get_request(endpoint: str, params: Optional[Dict[str, Any]] = None, con
134 return {"success": False, "message": f"Failed to send GET request to {endpoint} with error: {e}"}
135
136
128 -def send_post_request(endpoint: str, data: Dict[str, Any], connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
137 +async def send_post_request(endpoint: str, data: Dict[str, Any], connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
138 """
139 Sends a POST request to the Wazuh Manager service.
140
@@ -138,8 +147,9 @@ def send_post_request(endpoint: str, data: Dict[str, Any], connector_name: str =
147 Dict[str, Any]: The response from the POST request.
148 """
149 logger.info(f"Sending POST request to {endpoint}")
141 - wazuh_manager_client = create_wazuh_manager_client(connector_name)
142 - attributes = get_connector_info_from_db(connector_name)
150 + wazuh_manager_client = await create_wazuh_manager_client(connector_name)
151 + async with AsyncSessionLocal() as session:
152 + attributes = await get_connector_info_from_db(connector_name, session)
153 if attributes is None:
154 logger.error("No Wazuh Manager connector found in the database")
155 return None
@@ -157,7 +167,7 @@ def send_post_request(endpoint: str, data: Dict[str, Any], connector_name: str =
167 return {"success": False, "message": f"Failed to send POST request to {endpoint} with error: {e}"}
168
169
160 -def send_put_request(
170 +async def send_put_request(
171 endpoint: str,
172 data: Optional[Dict[str, Any]],
173 params: Optional[Dict[str, str]] = None,
@@ -175,8 +185,9 @@ def send_put_request(
185 Dict[str, Any]: The response from the PUT request.
186 """
187 logger.info(f"Sending PUT request to {endpoint}")
178 - wazuh_manager_client = create_wazuh_manager_client(connector_name)
179 - attributes = get_connector_info_from_db(connector_name)
188 + wazuh_manager_client = await create_wazuh_manager_client(connector_name)
189 + async with AsyncSessionLocal() as session:
190 + attributes = await get_connector_info_from_db(connector_name, session)
191 if attributes is None:
192 logger.error("No Wazuh Manager connector found in the database")
193 return None
@@ -195,7 +206,11 @@ def send_put_request(
206 return {"success": False, "message": f"Failed to send PUT request to {endpoint} with error: {e}"}
207
208
198 -def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None, connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
209 +async def send_delete_request(
210 + endpoint: str,
211 + params: Optional[Dict[str, Any]] = None,
212 + connector_name: str = "Wazuh-Manager",
213 +) -> Dict[str, Any]:
214 """
215 Sends a DELETE request to the Wazuh Manager service.
216
@@ -208,8 +223,9 @@ def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None,
223 Dict[str, Any]: The response from the DELETE request.
224 """
225 logger.info(f"Sending DELETE request to {endpoint}")
211 - wazuh_manager_client = create_wazuh_manager_client(connector_name)
212 - attributes = get_connector_info_from_db(connector_name)
226 + wazuh_manager_client = await create_wazuh_manager_client(connector_name)
227 + async with AsyncSessionLocal() as session:
228 + attributes = await get_connector_info_from_db(connector_name, session)
229 if attributes is None:
230 logger.error("No Wazuh Manager connector found in the database")
231 return None
@@ -227,7 +243,7 @@ def send_delete_request(endpoint: str, params: Optional[Dict[str, Any]] = None,
243 return {"success": False, "message": f"Failed to send DELETE request to {endpoint} with error: {e}"}
244
245
230 -def restart_service(connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
246 +async def restart_service(connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
247 """
248 Restarts the Wazuh Manager service.
249
@@ -235,8 +251,9 @@ def restart_service(connector_name: str = "Wazuh-Manager") -> Dict[str, Any]:
251 Dict[str, Any]: The response from the DELETE request.
252 """
253 logger.info("Restarting Wazuh Manager service")
238 - wazuh_manager_client = create_wazuh_manager_client(connector_name)
239 - attributes = get_connector_info_from_db(connector_name)
254 + wazuh_manager_client = await create_wazuh_manager_client(connector_name)
255 + async with AsyncSessionLocal() as session:
256 + attributes = await get_connector_info_from_db(connector_name, session)
257 if attributes is None:
258 logger.error("No Wazuh Manager connector found in the database")
259 return None
backend/app/customers/routes/customers.py
+238 -84
@@ -1,9 +1,15 @@
1 from fastapi import APIRouter
2 +from fastapi import Depends
3 from fastapi import HTTPException
4 from fastapi import Query
5 +from fastapi import Security
6 from loguru import logger
7 +from sqlalchemy.ext.asyncio import AsyncSession
8 +from sqlalchemy.future import select
9 from starlette.status import HTTP_401_UNAUTHORIZED
10
11 +from app.auth.utils import AuthHandler
12 +
13 # App specific imports
14 from app.customers.schema.customers import AgentModel
15 from app.customers.schema.customers import AgentsResponse
@@ -13,6 +19,7 @@ from app.customers.schema.customers import CustomerMetaResponse
19 from app.customers.schema.customers import CustomerRequestBody
20 from app.customers.schema.customers import CustomerResponse
21 from app.customers.schema.customers import CustomersResponse
22 +from app.db.db_session import get_session
23 from app.db.db_session import session
24 from app.db.universal_models import Agents
25 from app.db.universal_models import Customers
@@ -32,208 +39,355 @@ def verify_admin(user):
39 raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail="Unauthorized")
40
41
35 -def verify_unique_customer_code(customer: CustomerRequestBody):
36 - existing_customer = session.query(Customers).filter(Customers.customer_code == customer.customer_code).first()
42 +async def verify_unique_customer_code(session: AsyncSession, customer: CustomerRequestBody):
43 + stmt = select(Customers).filter(Customers.customer_code == customer.customer_code)
44 + result = await session.execute(stmt)
45 + existing_customer = result.scalars().first()
46 if existing_customer:
47 raise HTTPException(status_code=400, detail="Customer with this customer_code already exists")
48
49
41 -@customers_router.post("", response_model=CustomerResponse, description="Create a new customer")
42 -async def create_customer(customer: CustomerRequestBody) -> CustomerResponse:
43 - verify_unique_customer_code(customer)
50 +@customers_router.post(
51 + "",
52 + response_model=CustomerResponse,
53 + description="Create a new customer",
54 + dependencies=[Security(AuthHandler().require_any_scope("admin"))],
55 +)
56 +async def create_customer(customer: CustomerRequestBody, session: AsyncSession = Depends(get_session)) -> CustomerResponse:
57 + await verify_unique_customer_code(session, customer)
58 logger.info(f"Creating new customer: {customer}")
59 new_customer = Customers(**customer.dict())
60 session.add(new_customer)
47 - session.commit()
61 + await session.commit() # Use await to perform the commit operation asynchronously
62 return CustomerResponse(customer=customer, success=True, message="Customer created successfully")
63
64
51 -@customers_router.get("", response_model=CustomersResponse, description="Get all customers")
52 -async def get_customers() -> CustomersResponse:
65 +@customers_router.get(
66 + "",
67 + response_model=CustomersResponse,
68 + description="Get all customers",
69 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
70 +)
71 +async def get_customers(session: AsyncSession = Depends(get_session)) -> CustomersResponse:
72 logger.info("Fetching all customers")
54 - customers = session.query(Customers).all()
55 - # Explode the customers list into a list of Customer objects
56 - customers = [CustomerRequestBody.parse_obj(customer.__dict__) for customer in customers]
57 - return CustomersResponse(customers=customers, success=True, message="Customers fetched successfully")
73
74 + # Asynchronous query to fetch all customers
75 + result = await session.execute(select(Customers))
76 + customers = result.scalars().all()
77 +
78 + # Parse the customer ORM objects into schema objects
79 + customers_list = [CustomerRequestBody.from_orm(customer) for customer in customers]
80 + return CustomersResponse(customers=customers_list, success=True, message="Customers fetched successfully")
81
60 -@customers_router.get("/{customer_code}", response_model=CustomerResponse, description="Get customer by customer_code")
61 -async def get_customer(customer_code: str) -> CustomerResponse:
82 +
83 +@customers_router.get(
84 + "/{customer_code}",
85 + response_model=CustomerResponse,
86 + description="Get customer by customer_code",
87 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
88 +)
89 +async def get_customer(customer_code: str, session: AsyncSession = Depends(get_session)) -> CustomerResponse:
90 logger.info(f"Fetching customer with customer_code: {customer_code}")
63 - customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
91 +
92 + # Asynchronous query to fetch customer
93 + result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
94 + customer = result.scalars().first()
95 +
96 if not customer:
97 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
66 - return CustomerResponse(
67 - customer=CustomerRequestBody.parse_obj(customer.__dict__),
68 - success=True,
69 - message="Customer fetched successfully",
70 - )
98
99 + # Convert ORM object to Pydantic model
100 + customer_data = CustomerRequestBody.from_orm(customer)
101 + return CustomerResponse(customer=customer_data, success=True, message="Customer fetched successfully")
102
73 -@customers_router.put("/{customer_code}", response_model=CustomerResponse, description="Update customer by customer_code")
74 -async def update_customer(customer_code: str, customer: CustomerRequestBody) -> CustomerResponse:
103 +
104 +@customers_router.put(
105 + "/{customer_code}",
106 + response_model=CustomerResponse,
107 + description="Update customer by customer_code",
108 + dependencies=[Security(AuthHandler().require_any_scope("admin"))],
109 +)
110 +async def update_customer(
111 + customer_code: str,
112 + customer: CustomerRequestBody,
113 + session: AsyncSession = Depends(get_session),
114 +) -> CustomerResponse:
115 logger.info(f"Updating customer with customer_code: {customer_code}")
76 - existing_customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
116 +
117 + # Asynchronous query to find the existing customer
118 + result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
119 + existing_customer = result.scalars().first()
120 +
121 if not existing_customer:
122 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
79 - existing_customer.update_from_model(customer)
80 - session.commit()
123 +
124 + # Update model instance with input data
125 + for key, value in customer.dict().items():
126 + setattr(existing_customer, key, value)
127 +
128 + await session.commit() # Commit changes asynchronously
129 +
130 return CustomerResponse(
82 - customer=CustomerRequestBody.parse_obj(customer.__dict__),
131 + customer=customer, # CustomerRequestBody is already a Pydantic model
132 success=True,
133 message="Customer updated successfully",
134 )
135
136
88 -# ! TODO: Fix delete customer
89 -# @customers_router.delete("/{customer_code}", response_model=CustomerResponse, description="Delete customer by customer_code")
90 -# async def delete_customer(customer_code: str) -> CustomerResponse:
91 -# logger.info(f"Deleting customer with customer_code: {customer_code}")
92 -# existing_customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
93 -# if not existing_customer:
94 -# raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
95 -# session.delete(existing_customer)
96 -# session.commit()
97 -# return CustomerResponse(customer=CustomerRequestBody.parse_obj(existing_customer.__dict__), success=True, message="Customer deleted successfully")
137 +# ! TODO - Add a check to ensure that the customer_code is not being used by any agents
138 +@customers_router.delete(
139 + "/{customer_code}",
140 + response_model=CustomerResponse,
141 + description="Delete customer by customer_code",
142 + dependencies=[Security(AuthHandler().require_any_scope("admin"))],
143 +)
144 +async def delete_customer(customer_code: str, session: AsyncSession = Depends(get_session)) -> CustomerResponse:
145 + logger.info(f"Deleting customer with customer_code: {customer_code}")
146 +
147 + result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
148 + existing_customer = result.scalars().first()
149 +
150 + if not existing_customer:
151 + raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
152 +
153 + # Capture the customer data before deleting
154 + customer_data = CustomerRequestBody.from_orm(existing_customer)
155
156 + # Delete the customer
157 + session.delete(existing_customer)
158 + await session.flush() # Optional: Flush the changes to the database
159 + await session.commit() # Commit the transaction
160
100 -@customers_router.post("/{customer_code}/meta", response_model=CustomerMetaResponse, description="Add new customer meta")
101 -async def add_customer_meta(customer_code: str, customer_meta: CustomerMetaRequestBody) -> CustomerMetaResponse:
161 + return CustomerResponse(
162 + customer=customer_data,
163 + success=True,
164 + message="Customer deleted successfully",
165 + )
166 +
167 +
168 +@customers_router.post(
169 + "/{customer_code}/meta",
170 + response_model=CustomerMetaResponse,
171 + description="Add new customer meta",
172 + dependencies=[Security(AuthHandler().require_any_scope("admin"))],
173 +)
174 +async def add_customer_meta(
175 + customer_code: str,
176 + customer_meta: CustomerMetaRequestBody,
177 + session: AsyncSession = Depends(get_session),
178 +) -> CustomerMetaResponse:
179 logger.info(f"Adding new customer meta: {customer_meta}")
103 - existing_customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
180 +
181 + result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
182 + existing_customer = result.scalars().first()
183 +
184 if not existing_customer:
185 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
106 - # Get the customer_code and customer_name from the existing customer and add it to the customer_meta object
186 +
187 logger.info(f"Got existing customer: {existing_customer}")
188 new_customer_meta = CustomersMeta(**customer_meta.dict())
189 new_customer_meta.customer_code = existing_customer.customer_code
190 new_customer_meta.customer_name = existing_customer.customer_name
191 +
192 session.add(new_customer_meta)
112 - session.commit()
193 + await session.commit() # Use await to perform the commit operation asynchronously
194 +
195 return CustomerMetaResponse(customer_meta=customer_meta, success=True, message="Customer meta added successfully")
196
197
116 -@customers_router.get("/{customer_code}/meta", response_model=CustomerMetaResponse, description="Get customer meta by customer_code")
117 -async def get_customer_meta(customer_code: str) -> CustomerMetaResponse:
198 +@customers_router.get(
199 + "/{customer_code}/meta",
200 + response_model=CustomerMetaResponse,
201 + description="Get customer meta by customer_code",
202 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
203 +)
204 +async def get_customer_meta(customer_code: str, session: AsyncSession = Depends(get_session)) -> CustomerMetaResponse:
205 logger.info(f"Fetching customer meta with customer_code: {customer_code}")
119 - customer_meta = session.query(CustomersMeta).filter(CustomersMeta.customer_code == customer_code).first()
206 +
207 + result = await session.execute(select(CustomersMeta).filter(CustomersMeta.customer_code == customer_code))
208 + customer_meta = result.scalars().first()
209 +
210 if not customer_meta:
211 raise HTTPException(status_code=404, detail=f"Customer meta with customer_code {customer_code} not found")
212 +
213 + # Assuming CustomerMetaRequestBody can be created from the ORM model directly
214 + customer_meta_data = CustomerMetaRequestBody.from_orm(customer_meta)
215 return CustomerMetaResponse(
123 - customer_meta=CustomerMetaRequestBody.parse_obj(customer_meta.__dict__),
216 + customer_meta=customer_meta_data,
217 success=True,
218 message="Customer meta fetched successfully",
219 )
220
221
129 -@customers_router.put("/{customer_code}/meta", response_model=CustomerMetaResponse, description="Update customer meta by customer_code")
130 -async def update_customer_meta(customer_code: str, customer_meta: CustomerMetaRequestBody) -> CustomerMetaResponse:
222 +@customers_router.put(
223 + "/{customer_code}/meta",
224 + response_model=CustomerMetaResponse,
225 + description="Update customer meta by customer_code",
226 + dependencies=[Security(AuthHandler().require_any_scope("admin"))],
227 +)
228 +async def update_customer_meta(
229 + customer_code: str,
230 + customer_meta: CustomerMetaRequestBody,
231 + session: AsyncSession = Depends(get_session),
232 +) -> CustomerMetaResponse:
233 logger.info(f"Updating customer meta with customer_code: {customer_code}")
132 - existing_customer_meta = session.query(CustomersMeta).filter(CustomersMeta.customer_code == customer_code).first()
234 +
235 + result = await session.execute(select(CustomersMeta).filter(CustomersMeta.customer_code == customer_code))
236 + existing_customer_meta = result.scalars().first()
237 +
238 if not existing_customer_meta:
239 raise HTTPException(status_code=404, detail=f"Customer meta with customer_code {customer_code} not found")
240
241 # Update the existing record with new values
137 - existing_customer_meta.update_from_model(customer_meta)
138 - logger.info(f"Updated existing customer meta: {existing_customer_meta}")
242 + for key, value in customer_meta.dict(exclude_unset=True).items():
243 + setattr(existing_customer_meta, key, value)
244
140 - # Commit the changes to the database
141 - session.commit()
245 + await session.commit() # Commit the changes to the database asynchronously
246
247 + # Return the updated customer_meta
248 return CustomerMetaResponse(
144 - customer_meta=CustomerMetaRequestBody.parse_obj(customer_meta.__dict__),
249 + customer_meta=customer_meta,
250 success=True,
251 message="Customer meta updated successfully",
252 )
253
254
150 -# ! TODO: Fix delete customer meta
151 -# @customers_router.delete("/{customer_code}/meta", response_model=CustomerMetaResponse, description="Delete customer meta by customer_code")
152 -# async def delete_customer_meta(customer_code: str) -> CustomerMetaResponse:
153 -# logger.info(f"Deleting customer meta with customer_code: {customer_code}")
154 -# existing_customer_meta = session.query(CustomersMeta).filter(CustomersMeta.customer_code == customer_code).first()
155 -# if not existing_customer_meta:
156 -# raise HTTPException(status_code=404, detail=f"Customer meta with customer_code {customer_code} not found")
157 -# session.delete(existing_customer_meta)
158 -# session.commit()
159 -# return CustomerMetaResponse(customer_meta=CustomerMetaRequestBody.parse_obj(existing_customer_meta.__dict__), success=True, message="Customer meta deleted successfully")
255 +# ! TODO - DELETE NOT WORKING
256 +@customers_router.delete(
257 + "/{customer_code}/meta",
258 + response_model=CustomerMetaResponse,
259 + description="Delete customer meta by customer_code",
260 + dependencies=[Security(AuthHandler().require_any_scope("admin"))],
261 +)
262 +async def delete_customer_meta(customer_code: str, session: AsyncSession = Depends(get_session)) -> CustomerMetaResponse:
263 + logger.info(f"Deleting customer meta with customer_code: {customer_code}")
264 +
265 + result = await session.execute(select(CustomersMeta).filter(CustomersMeta.customer_code == customer_code))
266 + existing_customer_meta = result.scalars().first()
267 +
268 + if not existing_customer_meta:
269 + raise HTTPException(status_code=404, detail=f"Customer meta with customer_code {customer_code} not found")
270 +
271 + # Store customer meta data for response before deleting
272 + customer_meta_data = CustomerMetaRequestBody.from_orm(existing_customer_meta)
273 +
274 + session.delete(existing_customer_meta)
275 + await session.commit() # Ensure to await commit
276 +
277 + return CustomerMetaResponse(
278 + customer_meta=customer_meta_data,
279 + success=True,
280 + message="Customer meta deleted successfully",
281 + )
282
283
284 @customers_router.get(
285 "/{customer_code}/full",
286 response_model=CustomerFullResponse,
287 description="Get customer and customer meta by customer_code",
288 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
289 )
167 -async def get_customer_full(customer_code: str) -> CustomerFullResponse:
290 +async def get_customer_full(customer_code: str, session: AsyncSession = Depends(get_session)) -> CustomerFullResponse:
291 logger.info(f"Fetching customer and customer meta with customer_code: {customer_code}")
169 - customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
292 +
293 + customer_result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
294 + customer = customer_result.scalars().first()
295 if not customer:
296 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
172 - customer_meta = session.query(CustomersMeta).filter(CustomersMeta.customer_code == customer_code).first()
297 +
298 + customer_meta_result = await session.execute(select(CustomersMeta).filter(CustomersMeta.customer_code == customer_code))
299 + customer_meta = customer_meta_result.scalars().first()
300 if not customer_meta:
301 raise HTTPException(status_code=404, detail=f"Customer meta with customer_code {customer_code} not found")
302 +
303 return CustomerFullResponse(
176 - customer=CustomerRequestBody.parse_obj(customer.__dict__),
177 - customer_meta=CustomerMetaRequestBody.parse_obj(customer_meta.__dict__),
304 + customer=CustomerRequestBody.from_orm(customer),
305 + customer_meta=CustomerMetaRequestBody.from_orm(customer_meta),
306 success=True,
307 message="Customer and customer meta fetched successfully",
308 )
309
310
183 -# Get Agents for the given customer_code
184 -@customers_router.get("/{customer_code}/agents", response_model=AgentsResponse, description="Get agents for the given customer_code")
185 -async def get_agents(customer_code: str) -> AgentsResponse:
311 +@customers_router.get(
312 + "/{customer_code}/agents",
313 + response_model=AgentsResponse,
314 + description="Get agents for the given customer_code",
315 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
316 +)
317 +async def get_agents(customer_code: str, session: AsyncSession = Depends(get_session)) -> AgentsResponse:
318 logger.info(f"Fetching agents for customer_code: {customer_code}")
187 - customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
319 +
320 + # Check if the customer exists
321 + customer_result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
322 + customer = customer_result.scalars().first()
323 if not customer:
324 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
190 - agents = session.query(Agents).filter(Agents.customer_code == customer_code).all()
191 - # Explode the agents list into a list of Agent objects
192 - agents = [AgentModel.parse_obj(agent.__dict__) for agent in agents]
193 - return AgentsResponse(agents=agents, success=True, message="Agents fetched successfully")
325 +
326 + # Asynchronously fetch all agents for the customer
327 + agents_result = await session.execute(select(Agents).filter(Agents.customer_code == customer_code))
328 + agents = agents_result.scalars().all()
329 +
330 + # Convert ORM objects to Pydantic models
331 + agents_list = [AgentModel.from_orm(agent) for agent in agents]
332 + return AgentsResponse(agents=agents_list, success=True, message="Agents fetched successfully")
333
334
196 -# Retrieve the agents for the given customer_code then perform a healthcheck on them
335 @customers_router.get(
336 "/{customer_code}/agents/healthcheck/wazuh",
337 response_model=AgentHealthCheckResponse,
338 description="Get agents healthcheck for the given customer_code",
339 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
340 )
341 async def get_wazuh_agents_healthcheck(
342 customer_code: str,
343 + session: AsyncSession = Depends(get_session),
344 minutes: int = Query(60, description="Number of minutes within which the agent should have been last seen to be considered healthy."),
345 hours: int = Query(0, description="Number of hours within which the agent should have been last seen to be considered healthy."),
346 days: int = Query(0, description="Number of days within which the agent should have been last seen to be considered healthy."),
347 ) -> AgentHealthCheckResponse:
348 logger.info(f"Fetching agents for customer_code: {customer_code}")
209 - customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
349 +
350 + # Asynchronously fetch customer and agents
351 + customer_result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
352 + customer = customer_result.scalars().first()
353 if not customer:
354 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
212 - agents = session.query(Agents).filter(Agents.customer_code == customer_code).all()
355 +
356 + agents_result = await session.execute(select(Agents).filter(Agents.customer_code == customer_code))
357 + agents = agents_result.scalars().all()
358 +
359 + # Convert ORM objects to Pydantic models
360 +
361 # Explode the agents list into a list of Agent objects
362 agents = [AgentModel.parse_obj(agent.__dict__) for agent in agents]
363 time_criteria = TimeCriteriaModel(minutes=minutes, hours=hours, days=days)
216 - return wazuh_agents_healthcheck(agents, time_criteria)
364 + return await wazuh_agents_healthcheck(agents, time_criteria)
365
366
219 -# Retrieve the agents for the given customer_code then perform a healthcheck on them
367 @customers_router.get(
368 "/{customer_code}/agents/healthcheck/velociraptor",
369 response_model=AgentHealthCheckResponse,
370 description="Get agents healthcheck for the given customer_code",
371 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
372 )
373 async def get_velociraptor_agents_healthcheck(
374 customer_code: str,
375 + session: AsyncSession = Depends(get_session),
376 minutes: int = Query(60, description="Number of minutes within which the agent should have been last seen to be considered healthy."),
377 hours: int = Query(0, description="Number of hours within which the agent should have been last seen to be considered healthy."),
378 days: int = Query(0, description="Number of days within which the agent should have been last seen to be considered healthy."),
379 ) -> AgentHealthCheckResponse:
380 logger.info(f"Fetching agents for customer_code: {customer_code}")
232 - customer = session.query(Customers).filter(Customers.customer_code == customer_code).first()
381 +
382 + # Asynchronously fetch customer
383 + customer_result = await session.execute(select(Customers).filter(Customers.customer_code == customer_code))
384 + customer = customer_result.scalars().first()
385 if not customer:
386 raise HTTPException(status_code=404, detail=f"Customer with customer_code {customer_code} not found")
235 - agents = session.query(Agents).filter(Agents.customer_code == customer_code).all()
236 - # Explode the agents list into a list of Agent objects
387 +
388 + # Asynchronously fetch all agents for the customer
389 + agents_result = await session.execute(select(Agents).filter(Agents.customer_code == customer_code))
390 + agents = agents_result.scalars().all()
391 agents = [AgentModel.parse_obj(agent.__dict__) for agent in agents]
392 time_criteria = TimeCriteriaModel(minutes=minutes, hours=hours, days=days)
393 return velociraptor_agents_healthcheck(agents, time_criteria)
backend/app/customers/schema/customers.py
+2
@@ -24,6 +24,7 @@ class CustomerRequestBody(BaseModel):
24 logo_file: Optional[str] = Field(None, description="Logo file for the customer")
25
26 class Config:
27 + orm_mode = True
28 schema_extra = {
29 "example": {
30 "customer_code": "CUST123",
@@ -67,6 +68,7 @@ class CustomerMetaRequestBody(BaseModel):
68 wazuh_log_ingestion_port: int = Field(..., description="Wazuh log ingestion port for the customer")
69
70 class Config:
71 + orm_mode = True
72 schema_extra = {
73 "example": {
74 "customer_meta_graylog_index": "graylog_index",
backend/app/db/all_models.py
+1
@@ -7,3 +7,4 @@ from app.db.universal_models import Agents
7 from app.db.universal_models import Customers
8 from app.db.universal_models import CustomersMeta
9 from app.db.universal_models import LogEntry
10 +from app.schedulers.models.scheduler import JobMetadata
backend/app/db/db_session.py
+46 -1
@@ -1,7 +1,52 @@
1 +# ! Old Testing without Async
2 +from contextlib import asynccontextmanager
3 +
4 from sqlmodel import Session
5 from sqlmodel import create_engine
6
7 from settings import SQLALCHEMY_DATABASE_URI
8
9 engine = create_engine(SQLALCHEMY_DATABASE_URI, connect_args={"check_same_thread": False})
7 -session = Session(bind=engine)
10 +# session = Session(bind=engine)
11 +session = "placeholder"
12 +
13 +
14 +#! New Testings with Async
15 +
16 +from loguru import logger
17 +from sqlalchemy.ext.asyncio import AsyncSession
18 +from sqlalchemy.ext.asyncio import create_async_engine
19 +from sqlalchemy.orm import sessionmaker
20 +
21 +from settings import SQLALCHEMY_DATABASE_URI
22 +
23 +# create async engine for SQLite using aiosqlite
24 +async_engine = create_async_engine(SQLALCHEMY_DATABASE_URI, echo=True)
25 +
26 +# create a configured "AsyncSession" class
27 +AsyncSessionLocal = sessionmaker(bind=async_engine, class_=AsyncSession, expire_on_commit=False)
28 +
29 +
30 +# Dependency to get DB session for each request
31 +# @asynccontextmanager
32 +# async def get_db_session():
33 +# async with AsyncSessionLocal() as session:
34 +# yield session
35 +@asynccontextmanager
36 +async def get_db_session():
37 + async with AsyncSessionLocal() as session:
38 + logger.info("DB session created")
39 + try:
40 + yield session
41 + except Exception as e:
42 + logger.error(f"Error during DB session: {e}")
43 + await session.rollback()
44 + raise e
45 + finally:
46 + logger.info("Closing DB session")
47 + await session.close()
48 +
49 +
50 +async def get_session():
51 + async with get_db_session() as session:
52 + return session
backend/app/db/db_setup.py
+35 -22
@@ -1,31 +1,44 @@
1 from loguru import logger
2 -from sqlalchemy import inspect
3 -from sqlmodel import Session
2 +from sqlalchemy.ext.asyncio import create_async_engine
3 +
4 +# ! New with Async
5 from sqlmodel import SQLModel
6
6 -# from app.db.all_models import *
7 -from app.db.db_populate import add_connectors_if_not_exist
8 -from app.db.db_populate import add_roles_if_not_exist
7 +# from sqlalchemy import inspect
8 +# from sqlmodel import Session
9 +# from sqlmodel import SQLModel
10
11 +# #from app.db.all_models import *
12 +# from app.schedulers.models.scheduler import JobMetadata
13 +# from app.db.db_populate import add_connectors_if_not_exist
14 +# from app.db.db_populate import add_roles_if_not_exist
15
11 -def create_tables(engine):
12 - logger.info("Creating tables")
16 +# ! Old without Async
17 +# def create_tables(engine):
18 +# logger.info("Creating tables")
19 +
20 +# # Create an inspector object based on the engine
21 +# inspector = inspect(engine)
22
14 - # Create an inspector object based on the engine
15 - inspector = inspect(engine)
23 +# # Get the names of all tables in the database
24 +# existing_tables = inspector.get_table_names()
25
17 - # Get the names of all tables in the database
18 - existing_tables = inspector.get_table_names()
26 +# # Loop through all your models (tables)
27 +# for table in SQLModel.metadata.sorted_tables:
28 +# if table.name not in existing_tables:
29 +# # Only create the table if it doesn't exist
30 +# table.create(bind=engine)
31 +# logger.info(f"Table {table.name} created.")
32
20 - # Loop through all your models (tables)
21 - for table in SQLModel.metadata.sorted_tables:
22 - if table.name not in existing_tables:
23 - # Only create the table if it doesn't exist
24 - table.create(bind=engine)
25 - logger.info(f"Table {table.name} created.")
33 +# # After creating all tables, add connectors if they don't exist
34 +# with Session(engine) as session:
35 +# add_connectors_if_not_exist(session)
36 +# add_roles_if_not_exist(session)
37 +# session.commit()
38
27 - # After creating all tables, add connectors if they don't exist
28 - with Session(engine) as session:
29 - add_connectors_if_not_exist(session)
30 - add_roles_if_not_exist(session)
31 - session.commit()
39 +
40 +async def create_tables(async_engine):
41 + logger.info("Creating tables")
42 + async with async_engine.begin() as conn:
43 + # This will create all tables
44 + await conn.run_sync(SQLModel.metadata.create_all)
backend/app/healthchecks/agents/routes/agents.py
+58 -17
@@ -1,9 +1,15 @@
1 from fastapi import APIRouter
2 +from fastapi import Depends
3 from fastapi import HTTPException
4 from fastapi import Query
5 +from fastapi import Security
6 from loguru import logger
7 +from sqlalchemy.ext.asyncio import AsyncSession
8 +from sqlalchemy.future import select
9 from starlette.status import HTTP_401_UNAUTHORIZED
10
11 +from app.auth.utils import AuthHandler
12 +from app.db.db_session import get_session
13 from app.db.db_session import session
14 from app.db.universal_models import Agents
15 from app.healthchecks.agents.schema.agents import AgentHealthCheckResponse
@@ -24,71 +30,106 @@ def verify_admin(user):
30 raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail="Unauthorized")
31
32
27 -@healtcheck_agents_router.get("/wazuh", response_model=AgentHealthCheckResponse, description="Get Wazuh agents healthcheck")
33 +@healtcheck_agents_router.get(
34 + "/wazuh",
35 + response_model=AgentHealthCheckResponse,
36 + description="Get Wazuh agents healthcheck",
37 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
38 +)
39 async def get_wazuh_agent_healthcheck(
40 + session: AsyncSession = Depends(get_session),
41 minutes: int = Query(60, description="Number of minutes within which the agent should have been last seen to be considered healthy."),
42 hours: int = Query(0, description="Number of hours within which the agent should have been last seen to be considered healthy."),
43 days: int = Query(0, description="Number of days within which the agent should have been last seen to be considered healthy."),
44 ) -> AgentHealthCheckResponse:
45 time_criteria = TimeCriteriaModel(minutes=minutes, hours=hours, days=days)
34 - agents = session.query(Agents).all()
35 - return wazuh_agents_healthcheck(agents, time_criteria)
46 +
47 + # Asynchronously fetch all agents
48 + result = await session.execute(select(Agents))
49 + agents = result.scalars().all()
50 + return await wazuh_agents_healthcheck(agents, time_criteria)
51
52
38 -# Get single agent by agent_id
53 @healtcheck_agents_router.get(
54 "/wazuh/{agent_id}",
55 response_model=AgentHealthCheckResponse,
56 description="Get Wazuh agent healthcheck by agent_id",
57 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
58 )
59 async def get_wazuh_agent_healthcheck_by_agent_id(
60 agent_id: str,
61 + session: AsyncSession = Depends(get_session),
62 minutes: int = Query(60, description="Number of minutes within which the agent should have been last seen to be considered healthy."),
63 hours: int = Query(0, description="Number of hours within which the agent should have been last seen to be considered healthy."),
64 days: int = Query(0, description="Number of days within which the agent should have been last seen to be considered healthy."),
65 ) -> AgentHealthCheckResponse:
66 time_criteria = TimeCriteriaModel(minutes=minutes, hours=hours, days=days)
51 - agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
67 +
68 + # Asynchronously fetch the agent by id
69 + result = await session.execute(select(Agents).filter(Agents.agent_id == agent_id))
70 + agent = result.scalars().first()
71 +
72 if not agent:
73 raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
54 - return wazuh_agent_healthcheck(agent, time_criteria)
74 + return await wazuh_agent_healthcheck(agent, time_criteria)
75
76
57 -@healtcheck_agents_router.get("/velociraptor", response_model=AgentHealthCheckResponse, description="Get Velociraptor agents healthcheck")
77 +@healtcheck_agents_router.get(
78 + "/velociraptor",
79 + response_model=AgentHealthCheckResponse,
80 + description="Get Velociraptor agents healthcheck",
81 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
82 +)
83 async def get_velociraptor_agent_healthcheck(
84 + session: AsyncSession = Depends(get_session),
85 minutes: int = Query(60, description="Number of minutes within which the agent should have been last seen to be considered healthy."),
86 hours: int = Query(0, description="Number of hours within which the agent should have been last seen to be considered healthy."),
87 days: int = Query(0, description="Number of days within which the agent should have been last seen to be considered healthy."),
88 ) -> AgentHealthCheckResponse:
89 time_criteria = TimeCriteriaModel(minutes=minutes, hours=hours, days=days)
64 - agents = session.query(Agents).all()
65 - return velociraptor_agents_healthcheck(agents, time_criteria)
90 +
91 + # Asynchronously fetch all agents
92 + result = await session.execute(select(Agents))
93 + agents = result.scalars().all()
94 + return await velociraptor_agents_healthcheck(agents, time_criteria)
95
96
68 -# Get single agent by agent_id
97 @healtcheck_agents_router.get(
98 "/velociraptor/{agent_id}",
99 response_model=AgentHealthCheckResponse,
100 description="Get Velociraptor agent healthcheck by agent_id",
101 + dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
102 )
103 async def get_velociraptor_agent_healthcheck_by_agent_id(
104 agent_id: str,
105 + session: AsyncSession = Depends(get_session),
106 minutes: int = Query(60, description="Number of minutes within which the agent should have been last seen to be considered healthy."),
107 hours: int = Query(0, description="Number of hours within which the agent should have been last seen to be considered healthy."),
108 days: int = Query(0, description="Number of days within which the agent should have been last seen to be considered healthy."),
109 ) -> AgentHealthCheckResponse:
110 time_criteria = TimeCriteriaModel(minutes=minutes, hours=hours, days=days)
81 - agent = session.query(Agents).filter(Agents.agent_id == agent_id).first()
111 +
112 + # Asynchronously fetch the agent by id
113 + result = await session.execute(select(Agents).filter(Agents.agent_id == agent_id))
114 + agent = result.scalars().first()
115 if not agent:
116 raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
84 - return velociraptor_agent_healthcheck(agent, time_criteria)
117 + return await velociraptor_agent_healthcheck(agent, time_criteria)
118
119
87 -@healtcheck_agents_router.post("/logs", response_model=HostLogsSearchResponse, description="Get host logs")
88 -async def get_host_logs(body: HostLogsSearchBody) -> HostLogsSearchResponse:
120 +@healtcheck_agents_router.post(
121 + "/logs",
122 + response_model=HostLogsSearchResponse,
123 + description="Get host logs",
124 + dependencies=[Security(AuthHandler().get_current_user, scopes=["admin", "analyst"])],
125 +)
126 +async def get_host_logs(body: HostLogsSearchBody, session: AsyncSession = Depends(get_session)) -> HostLogsSearchResponse:
127 logger.info(f"Received request to get host logs for {body.agent_name}")
90 - # Verify the agent exists
91 - agent = session.query(Agents).filter(Agents.hostname == body.agent_name).first()
128 +
129 + # Asynchronously verify the agent exists
130 + result = await session.execute(select(Agents).filter(Agents.hostname == body.agent_name))
131 + agent = result.scalars().first()
132 +
133 if not agent:
134 raise HTTPException(status_code=404, detail=f"Agent with hostname {body.agent_name} not found")
94 - return host_logs(body)
135 + return await host_logs(body)
backend/app/healthchecks/agents/services/agents.py
+11 -11
@@ -51,7 +51,7 @@ def is_velociraptor_agent_unhealthy(agent: AgentModel, time_criteria: TimeCriter
51 return ExtendedAgentModel(**agent.dict(), unhealthy_velociraptor_agent=is_unhealthy)
52
53
54 -def wazuh_agents_healthcheck(agents: list, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
54 +async def wazuh_agents_healthcheck(agents: list, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
55 healthy_wazuh_agents = []
56 unhealthy_wazuh_agents = []
57 for agent in agents:
@@ -74,7 +74,7 @@ def wazuh_agents_healthcheck(agents: list, time_criteria: TimeCriteriaModel) ->
74 )
75
76
77 -def wazuh_agent_healthcheck(agent: AgentModel, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
77 +async def wazuh_agent_healthcheck(agent: AgentModel, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
78 extended_agent = is_wazuh_agent_unhealthy(agent, time_criteria)
79 if extended_agent.unhealthy_wazuh_agent:
80 return AgentHealthCheckResponse(
@@ -92,7 +92,7 @@ def wazuh_agent_healthcheck(agent: AgentModel, time_criteria: TimeCriteriaModel)
92 )
93
94
95 -def velociraptor_agents_healthcheck(agents: list, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
95 +async def velociraptor_agents_healthcheck(agents: list, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
96 healthy_velociraptor_agents = []
97 unhealthy_velociraptor_agents = []
98 for agent in agents:
@@ -115,7 +115,7 @@ def velociraptor_agents_healthcheck(agents: list, time_criteria: TimeCriteriaMod
115 )
116
117
118 -def velociraptor_agent_healthcheck(agent: AgentModel, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
118 +async def velociraptor_agent_healthcheck(agent: AgentModel, time_criteria: TimeCriteriaModel) -> AgentHealthCheckResponse:
119 extended_agent = is_velociraptor_agent_unhealthy(agent, time_criteria)
120 if extended_agent.unhealthy_velociraptor_agent:
121 return AgentHealthCheckResponse(
@@ -133,8 +133,8 @@ def velociraptor_agent_healthcheck(agent: AgentModel, time_criteria: TimeCriteri
133 )
134
135
136 -def host_logs(search_body: HostLogsSearchBody) -> HostLogsSearchResponse:
137 - result = get_logs_generic(search_body, is_host_specific=True)
136 +async def host_logs(search_body: HostLogsSearchBody) -> HostLogsSearchResponse:
137 + result = await get_logs_generic(search_body, is_host_specific=True)
138 logger.info(f"Host logs search result: {result}")
139
140 # Initialize variable to keep track of total logs
@@ -159,15 +159,15 @@ def host_logs(search_body: HostLogsSearchBody) -> HostLogsSearchResponse:
159 )
160
161
162 -def get_logs_generic(search_body: Type[LogsSearchBody], is_host_specific: bool = False, index_name: Optional[str] = None):
162 +async def get_logs_generic(search_body: Type[LogsSearchBody], is_host_specific: bool = False, index_name: Optional[str] = None):
163 logger.info(f"Collecting Wazuh Indexer alerts for host {search_body.agent_name if is_host_specific else ''}")
164 logs_summary = []
165 - indices = collect_indices()
165 + indices = await collect_indices()
166 index_list = [index_name] if index_name else indices.indices_list # Use the provided index_name or get all indices
167
168 for index_name in index_list:
169 try:
170 - logs = collect_logs_generic(index_name, body=search_body, is_host_specific=is_host_specific)
170 + logs = await collect_logs_generic(index_name, body=search_body, is_host_specific=is_host_specific)
171 if logs.success and len(logs.logs) > 0:
172 logs_summary.append(
173 {
@@ -188,8 +188,8 @@ def get_logs_generic(search_body: Type[LogsSearchBody], is_host_specific: bool =
188 return {"logs_summary": logs_summary, "success": len(logs_summary) > 0, "message": message}
189
190
191 -def collect_logs_generic(index_name: str, body: LogsSearchBody, is_host_specific: bool = False) -> CollectLogsResponse:
192 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
191 +async def collect_logs_generic(index_name: str, body: LogsSearchBody, is_host_specific: bool = False) -> CollectLogsResponse:
192 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
193 query_builder = LogsQueryBuilder()
194 query_builder.add_time_range(timerange=body.timerange, timestamp_field=body.timestamp_field)
195 query_builder.add_matches(matches=[(body.log_field, body.log_value)])
backend/app/integrations/alert_escalation/routes/general_alert.py
+5 -2
@@ -1,8 +1,11 @@
1 from fastapi import APIRouter
2 +from fastapi import Depends
3 from fastapi import Security
4 from loguru import logger
5 +from sqlalchemy.ext.asyncio import AsyncSession
6
7 from app.auth.utils import AuthHandler
8 +from app.db.db_session import get_session
9 from app.integrations.alert_escalation.schema.general_alert import CreateAlertRequest
10 from app.integrations.alert_escalation.schema.general_alert import CreateAlertResponse
11 from app.integrations.alert_escalation.services.general_alert import create_alert
@@ -16,6 +19,6 @@ integration_general_alerts_router = APIRouter()
19 description="Create an alert in IRIS",
20 dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst"))],
21 )
19 -async def create_alert_route(create_alert_request: CreateAlertRequest) -> CreateAlertResponse:
22 +async def create_alert_route(create_alert_request: CreateAlertRequest, session: AsyncSession = Depends(get_session)) -> CreateAlertResponse:
23 logger.info(f"Creating alert {create_alert_request.alert_id} in IRIS")
21 - return create_alert(create_alert_request)
24 + return await create_alert(create_alert_request, session)
backend/app/integrations/alert_escalation/schema/general_alert.py
+1
@@ -24,6 +24,7 @@ class CreateAlertResponse(BaseModel):
24 success: bool
25 message: str
26 alert_id: int = Field(..., description="The alert id as created in IRIS.")
27 + alert_url: str = Field(..., description="The alert url as created in IRIS.")
28
29
30 class GenericSourceModel(BaseModel):
backend/app/integrations/alert_escalation/services/general_alert.py
+42 -8
@@ -3,9 +3,11 @@ from typing import Set
3
4 from fastapi import HTTPException
5 from loguru import logger
6 +from sqlalchemy.ext.asyncio import AsyncSession
7
8 from app.connectors.dfir_iris.utils.universal import fetch_and_validate_data
9 from app.connectors.dfir_iris.utils.universal import initialize_client_and_alert
10 +from app.connectors.utils import get_connector_info_from_db
11 from app.connectors.wazuh_indexer.utils.universal import create_wazuh_indexer_client
12 from app.integrations.alert_escalation.schema.general_alert import CreateAlertRequest
13 from app.integrations.alert_escalation.schema.general_alert import CreateAlertResponse
@@ -32,9 +34,9 @@ def valid_ioc_fields() -> Set[str]:
34 return {field.value for field in ValidIocFields}
35
36
35 -def get_single_alert_details(alert_details: CreateAlertRequest) -> GenericAlertModel:
37 +async def get_single_alert_details(alert_details: CreateAlertRequest) -> GenericAlertModel:
38 logger.info(f"Fetching alert details for alert {alert_details.alert_id} in index {alert_details.index_name}")
37 - es_client = create_wazuh_indexer_client("Wazuh-Indexer")
39 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
40 try:
41 alert = es_client.get(index=alert_details.index_name, id=alert_details.alert_id)
42 source_model = GenericSourceModel(**alert["_source"])
@@ -110,18 +112,50 @@ def build_alert_payload(alert_details: GenericAlertModel, agent_data, ioc_payloa
112 )
113
114
113 -def create_alert(alert: CreateAlertRequest) -> CreateAlertResponse:
115 +async def construct_soc_alert_url(root_url: str, soc_alert_id: int) -> str:
116 + """Constructs the full URL for the SOC alert."""
117 + url_path = f"/alerts?cid=1&page=1&per_page=10&sort=desc&alert_ids={soc_alert_id}"
118 + return f"{root_url}{url_path}"
119 +
120 +
121 +async def add_alert_to_document(es_client, alert: CreateAlertRequest, soc_alert_id: int, session: AsyncSession) -> Optional[str]:
122 + """
123 + Update the alert document in Elasticsearch with the provided SOC alert ID URL.
124 +
125 + Parameters:
126 + - es_client: The Elasticsearch client instance to use for the update.
127 + - alert: The alert request object containing alert_id and index_name.
128 + - soc_alert_id: The alert ID as it exists within IRIS.
129 + - session: The database session for retrieving connector information.
130 +
131 + Returns:
132 + - True if the update is successful, False otherwise.
133 + """
134 + try:
135 + connector_info = await get_connector_info_from_db("DFIR-IRIS", session)
136 + full_url = await construct_soc_alert_url(connector_info["connector_url"], soc_alert_id)
137 + es_client.update(index=alert.index_name, id=alert.alert_id, body={"doc": {"alert_url": full_url}})
138 + logger.info(f"Added alert ID {soc_alert_id} to alert {alert.alert_id} in index {alert.index_name}")
139 + return full_url
140 + except Exception as e:
141 + logger.error(f"Failed to add alert ID {soc_alert_id} to alert {alert.alert_id} in index {alert.index_name}: {e}")
142 + return None
143 +
144 +
145 +async def create_alert(alert: CreateAlertRequest, session: AsyncSession) -> CreateAlertResponse:
146 logger.info(f"Creating alert {alert.alert_id} in IRIS")
115 - alert_details = get_single_alert_details(alert_details=alert)
116 - agent_data = get_agent_data(agent_id=alert_details._source.agent_id)
147 + alert_details = await get_single_alert_details(alert_details=alert)
148 + agent_data = await get_agent_data(session, agent_id=alert_details._source.agent_id)
149 alert_details.asset_type_id = get_asset_type_id(os=agent_data.os)
150 ioc_payload = build_ioc_payload(alert_details)
151 iris_alert_payload = build_alert_payload(alert_details, agent_data, ioc_payload)
120 - client, alert = initialize_client_and_alert("DFIR-IRIS")
121 - result = fetch_and_validate_data(client, alert.add_alert, iris_alert_payload.to_dict())
152 + client, alert_client = await initialize_client_and_alert("DFIR-IRIS")
153 + result = await fetch_and_validate_data(client, alert_client.add_alert, iris_alert_payload.to_dict())
154 + es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
155 + iris_url = await add_alert_to_document(es_client, alert, result["data"]["alert_id"], session=session)
156 try:
157 alert_id = result["data"]["alert_id"]
124 - return CreateAlertResponse(alert_id=alert_id, success=True, message=f"Alert {alert_id} created successfully")
158 + return CreateAlertResponse(alert_id=alert_id, success=True, message=f"Alert {alert_id} created successfully", alert_url=iris_url)
159 except Exception as e:
160 logger.error(f"Failed to create alert {alert.alert_id}: {e}")
161 raise HTTPException(status_code=500, detail=f"Failed to create alert for ID {alert.alert_id}: {e}")
backend/app/integrations/alert_escalation/utils/universal.py
+11 -4
@@ -10,6 +10,8 @@ import regex
10 from elasticsearch7 import Elasticsearch
11 from fastapi import HTTPException
12 from loguru import logger
13 +from sqlalchemy.ext.asyncio import AsyncSession
14 +from sqlalchemy.future import select
15
16 from app.connectors.utils import get_connector_info_from_db
17 from app.db.all_models import Agents
@@ -341,19 +343,24 @@ def create_wazuh_indexer_client(connector_name: str) -> Elasticsearch:
343 )
344
345
344 -def get_agent_data(agent_id: str) -> AgentModel:
346 +async def get_agent_data(session: AsyncSession, agent_id: str) -> AgentModel:
347 """
348 Get agent data based on the agent id from the agents table.
349
350 Args:
351 + session (AsyncSession): The SQLAlchemy session.
352 agent_id (str): Agent id.
353
354 Returns:
352 - Dict[str, Any]: Agent data.
355 + AgentModel: Agent data.
356 """
354 - agent_details = session.query(Agents).filter(Agents.agent_id == agent_id).first()
357 + agent_query = select(Agents).filter(Agents.agent_id == agent_id)
358 + result = await session.execute(agent_query)
359 + agent_details = result.scalars().first()
360 +
361 if agent_details is not None:
356 - return agent_details
362 + # Assuming AgentModel can be created from the Agents ORM model
363 + return AgentModel.from_orm(agent_details)
364 else:
365 raise HTTPException(status_code=404, detail=f"Agent with id {agent_id} not found in agents table")
366
backend/app/middleware/exception_handlers.py new
+112
@@ -0,0 +1,112 @@
1 +# from fastapi import HTTPException
2 +# from fastapi import Request
3 +# from fastapi.exceptions import RequestValidationError
4 +# from fastapi.responses import JSONResponse
5 +# from sqlmodel import Session
6 +
7 +# from app.auth.utils import AuthHandler
8 +# from app.db.db_session import engine
9 +# from app.utils import ErrorType
10 +# from app.utils import Logger
11 +# from app.utils import ValidationErrorItem
12 +# from app.utils import ValidationErrorResponse
13 +
14 +
15 +# # Utility function to get user_id from request
16 +# async def get_user_id_from_request(request: Request, session, logger_instance):
17 +# return await logger_instance.get_user_id_from_request(request)
18 +
19 +
20 +# async def custom_http_exception_handler(request: Request, exc: HTTPException):
21 +# with Session(engine) as session:
22 +# logger_instance = Logger(session, AuthHandler())
23 +# user_id = await get_user_id_from_request(request, session, logger_instance)
24 +# await logger_instance.log_error(user_id, request, exc.detail)
25 +
26 +# return JSONResponse(
27 +# status_code=exc.status_code,
28 +# content={
29 +# "success": False,
30 +# "message": exc.detail,
31 +# },
32 +# )
33 +
34 +
35 +# async def validation_exception_handler(request: Request, exc: RequestValidationError):
36 +# errors = exc.errors()
37 +# details = []
38 +
39 +# for error in errors:
40 +# field = error["loc"][-1]
41 +# error_type = ErrorType(error["type"])
42 +# details.append(ValidationErrorItem(field=field, error_type=error_type))
43 +
44 +# main_message = details[0].message if details else "Validation Error"
45 +
46 +# with Session(engine) as session:
47 +# logger_instance = Logger(session, AuthHandler())
48 +# user_id = await get_user_id_from_request(request, session, logger_instance)
49 +# await logger_instance.log_error(user_id, request, main_message)
50 +
51 +# return JSONResponse(
52 +# status_code=422,
53 +# content=ValidationErrorResponse(message=main_message, details=details).dict(),
54 +# )
55 +
56 +# ! With Async
57 +from fastapi import HTTPException
58 +from fastapi import Request
59 +from fastapi.exceptions import RequestValidationError
60 +from fastapi.responses import JSONResponse
61 +from sqlalchemy.ext.asyncio import AsyncSession
62 +
63 +from app.auth.utils import AuthHandler
64 +from app.db.db_session import async_engine # Make sure to import the async engine
65 +from app.utils import ErrorType
66 +from app.utils import Logger
67 +from app.utils import ValidationErrorItem
68 +from app.utils import ValidationErrorResponse
69 +
70 +
71 +# Utility function to get user_id from request
72 +async def get_user_id_from_request(request: Request, logger_instance):
73 + return await logger_instance.get_user_id_from_request(request)
74 +
75 +
76 +async def custom_http_exception_handler(request: Request, exc: HTTPException):
77 + async with AsyncSession(async_engine) as session: # Use AsyncSession
78 + logger_instance = Logger(session, AuthHandler())
79 + user_id = await get_user_id_from_request(request, logger_instance)
80 + await logger_instance.log_error(user_id, request, exc.detail)
81 + await session.commit() # Make sure to commit the session
82 +
83 + return JSONResponse(
84 + status_code=exc.status_code,
85 + content={
86 + "success": False,
87 + "message": exc.detail,
88 + },
89 + )
90 +
91 +
92 +async def validation_exception_handler(request: Request, exc: RequestValidationError):
93 + errors = exc.errors()
94 + details = []
95 +
96 + for error in errors:
97 + field = error["loc"][-1]
98 + error_type = ErrorType(error["type"])
99 + details.append(ValidationErrorItem(field=field, error_type=error_type))
100 +
101 + main_message = details[0].message if details else "Validation Error"
102 +
103 + async with AsyncSession(async_engine) as session: # Use AsyncSession
104 + logger_instance = Logger(session, AuthHandler())
105 + user_id = await get_user_id_from_request(request, logger_instance)
106 + await logger_instance.log_error(user_id, request, main_message)
107 + await session.commit() # Make sure to commit the session
108 +
109 + return JSONResponse(
110 + status_code=422,
111 + content=ValidationErrorResponse(message=main_message, details=details).dict(),
112 + )
backend/app/middleware/logger.py new
+101
@@ -0,0 +1,101 @@
1 +# from fastapi import HTTPException
2 +# from fastapi import Request
3 +# from fastapi.responses import JSONResponse
4 +# from sqlmodel import Session
5 +
6 +# from app.auth.utils import AuthHandler
7 +# from app.db.db_session import engine
8 +# from app.utils import Logger
9 +
10 +# EXCLUDED_PATHS = ["/auth/token", "/auth/register"]
11 +# INTERNAL_SERVER_ERROR = 500
12 +
13 +
14 +# async def process_request(request: Request, call_next, session, logger_instance):
15 +# response = await call_next(request)
16 +# user_id = await logger_instance.get_user_id_from_request(request)
17 +# return response, user_id
18 +
19 +
20 +# def is_excluded_path(path: str) -> bool:
21 +# return path in EXCLUDED_PATHS
22 +
23 +
24 +# async def handle_exception(e, user_id, request, logger_instance):
25 +# user_id = await logger_instance.get_user_id_from_request(request) if user_id is None else user_id
26 +# await logger_instance.log_error(user_id, request, e)
27 +# status_code = e.status_code if isinstance(e, HTTPException) else INTERNAL_SERVER_ERROR
28 +# return JSONResponse(status_code=status_code, content={"message": str(e), "success": False})
29 +
30 +
31 +# async def log_requests(request: Request, call_next):
32 +# if request.method == "OPTIONS":
33 +# return await call_next(request)
34 +
35 +# with Session(engine) as session:
36 +# logger_instance = Logger(session, AuthHandler())
37 +# user_id = None
38 +
39 +# try:
40 +# if not is_excluded_path(request.url.path):
41 +# response, user_id = await process_request(request, call_next, session, logger_instance)
42 +# else:
43 +# response = await call_next(request)
44 +# except Exception as e:
45 +# return await handle_exception(e, user_id, request, logger_instance)
46 +
47 +# await logger_instance.log_route_access(user_id, request, response)
48 +
49 +# return response if response else await call_next(request)
50 +# ! Above is old without Async
51 +
52 +# ! New with Async
53 +from fastapi import HTTPException
54 +from fastapi import Request
55 +from fastapi.responses import JSONResponse
56 +from sqlalchemy.ext.asyncio import AsyncSession
57 +
58 +from app.auth.utils import AuthHandler
59 +from app.db.db_session import async_engine
60 +from app.utils import Logger
61 +
62 +EXCLUDED_PATHS = ["/auth/token", "/auth/register"]
63 +INTERNAL_SERVER_ERROR = 500
64 +
65 +
66 +async def process_request(request: Request, call_next, session, logger_instance):
67 + response = await call_next(request)
68 + user_id = await logger_instance.get_user_id_from_request(request)
69 + return response, user_id
70 +
71 +
72 +def is_excluded_path(path: str) -> bool:
73 + return path in EXCLUDED_PATHS
74 +
75 +
76 +async def handle_exception(e, user_id, request, logger_instance):
77 + user_id = await logger_instance.get_user_id_from_request(request) if user_id is None else user_id
78 + await logger_instance.log_error(user_id, request, e)
79 + status_code = e.status_code if isinstance(e, HTTPException) else INTERNAL_SERVER_ERROR
80 + return JSONResponse(status_code=status_code, content={"message": str(e), "success": False})
81 +
82 +
83 +async def log_requests(request: Request, call_next):
84 + if request.method == "OPTIONS":
85 + return await call_next(request)
86 +
87 + async with AsyncSession(async_engine) as session:
88 + logger_instance = Logger(session, AuthHandler())
89 + user_id = None
90 +
91 + try:
92 + if not is_excluded_path(request.url.path):
93 + response, user_id = await process_request(request, call_next, session, logger_instance)
94 + else:
95 + response = await call_next(request)
96 + except Exception as e:
97 + return await handle_exception(e, user_id, request, logger_instance)
98 +
99 + await logger_instance.log_route_access(user_id, request, response)
100 +
101 + return response
backend/app/routers/__init__.py new
+16
@@ -0,0 +1,16 @@
1 +from .agents import router as agents_router
2 +from .auth import router as auth_router
3 +from .connectors import router as connectors_router
4 +from .cortex import router as cortex_router
5 +from .customers import router as customers_router
6 +from .dfir_iris import router as dfir_iris_router
7 +from .dnstwist import router as dnstwist_router
8 +from .graylog import router as graylog_router
9 +from .healthcheck import router as healtcheck_router
10 +from .logs import router as logs_router
11 +from .shuffle import router as shuffle_router
12 +from .smtp import router as smtp_router
13 +from .sublime import router as sublime_router
14 +from .velociraptor import router as velociraptor_router
15 +from .wazuh_indexer import router as wazuh_indexer_router
16 +from .wazuh_manager import router as wazuh_manager_router
backend/app/routers/agents.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.agents.routes.agents import agents_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Wazuh Manager related routes
9 +router.include_router(agents_router, prefix="/agents", tags=["agents"])
backend/app/routers/auth.py new
+8
@@ -0,0 +1,8 @@
1 +from fastapi import APIRouter
2 +
3 +from app.auth.routes.auth import auth_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +router.include_router(auth_router, prefix="/auth", tags=["auth"])
backend/app/routers/connectors.py new
+7
@@ -0,0 +1,7 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.routes import connector_router
4 +
5 +router = APIRouter()
6 +
7 +router.include_router(connector_router, prefix="/connectors", tags=["connectors"])
backend/app/routers/cortex.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.cortex.routes.analyzers import cortex_analyzer_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Cortex related routes
9 +router.include_router(cortex_analyzer_router, prefix="/analyzers", tags=["cortex-analyzers"])
backend/app/routers/customers.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.customers.routes.customers import customers_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Customers related routes
9 +router.include_router(customers_router, prefix="/customers", tags=["customers"])
backend/app/routers/dfir_iris.py new
+21
@@ -0,0 +1,21 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.dfir_iris.routes.alerts import dfir_iris_alerts_router
4 +from app.connectors.dfir_iris.routes.assets import dfir_iris_assets_router
5 +from app.connectors.dfir_iris.routes.cases import dfir_iris_cases_router
6 +from app.connectors.dfir_iris.routes.notes import dfir_iris_notes_router
7 +from app.connectors.dfir_iris.routes.users import dfir_iris_users_router
8 +from app.integrations.alert_escalation.routes.general_alert import (
9 + integration_general_alerts_router,
10 +)
11 +
12 +# Instantiate the APIRouter
13 +router = APIRouter()
14 +
15 +# Include the DFIR Iris related routes
16 +router.include_router(dfir_iris_alerts_router, prefix="/soc/alerts", tags=["soc-alerts"])
17 +router.include_router(dfir_iris_assets_router, prefix="/soc/assets", tags=["soc-assets"])
18 +router.include_router(dfir_iris_cases_router, prefix="/soc/cases", tags=["soc-cases"])
19 +router.include_router(dfir_iris_notes_router, prefix="/soc/notes", tags=["soc-notes"])
20 +router.include_router(dfir_iris_users_router, prefix="/soc/users", tags=["soc-users"])
21 +router.include_router(integration_general_alerts_router, prefix="/soc/general_alert", tags=["soc-general-alerts"])
backend/app/routers/dnstwist.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.integrations.dnstwist.routes.analyze import dnstwist_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the DNS Twist related routes
9 +router.include_router(dnstwist_router, prefix="/dnstwist", tags=["dnstwist"])
backend/app/routers/graylog.py new
+17
@@ -0,0 +1,17 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.graylog.routes.collector import graylog_collector_router
4 +from app.connectors.graylog.routes.events import graylog_events_router
5 +from app.connectors.graylog.routes.management import graylog_management_router
6 +from app.connectors.graylog.routes.monitoring import graylog_monitoring_router
7 +from app.connectors.graylog.routes.pipelines import graylog_pipelines_router
8 +from app.connectors.graylog.routes.streams import graylog_streams_router
9 +
10 +router = APIRouter()
11 +
12 +router.include_router(graylog_collector_router, prefix="/graylog", tags=["graylog"])
13 +router.include_router(graylog_events_router, prefix="/graylog", tags=["graylog"])
14 +router.include_router(graylog_management_router, prefix="/graylog", tags=["graylog"])
15 +router.include_router(graylog_monitoring_router, prefix="/graylog", tags=["graylog"])
16 +router.include_router(graylog_streams_router, prefix="/graylog", tags=["graylog"])
17 +router.include_router(graylog_pipelines_router, prefix="/graylog", tags=["graylog"])
backend/app/routers/healthcheck.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.healthchecks.agents.routes.agents import healtcheck_agents_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Healthcheck related routes
9 +router.include_router(healtcheck_agents_router, prefix="/healthcheck", tags=["healthcheck"])
backend/app/routers/logs.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.utils import logs_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Logs related routes
9 +router.include_router(logs_router, prefix="/logs", tags=["logs"])
backend/app/routers/shuffle.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.shuffle.routes.workflows import shuffle_workflows_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Shuffle related routes
9 +router.include_router(shuffle_workflows_router, prefix="/workflows", tags=["shuffle-workflows"])
backend/app/routers/smtp.py new
+11
@@ -0,0 +1,11 @@
1 +from fastapi import APIRouter
2 +
3 +from app.smtp.routes.configure import smtp_configure_router
4 +from app.smtp.routes.reports import smtp_reports_router
5 +
6 +# Instantiate the APIRouter
7 +router = APIRouter()
8 +
9 +# Include the SMTP related routes
10 +router.include_router(smtp_configure_router, prefix="/smtp", tags=["smtp"])
11 +router.include_router(smtp_reports_router, prefix="/smtp", tags=["smtp"])
backend/app/routers/sublime.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.sublime.routes.alerts import sublime_alerts_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Sublime related routes
9 +router.include_router(sublime_alerts_router, prefix="/sublime", tags=["sublime-alerts"])
backend/app/routers/velociraptor.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.velociraptor.routes.artifacts import velociraptor_artifacts_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Velociraptor related routes
9 +router.include_router(velociraptor_artifacts_router, prefix="/artifacts", tags=["velociraptor-artifacts"])
backend/app/routers/wazuh_indexer.py new
+11
@@ -0,0 +1,11 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.wazuh_indexer.routes.alerts import wazuh_indexer_alerts_router
4 +from app.connectors.wazuh_indexer.routes.monitoring import wazuh_indexer_router
5 +
6 +# Instantiate the APIRouter
7 +router = APIRouter()
8 +
9 +# Include the Wazuh Indexer related routes
10 +router.include_router(wazuh_indexer_alerts_router, prefix="/alerts", tags=["wazuh-indexer-alerts"])
11 +router.include_router(wazuh_indexer_router, prefix="/wazuh_indexer", tags=["wazuh-indexer-monitoring"])
backend/app/routers/wazuh_manager.py new
+9
@@ -0,0 +1,9 @@
1 +from fastapi import APIRouter
2 +
3 +from app.connectors.wazuh_manager.routes.rules import wazuh_manager_rules_router
4 +
5 +# Instantiate the APIRouter
6 +router = APIRouter()
7 +
8 +# Include the Wazuh Manager related routes
9 +router.include_router(wazuh_manager_rules_router, prefix="/wazuh_manager", tags=["wazuh-manager"])
backend/app/schedulers/models/scheduler.py new
+14
@@ -0,0 +1,14 @@
1 +from datetime import datetime
2 +from typing import Optional
3 +
4 +from sqlmodel import Field
5 +from sqlmodel import SQLModel
6 +
7 +
8 +class JobMetadata(SQLModel, table=True):
9 + __tablename__ = "scheduled_job_metadata"
10 + id: Optional[int] = Field(default=None, primary_key=True)
11 + job_id: str = Field(index=True) # Corresponds to the APScheduler job ID
12 + last_success: Optional[datetime] = None
13 + time_interval: int # The frequency of the job in minutes
14 + enabled: bool # Indicates if the job is active or not
backend/app/schedulers/scheduler.py new
+45
@@ -0,0 +1,45 @@
1 +# app/schedulers/scheduler.py
2 +
3 +from datetime import datetime
4 +
5 +import requests
6 +from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
7 +from apscheduler.schedulers.asyncio import AsyncIOScheduler
8 +from apscheduler.triggers.interval import IntervalTrigger
9 +
10 +from app.db.db_session import session
11 +from app.schedulers.models.scheduler import JobMetadata
12 +from settings import SQLALCHEMY_DATABASE_URI
13 +
14 +
15 +def scheduled_task():
16 + # Your actual task
17 + response = requests.get("http://127.0.0.1:5000/agents/sync")
18 + print(response.json())
19 +
20 + # Update the last_success in the metadata table
21 + job_metadata = session.get(JobMetadata, "scheduled_task")
22 + if job_metadata:
23 + job_metadata.last_success = datetime.utcnow()
24 + session.add(job_metadata)
25 + session.commit()
26 +
27 +
28 +def init_scheduler():
29 + scheduler = AsyncIOScheduler()
30 + jobstores = {"default": SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URI)}
31 + scheduler.configure(jobstores=jobstores)
32 + job = scheduler.add_job(scheduled_task, "interval", minutes=1, id="scheduled_task", replace_existing=True)
33 +
34 + # Initialize or update the metadata in the database
35 + job_metadata = session.get(JobMetadata, job.id)
36 + if not job_metadata:
37 + job_metadata = JobMetadata(job_id=job.id, last_success=None, time_interval=1, enabled=True)
38 + session.add(job_metadata)
39 + else:
40 + # Update existing metadata if needed
41 + job_metadata.time_interval = 1 # Update interval if it's changed
42 + job_metadata.enabled = True # Make sure the job is enabled
43 + session.commit()
44 +
45 + return scheduler
backend/app/smtp/routes/configure.py
+5 -5
@@ -9,11 +9,11 @@ from app.auth.utils import AuthHandler
9 from app.db.db_session import session
10 from app.smtp.schema.configure import SMTPResponse
11
12 -smtp_router = APIRouter()
12 +smtp_configure_router = APIRouter()
13 auth_handler = AuthHandler()
14
15
16 -@smtp_router.post("/{user_id}/register", response_model=SMTPResponse, status_code=200, description="Register new SMTP for user")
16 +@smtp_configure_router.post("/{user_id}/register", response_model=SMTPResponse, status_code=200, description="Register new SMTP for user")
17 async def register(user_id: int, smtp: SMTPInput):
18 users = select_all_users()
19 logger.info(users)
@@ -30,7 +30,7 @@ async def register(user_id: int, smtp: SMTPInput):
30 return {"message": "SMTP created successfully", "success": True}
31
32
33 -@smtp_router.get("/{user_id}", response_model=SMTP, status_code=200, description="Get SMTP for user")
33 +@smtp_configure_router.get("/{user_id}", response_model=SMTP, status_code=200, description="Get SMTP for user")
34 async def get_smtp(user_id: int):
35 users = select_all_users()
36 if not any(x.id == user_id for x in users):
@@ -41,7 +41,7 @@ async def get_smtp(user_id: int):
41 return smtp_found
42
43
44 -@smtp_router.put("/{user_id}", response_model=SMTPResponse, status_code=200, description="Update SMTP for user")
44 +@smtp_configure_router.put("/{user_id}", response_model=SMTPResponse, status_code=200, description="Update SMTP for user")
45 async def update_smtp(user_id: int, smtp: SMTPInput):
46 users = select_all_users()
47 if not any(x.id == user_id for x in users):
@@ -57,7 +57,7 @@ async def update_smtp(user_id: int, smtp: SMTPInput):
57 return {"message": "SMTP updated successfully", "success": True}
58
59
60 -@smtp_router.delete("/{user_id}", response_model=SMTPResponse, status_code=200, description="Delete SMTP for user")
60 +@smtp_configure_router.delete("/{user_id}", response_model=SMTPResponse, status_code=200, description="Delete SMTP for user")
61 async def delete_smtp(user_id: int):
62 users = select_all_users()
63 if not any(x.id == user_id for x in users):
backend/app/utils.py
+96 -89
@@ -6,6 +6,7 @@ from typing import Optional
6 from typing import Union
7
8 from fastapi import APIRouter
9 +from fastapi import Depends
10 from fastapi import HTTPException
11 from fastapi import Request
12 from fastapi import Security
@@ -14,11 +15,14 @@ from loguru import logger
15 from pydantic import BaseModel
16 from pydantic import Field
17 from pydantic import validator
18 +from sqlalchemy.ext.asyncio import AsyncSession
19 +from sqlalchemy.future import select
20
21 from app.auth.services.universal import find_user
22 from app.auth.utils import AuthHandler
23 from app.db.db_session import Session
24 from app.db.db_session import engine
25 +from app.db.db_session import get_session
26 from app.db.universal_models import LogEntry
27
28
@@ -172,24 +176,36 @@ class TimeRangeModel(BaseModel):
176
177 #########! LOGGER CLASS !#########
178 class Logger:
175 - def __init__(self, session, auth_handler: AuthHandler):
179 + # def __init__(self, session, auth_handler: AuthHandler):
180 + # self.session = session
181 + # self.auth_handler = auth_handler
182 + def __init__(self, session: AsyncSession, auth_handler: AuthHandler):
183 self.session = session
184 self.auth_handler = auth_handler
185
186 + # async def get_user_id_from_request(self, request: Request):
187 + # auth_header = request.headers.get("Authorization")
188 + # if auth_header:
189 + # token = auth_header.replace("Bearer ", "")
190 + # username, _ = self.auth_handler.decode_token(token)
191 + # user = find_user(username)
192 + # if user:
193 + # return user.id
194 + # return None
195 async def get_user_id_from_request(self, request: Request):
196 auth_header = request.headers.get("Authorization")
197 if auth_header:
182 - token = auth_header.replace("Bearer ", "")
198 + token = auth_header.split(" ")[1] # Better split by space and take the second part
199 username, _ = self.auth_handler.decode_token(token)
184 - user = find_user(username)
200 + user = await find_user(username) # Correctly using await for an async call
201 if user:
202 return user.id
203 return None
204
189 - def insert_log_entry(self, log_entry_model: LogEntryModel):
205 + async def insert_log_entry(self, log_entry_model: LogEntryModel):
206 log_entry = LogEntry(**log_entry_model.dict())
207 self.session.add(log_entry)
192 - self.session.commit()
208 + await self.session.commit()
209
210 async def log_route_access(self, user_id, request: Request, response):
211 log_entry_model = LogEntryModel(
@@ -200,7 +216,7 @@ class Logger:
216 status_code=response.status_code,
217 message="Route accessed",
218 )
203 - self.insert_log_entry(log_entry_model)
219 + await self.insert_log_entry(log_entry_model)
220
221 async def log_error(self, user_id, request: Request, exception: Exception, additional_info: Optional[str] = None):
222 log_entry_model = LogEntryModel(
@@ -212,14 +228,16 @@ class Logger:
228 message=str(exception),
229 additional_info=additional_info,
230 )
215 - self.insert_log_entry(log_entry_model)
231 + await self.insert_log_entry(log_entry_model)
232
233 async def log_and_raise_http_error(self, user_id, request: Request, exception: Exception):
234 await self.log_error(user_id, request, exception)
235 raise HTTPException(status_code=500, detail="Internal Server Error")
236
221 - def fetch_all_logs(self):
222 - logs = self.session.query(LogEntry).all() # Replace LogEntry with your actual LogEntry model
237 + async def fetch_all_logs(self):
238 + # Perform an asynchronous query to fetch all log entries
239 + result = await self.session.execute(select(LogEntry))
240 + logs = result.scalars().all()
241 return logs
242
243
@@ -233,7 +251,7 @@ logs_router = APIRouter()
251 description="Fetch all logs",
252 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
253 )
236 -async def get_logs() -> LogsResponse: # Update this line to use the new model
254 +async def get_logs(session: AsyncSession = Depends(get_session)) -> LogsResponse:
255 """
256 Fetch all logs from the database.
257
@@ -246,15 +264,14 @@ async def get_logs() -> LogsResponse: # Update this line to use the new model
264 Raises:
265 HTTPException: An exception with a 404 status code is raised if no logs are found.
266 """
249 - with Session(engine) as session:
250 - auth_handler_instance = AuthHandler() # Replace with your actual AuthHandler initialization
251 - logger_instance = Logger(session, auth_handler_instance)
267 + auth_handler_instance = AuthHandler() # Initialize your AuthHandler
268 + logger_instance = Logger(session, auth_handler_instance)
269
253 - logs = logger_instance.fetch_all_logs()
254 - if logs:
255 - return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
256 - else:
257 - raise HTTPException(status_code=404, detail="No logs found")
270 + logs = await logger_instance.fetch_all_logs() # Assuming fetch_all_logs is an async function
271 + if logs:
272 + return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
273 + else:
274 + raise HTTPException(status_code=404, detail="No logs found")
275
276
277 @logs_router.get(
@@ -263,7 +280,7 @@ async def get_logs() -> LogsResponse: # Update this line to use the new model
280 description="Fetch logs by user ID",
281 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
282 )
266 -async def get_logs_by_user_id(user_id: int) -> LogsResponse: # Update this line to use the new model
283 +async def get_logs_by_user_id(user_id: int, session: AsyncSession = Depends(get_session)) -> LogsResponse:
284 """
285 Fetch all logs from the database where the user_id matches the provided user_id.
286
@@ -279,18 +296,13 @@ async def get_logs_by_user_id(user_id: int) -> LogsResponse: # Update this line
296 Raises:
297 HTTPException: An exception with a 404 status code is raised if no logs are found.
298 """
282 - with Session(engine) as session:
283 - auth_handler_instance = AuthHandler()
284 - logger_instance = Logger(session, auth_handler_instance)
285 - logs = logger_instance.fetch_all_logs()
286 - if logs:
287 - logs = [log for log in logs if log.user_id == user_id]
288 - if logs != []:
289 - return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
290 - else:
291 - raise HTTPException(status_code=404, detail=f"No logs found for user ID: {user_id}".format(user_id=user_id))
292 - else:
293 - raise HTTPException(status_code=404, detail="No logs found")
299 + result = await session.execute(select(LogEntry).filter(LogEntry.user_id == user_id))
300 + logs = result.scalars().all()
301 +
302 + if not logs:
303 + raise HTTPException(status_code=404, detail=f"No logs found for user ID: {user_id}")
304 +
305 + return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
306
307
308 @logs_router.post(
@@ -299,7 +311,7 @@ async def get_logs_by_user_id(user_id: int) -> LogsResponse: # Update this line
311 description="Fetch logs by time range",
312 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
313 )
302 -async def get_logs_by_time_range(time_range: TimeRangeModel) -> LogsResponse:
314 +async def get_logs_by_time_range(time_range: TimeRangeModel, session: AsyncSession = Depends(get_session)) -> LogsResponse:
315 """
316 Fetch all logs from the database where the timestamp is within the provided time range.
317
@@ -315,21 +327,20 @@ async def get_logs_by_time_range(time_range: TimeRangeModel) -> LogsResponse:
327 Raises:
328 HTTPException: An exception with a 404 status code is raised if no logs are found.
329 """
318 - with Session(engine) as session:
319 - auth_handler_instance = AuthHandler()
320 - logger_instance = Logger(session, auth_handler_instance)
321 - logs = logger_instance.fetch_all_logs()
322 - if logs:
323 - logs = [log for log in logs if log.timestamp >= datetime.now() - timedelta(days=int(time_range.time_range[:-1]))]
324 - if logs != []:
325 - return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
326 - else:
327 - raise HTTPException(
328 - status_code=404,
329 - detail=f"No logs found for time range: {time_range.time_range}".format(time_range=time_range.time_range),
330 - )
330 + result = await session.execute(select(LogEntry))
331 + logs = result.scalars().all()
332 +
333 + if logs:
334 + logs = [log for log in logs if log.timestamp >= datetime.now() - timedelta(days=int(time_range.time_range[:-1]))]
335 + if logs != []:
336 + return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
337 else:
332 - raise HTTPException(status_code=404, detail="No logs found")
338 + raise HTTPException(
339 + status_code=404,
340 + detail=f"No logs found for time range: {time_range.time_range}".format(time_range=time_range.time_range),
341 + )
342 + else:
343 + raise HTTPException(status_code=404, detail="No logs found")
344
345
346 @logs_router.post(
@@ -338,7 +349,10 @@ async def get_logs_by_time_range(time_range: TimeRangeModel) -> LogsResponse:
349 description="Fetch logs by event type",
350 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
351 )
341 -async def get_logs_by_event_type(event_type: EventType) -> LogsResponse: # Update this line to use the new model
352 +async def get_logs_by_event_type(
353 + event_type: EventType,
354 + session: AsyncSession = Depends(get_session),
355 +) -> LogsResponse: # Update this line to use the new model
356 """
357 Fetch all logs from the database where the event_type matches the provided event_type.
358
@@ -354,18 +368,13 @@ async def get_logs_by_event_type(event_type: EventType) -> LogsResponse: # Upda
368 Raises:
369 HTTPException: An exception with a 404 status code is raised if no logs are found.
370 """
357 - with Session(engine) as session:
358 - auth_handler_instance = AuthHandler()
359 - logger_instance = Logger(session, auth_handler_instance)
360 - logs = logger_instance.fetch_all_logs()
361 - if logs:
362 - logs = [log for log in logs if log.event_type == event_type]
363 - if logs != []:
364 - return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
365 - else:
366 - raise HTTPException(status_code=404, detail=f"No logs found for event type: {event_type}".format(event_type=event_type))
367 - else:
368 - raise HTTPException(status_code=404, detail="No logs found")
371 + result = await session.execute(select(LogEntry).filter(LogEntry.event_type == event_type))
372 + logs = result.scalars().all()
373 +
374 + if not logs:
375 + raise HTTPException(status_code=404, detail=f"No logs found for event type: {event_type}")
376 +
377 + return LogsResponse(logs=logs, success=True, message="Logs fetched successfully")
378
379
380 @logs_router.delete(
@@ -374,7 +383,7 @@ async def get_logs_by_event_type(event_type: EventType) -> LogsResponse: # Upda
383 description="Purge all logs",
384 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
385 )
377 -async def purge_logs() -> LogsResponse: # Update this line to use the new model
386 +async def purge_logs(session: AsyncSession = Depends(get_session)) -> LogsResponse: # Update this line to use the new model
387 """
388 Purge all logs from the database.
389
@@ -386,17 +395,16 @@ async def purge_logs() -> LogsResponse: # Update this line to use the new model
395 Raises:
396 HTTPException: An exception with a 404 status code is raised if no logs are found.
397 """
389 - with Session(engine) as session:
390 - auth_handler_instance = AuthHandler()
391 - logger_instance = Logger(session, auth_handler_instance)
392 - logs = logger_instance.fetch_all_logs()
393 - if logs:
394 - for log in logs:
395 - session.delete(log)
396 - session.commit()
397 - return LogsResponse(logs=[], success=True, message="Logs purged successfully")
398 - else:
399 - raise HTTPException(status_code=404, detail="No logs found")
398 + result = await session.execute(select(LogEntry))
399 + logs = result.scalars().all()
400 +
401 + if logs:
402 + for log in logs:
403 + session.delete(log)
404 + await session.commit()
405 + return LogsResponse(logs=[], success=True, message="Logs purged successfully")
406 + else:
407 + raise HTTPException(status_code=404, detail="No logs found")
408
409
410 @logs_router.delete(
@@ -405,7 +413,7 @@ async def purge_logs() -> LogsResponse: # Update this line to use the new model
413 description="Purge logs by time range",
414 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
415 )
408 -async def purge_logs_by_time_range(time_range: TimeRangeModel) -> LogsResponse:
416 +async def purge_logs_by_time_range(time_range: TimeRangeModel, session: AsyncSession = Depends(get_session)) -> LogsResponse:
417 """
418 Purge all logs from the database where the timestamp is within the provided time range.
419
@@ -421,24 +429,23 @@ async def purge_logs_by_time_range(time_range: TimeRangeModel) -> LogsResponse:
429 Raises:
430 HTTPException: An exception with a 404 status code is raised if no logs are found.
431 """
424 - with Session(engine) as session:
425 - auth_handler_instance = AuthHandler()
426 - logger_instance = Logger(session, auth_handler_instance)
427 - logs = logger_instance.fetch_all_logs()
428 - if logs:
429 - logs = [log for log in logs if log.timestamp >= datetime.now() - timedelta(days=int(time_range.time_range[:-1]))]
430 - if logs != []:
431 - for log in logs:
432 - session.delete(log)
433 - session.commit()
434 - return LogsResponse(logs=[], success=True, message="Logs purged successfully")
435 - else:
436 - raise HTTPException(
437 - status_code=404,
438 - detail=f"No logs found for time range: {time_range.time_range}".format(time_range=time_range.time_range),
439 - )
432 + result = await session.execute(select(LogEntry))
433 + logs = result.scalars().all()
434 +
435 + if logs:
436 + logs = [log for log in logs if log.timestamp >= datetime.now() - timedelta(days=int(time_range.time_range[:-1]))]
437 + if logs != []:
438 + for log in logs:
439 + session.delete(log)
440 + await session.commit()
441 + return LogsResponse(logs=[], success=True, message="Logs purged successfully")
442 else:
441 - raise HTTPException(status_code=404, detail="No logs found")
443 + raise HTTPException(
444 + status_code=404,
445 + detail=f"No logs found for time range: {time_range.time_range}".format(time_range=time_range.time_range),
446 + )
447 + else:
448 + raise HTTPException(status_code=404, detail="No logs found")
449
450
451 ################## ! ALLOWED FILES ! ##################
backend/copilot.py
+62 -163
@@ -1,59 +1,46 @@
1 +import requests
2 import uvicorn
3 +from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
4 +from apscheduler.schedulers.asyncio import AsyncIOScheduler
5 +from apscheduler.triggers.interval import IntervalTrigger
6 from dotenv import load_dotenv
7 from fastapi import FastAPI
8 from fastapi import HTTPException
5 -from fastapi import Request
9 from fastapi.exceptions import RequestValidationError
10 from fastapi.middleware.cors import CORSMiddleware
8 -from fastapi.responses import JSONResponse
11 from loguru import logger
10 -from pydantic import BaseSettings
11 -from sqlmodel import Session
12
13 -from app.agents.routes.agents import agents_router
14 -from app.auth.routes.auth import user_router
13 from app.auth.utils import AuthHandler
16 -from app.connectors.cortex.routes.analyzers import cortex_analyzer_router
17 -from app.connectors.dfir_iris.routes.alerts import dfir_iris_alerts_router
18 -from app.connectors.dfir_iris.routes.assets import assets_router
19 -from app.connectors.dfir_iris.routes.cases import cases_router
20 -from app.connectors.dfir_iris.routes.notes import notes_router
21 -from app.connectors.dfir_iris.routes.users import dfir_iris_users_router
22 -from app.connectors.graylog.routes.collector import graylog_collector_router
23 -from app.connectors.graylog.routes.events import graylog_events_router
24 -from app.connectors.graylog.routes.management import graylog_management_router
25 -from app.connectors.graylog.routes.monitoring import graylog_monitoring_router
26 -from app.connectors.graylog.routes.pipelines import graylog_pipelines_router
27 -from app.connectors.graylog.routes.streams import graylog_streams_router
28 -from app.connectors.routes import connector_router
29 -from app.connectors.shuffle.routes.workflows import shuffle_workflows_router
30 -from app.connectors.sublime.routes.alerts import sublime_alerts_router
31 -from app.connectors.velociraptor.routes.artifacts import velociraptor_artifacts_router
32 -from app.connectors.wazuh_indexer.routes.alerts import wazuh_indexer_alerts_router
33 -
34 -# from app.connectors.wazuh_indexer.routes.routes import wazuh_indexer_router
35 -from app.connectors.wazuh_indexer.routes.monitoring import wazuh_indexer_router
36 -from app.connectors.wazuh_manager.routes.rules import wazuh_manager_router
37 -from app.customers.routes.customers import customers_router
14 +from app.db.db_session import async_engine
15 from app.db.db_session import engine
16 from app.db.db_setup import create_tables
40 -from app.healthchecks.agents.routes.agents import healtcheck_agents_router
41 -from app.integrations.alert_escalation.routes.general_alert import (
42 - integration_general_alerts_router,
43 -)
44 -from app.integrations.dnstwist.routes.analyze import dnstwist_router
45 -from app.smtp.routes.configure import smtp_router
46 -from app.utils import ErrorType
47 -from app.utils import Logger
48 -from app.utils import ValidationErrorItem
49 -from app.utils import ValidationErrorResponse
50 -from app.utils import logs_router
17 +from app.middleware.exception_handlers import custom_http_exception_handler
18 +from app.middleware.exception_handlers import validation_exception_handler
19 +from app.middleware.logger import log_requests
20 +from app.routers import agents
21 +from app.routers import auth
22 +from app.routers import connectors
23 +from app.routers import cortex
24 +from app.routers import customers
25 +from app.routers import dfir_iris
26 +from app.routers import dnstwist
27 +from app.routers import graylog
28 +from app.routers import healthcheck
29 +from app.routers import logs
30 +from app.routers import shuffle
31 +from app.routers import smtp
32 +from app.routers import sublime
33 +from app.routers import velociraptor
34 +from app.routers import wazuh_indexer
35 +from app.routers import wazuh_manager
36 +from app.schedulers.scheduler import init_scheduler
37
38 auth_handler = AuthHandler()
39
40
41 app = FastAPI(description="CoPilot API", version="0.1.0", title="CoPilot API")
42
43 +
44 # Allow all origins, methods and headers
45 app.add_middleware(
46 CORSMiddleware,
@@ -65,140 +52,43 @@ app.add_middleware(
52
53
54 ################## ! Middleware LOGGING TO `log_entry` table ! ##################
68 -# Constants
69 -EXCLUDED_PATHS = ["/auth/token", "/auth/register"]
70 -INTERNAL_SERVER_ERROR = 500
71 -
72 -
73 -async def process_request(request: Request, call_next, session, logger_instance):
74 - response = await call_next(request)
75 - user_id = await logger_instance.get_user_id_from_request(request)
76 - return response, user_id
77 -
78 -
79 -def is_excluded_path(path: str) -> bool:
80 - """Check if the request path is in the list of excluded paths."""
81 - return path in EXCLUDED_PATHS
82 -
83 -
84 -async def handle_exception(e, user_id, request, logger_instance):
85 - """
86 - Handle exceptions that occur during request processing.
87 - """
88 - user_id = await logger_instance.get_user_id_from_request(request) if user_id is None else user_id
89 - await logger_instance.log_error(user_id, request, e)
90 - if isinstance(e, HTTPException):
91 - status_code = e.status_code
92 - else:
93 - status_code = INTERNAL_SERVER_ERROR
94 - return JSONResponse(status_code=status_code, content={"message": str(e), "success": False})
95 -
96 -
97 -@app.middleware("http")
98 -async def log_requests(request: Request, call_next):
99 - """
100 - Middleware for logging requests.
101 - """
102 - # Skip logging for OPTIONS requests
103 - if request.method == "OPTIONS":
104 - return await call_next(request)
105 -
106 - with Session(engine) as session:
107 - logger_instance = Logger(session, auth_handler)
108 - user_id = None
109 -
110 - try:
111 - if not is_excluded_path(request.url.path):
112 - response, user_id = await process_request(request, call_next, session, logger_instance)
113 - else:
114 - response = await call_next(request)
115 - except Exception as e:
116 - return await handle_exception(e, user_id, request, logger_instance)
117 -
118 - await logger_instance.log_route_access(user_id, request, response)
119 -
120 - return response if response else await call_next(request)
55 +app.middleware("http")(log_requests) # using the imported middleware
56
57
58 ################## ! Exception Handlers ! ##################
124 -# Utility function to get user_id from request
125 -async def get_user_id_from_request(request: Request, session, logger_instance):
126 - return await logger_instance.get_user_id_from_request(request)
127 -
128 -
129 -@app.exception_handler(HTTPException)
130 -async def custom_http_exception_handler(request: Request, exc: HTTPException):
131 - with Session(engine) as session:
132 - logger_instance = Logger(session, auth_handler)
133 - user_id = await get_user_id_from_request(request, session, logger_instance)
134 - await logger_instance.log_error(user_id, request, exc.detail)
135 -
136 - return JSONResponse(
137 - status_code=exc.status_code,
138 - content={
139 - "success": False,
140 - "message": exc.detail,
141 - },
142 - )
143 -
144 -
145 -@app.exception_handler(RequestValidationError)
146 -async def validation_exception_handler(request: Request, exc: RequestValidationError):
147 - errors = exc.errors()
148 - details = []
149 -
150 - for error in errors:
151 - field = error["loc"][-1]
152 - error_type = ErrorType(error["type"])
153 - details.append(ValidationErrorItem(field=field, error_type=error_type))
154 -
155 - # Extract the first message from details for use in ValidationErrorResponse
156 - main_message = details[0].message if details else "Validation Error"
157 -
158 - with Session(engine) as session:
159 - logger_instance = Logger(session, auth_handler)
160 - user_id = await get_user_id_from_request(request, session, logger_instance)
161 - await logger_instance.log_error(user_id, request, main_message)
162 -
163 - return JSONResponse(
164 - status_code=422,
165 - content=ValidationErrorResponse(message=main_message, details=details).dict(),
166 - )
59 +app.add_exception_handler(HTTPException, custom_http_exception_handler)
60 +app.add_exception_handler(RequestValidationError, validation_exception_handler)
61
62
63 ################## ! INCLUDE ROUTES ! ##################
170 -app.include_router(connector_router, prefix="/connectors", tags=["connectors"])
171 -app.include_router(wazuh_indexer_router, prefix="/wazuh_indexer", tags=["wazuh-indexer"])
172 -app.include_router(user_router, prefix="/auth", tags=["auth"])
173 -app.include_router(wazuh_manager_router, prefix="/wazuh_manager", tags=["wazuh-manager"])
174 -app.include_router(agents_router, prefix="/agents", tags=["agents"])
175 -app.include_router(graylog_monitoring_router, prefix="/graylog", tags=["graylog"])
176 -app.include_router(graylog_collector_router, prefix="/graylog", tags=["graylog"])
177 -app.include_router(graylog_events_router, prefix="/graylog", tags=["graylog"])
178 -app.include_router(graylog_pipelines_router, prefix="/graylog", tags=["graylog"])
179 -app.include_router(graylog_streams_router, prefix="/graylog", tags=["graylog"])
180 -app.include_router(graylog_management_router, prefix="/graylog", tags=["graylog"])
181 -app.include_router(wazuh_indexer_alerts_router, prefix="/alerts", tags=["alerts"])
182 -app.include_router(cases_router, prefix="/cases", tags=["cases"])
183 -app.include_router(notes_router, prefix="/notes", tags=["notes"])
184 -app.include_router(assets_router, prefix="/assets", tags=["assets"])
185 -app.include_router(dfir_iris_alerts_router, prefix="/alerts", tags=["soc-alerts"])
186 -app.include_router(dfir_iris_users_router, prefix="/users", tags=["dfir_iris-users"])
187 -app.include_router(cortex_analyzer_router, prefix="/analyzers", tags=["cortex-analyzers"])
188 -app.include_router(velociraptor_artifacts_router, prefix="/artifacts", tags=["velociraptor"])
189 -app.include_router(shuffle_workflows_router, prefix="/workflows", tags=["shuffle"])
190 -app.include_router(sublime_alerts_router, prefix="/sublime", tags=["sublime"])
191 -app.include_router(customers_router, prefix="/customers", tags=["customers"])
192 -app.include_router(healtcheck_agents_router, prefix="/healthcheck", tags=["healthcheck"])
193 -app.include_router(smtp_router, prefix="/smtp", tags=["smtp"])
194 -app.include_router(dnstwist_router, prefix="/dnstwist", tags=["dnstwist"])
195 -app.include_router(integration_general_alerts_router, prefix="/alerts", tags=["alerts"])
196 -app.include_router(logs_router, prefix="/logs", tags=["logs"])
64 +app.include_router(connectors.router)
65 +app.include_router(wazuh_indexer.router)
66 +app.include_router(auth.router)
67 +app.include_router(wazuh_manager.router)
68 +app.include_router(agents.router)
69 +app.include_router(graylog.router)
70 +app.include_router(dfir_iris.router)
71 +app.include_router(cortex.router)
72 +app.include_router(velociraptor.router)
73 +app.include_router(shuffle.router)
74 +app.include_router(sublime.router)
75 +app.include_router(customers.router)
76 +app.include_router(healthcheck.router)
77 +app.include_router(smtp.router)
78 +app.include_router(dnstwist.router)
79 +app.include_router(logs.router)
80
81
82 @app.on_event("startup")
83 async def init_db():
201 - create_tables(engine)
84 + # create_tables(engine)
85 + await create_tables(async_engine)
86 + # Initialize the scheduler
87 + # scheduler = init_scheduler()
88 +
89 + # logger.info("Starting scheduler")
90 + # if not scheduler.running:
91 + # scheduler.start()
92
93
94 @app.get("/")
@@ -206,5 +96,14 @@ def hello():
96 return {"message": "Hello World"}
97
98
99 +@app.on_event("shutdown")
100 +async def shutdown_scheduler():
101 + logger.info("Shutting down scheduler")
102 + # Initialize the scheduler
103 + scheduler = init_scheduler()
104 + if scheduler.running:
105 + scheduler.shutdown()
106 +
107 +
108 if __name__ == "__main__":
109 uvicorn.run(app, host="localhost", port=5000)
backend/requirements.in
+3
@@ -1,3 +1,6 @@
1 +apscheduler
2 +aiofiles
3 +aiosqlite
4 bcrypt
5 blueprint
6 cortex4py
backend/settings.py
+2 -1
@@ -18,7 +18,8 @@ db_path = str(basedir / "copilot.db")
18 ENV = env.str("SECRET_KEY", default="production")
19 DEBUG = env.bool("FLASK_DEBUG", default=False)
20 SECRET_KEY = env.str("SECRET_KEY", "not-a-secret")
21 -SQLALCHEMY_DATABASE_URI = env.str("SQLALCHEMY_DATABASE_URI", f"sqlite:///{db_path}")
21 +# SQLALCHEMY_DATABASE_URI = env.str("SQLALCHEMY_DATABASE_URI", f"sqlite:///{db_path}")
22 +SQLALCHEMY_DATABASE_URI = env.str("SQLALCHEMY_DATABASE_URI", f"sqlite+aiosqlite:///{db_path}")
23 SQLALCHEMY_TRACK_MODIFICATIONS = env.bool(
24 "SQLALCHEMY_TRACK_MODIFICATIONS",
25 default=False,
package-lock.json
+333 -272
@@ -52,7 +52,7 @@
52 "echarts": "^5.4.3",
53 "geojson": "^0.5.0",
54 "highlight.js": "^11.9.0",
55 - "jose": "^5.0.1",
55 + "jose": "^5.1.0",
56 "lodash": "^4.17.21",
57 "maplibre-gl": "^3.5.2",
58 "mitt": "^3.0.1",
@@ -65,12 +65,12 @@
65 "shepherd.js": "^11.2.0",
66 "v-calendar": "^3.1.2",
67 "validator": "^13.11.0",
68 - "vue": "^3.3.7",
68 + "vue": "^3.3.8",
69 "vue-advanced-cropper": "^2.8.8",
70 "vue-cal": "^4.8.1",
71 "vue-chartjs": "^5.2.0",
72 "vue-highlight-words": "^3.0.1",
73 - "vue-i18n": "^9.6.2",
73 + "vue-i18n": "^9.6.5",
74 "vue-maplibre-gl": "^3.0.3",
75 "vue-router": "^4.2.5",
76 "vue-sjv": "^0.0.6",
@@ -86,12 +86,12 @@
86 "@iconify/vue": "^4.1.1",
87 "@rushstack/eslint-patch": "^1.5.1",
88 "@tsconfig/node18": "^18.2.2",
89 - "@types/bytes": "^3.1.3",
89 + "@types/bytes": "^3.1.4",
90 "@types/fs-extra": "^11.0.3",
91 "@types/inquirer": "^9.0.6",
92 - "@types/jsdom": "^21.1.4",
92 + "@types/jsdom": "^21.1.5",
93 "@types/lodash": "^4.14.200",
94 - "@types/node": "^20.8.9",
94 + "@types/node": "^20.8.10",
95 "@types/validator": "^13.11.5",
96 "@vitejs/plugin-vue": "^4.4.0",
97 "@vitejs/plugin-vue-jsx": "^3.0.2",
@@ -101,8 +101,8 @@
101 "@vue/test-utils": "^2.4.1",
102 "@vue/tsconfig": "^0.4.0",
103 "autoprefixer": "^10.4.16",
104 - "cypress": "^13.3.3",
105 - "eslint": "^8.52.0",
104 + "cypress": "^13.4.0",
105 + "eslint": "^8.53.0",
106 "eslint-plugin-cypress": "^2.15.1",
107 "eslint-plugin-vue": "^9.18.1",
108 "fs-extra": "^11.1.1",
@@ -114,8 +114,8 @@
114 "postcss": "^8.4.31",
115 "prettier": "^3.0.3",
116 "sass": "^1.69.5",
117 - "start-server-and-test": "^2.0.1",
118 - "tailwind-config-viewer": "^1.7.2",
117 + "start-server-and-test": "^2.0.2",
118 + "tailwind-config-viewer": "^1.7.3",
119 "tailwindcss": "^3.3.5",
120 "taze": "^0.12.0",
121 "ts-node": "^10.9.1",
@@ -1153,9 +1153,9 @@
1153 }
1154 },
1155 "node_modules/@eslint/eslintrc": {
1156 - "version": "2.1.2",
1157 - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
1158 - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
1156 + "version": "2.1.3",
1157 + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
1158 + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
1159 "dev": true,
1160 "dependencies": {
1161 "ajv": "^6.12.4",
@@ -1176,9 +1176,9 @@
1176 }
1177 },
1178 "node_modules/@eslint/eslintrc/node_modules/globals": {
1179 - "version": "13.21.0",
1180 - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
1181 - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
1179 + "version": "13.23.0",
1180 + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
1181 + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
1182 "dev": true,
1183 "dependencies": {
1184 "type-fest": "^0.20.2"
@@ -1203,9 +1203,9 @@
1203 }
1204 },
1205 "node_modules/@eslint/js": {
1206 - "version": "8.52.0",
1207 - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz",
1208 - "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==",
1206 + "version": "8.53.0",
1207 + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
1208 + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
1209 "dev": true,
1210 "engines": {
1211 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1403,12 +1403,12 @@
1403 }
1404 },
1405 "node_modules/@intlify/core-base": {
1406 - "version": "9.6.2",
1407 - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.6.2.tgz",
1408 - "integrity": "sha512-ci0j2nbEL/pamvqgcCqyIVeQ3LS41F1IRqI5rCBNnpSp0FjNnH8bpha8R3OifkhqatzlP4wGOuN/UqfLYVDv7g==",
1406 + "version": "9.6.5",
1407 + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.6.5.tgz",
1408 + "integrity": "sha512-LzbGXiZkMWPIHnHI0g6q554S87Cmh2mmCmjytK/3pDQfjI84l+dgGoeQuKj02q7EbULRuUUgYVZVqAwEUawXGg==",
1409 "dependencies": {
1410 - "@intlify/message-compiler": "9.6.2",
1411 - "@intlify/shared": "9.6.2"
1410 + "@intlify/message-compiler": "9.6.5",
1411 + "@intlify/shared": "9.6.5"
1412 },
1413 "engines": {
1414 "node": ">= 16"
@@ -1418,11 +1418,11 @@
1418 }
1419 },
1420 "node_modules/@intlify/message-compiler": {
1421 - "version": "9.6.2",
1422 - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.6.2.tgz",
1423 - "integrity": "sha512-kgZQL9zeJDeEB5vvD93Y++HvFUELnT48PjnpfCcF3EJaLLVs9he8IzODiNK42Z40lWbFyja0SXJZjsalybQygA==",
1421 + "version": "9.6.5",
1422 + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.6.5.tgz",
1423 + "integrity": "sha512-WeJ499thIj0p7JaIO1V3JaJbqdqfBykS5R8fElFs5hNeotHtPAMBs4IiA+8/KGFkAbjJusgFefCq6ajP7F7+4Q==",
1424 "dependencies": {
1425 - "@intlify/shared": "9.6.2",
1425 + "@intlify/shared": "9.6.5",
1426 "source-map-js": "^1.0.2"
1427 },
1428 "engines": {
@@ -1433,9 +1433,9 @@
1433 }
1434 },
1435 "node_modules/@intlify/shared": {
1436 - "version": "9.6.2",
1437 - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.6.2.tgz",
1438 - "integrity": "sha512-9KBcXmJNxElp7QMnU8V0/tScTOitDqyFi4HceEZqJyyDkMi8K5DBPMTIuXIAMmtMlXpe/nj5pke7tRw97VeQRA==",
1436 + "version": "9.6.5",
1437 + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.6.5.tgz",
1438 + "integrity": "sha512-gD7Ey47Xi4h/t6P+S04ymMSoA3wVRxGqjxuIMglwRO8POki9h164Epu2N8wk/GHXM/hR6ZGcsx2HArCCENjqSQ==",
1439 "engines": {
1440 "node": ">= 16"
1441 },
@@ -1604,19 +1604,53 @@
1604 "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
1605 },
1606 "node_modules/@koa/router": {
1607 - "version": "9.4.0",
1608 - "resolved": "https://registry.npmjs.org/@koa/router/-/router-9.4.0.tgz",
1609 - "integrity": "sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A==",
1607 + "version": "12.0.1",
1608 + "resolved": "https://registry.npmjs.org/@koa/router/-/router-12.0.1.tgz",
1609 + "integrity": "sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==",
1610 "dev": true,
1611 "dependencies": {
1612 - "debug": "^4.1.1",
1613 - "http-errors": "^1.7.3",
1612 + "debug": "^4.3.4",
1613 + "http-errors": "^2.0.0",
1614 "koa-compose": "^4.1.0",
1615 "methods": "^1.1.2",
1616 - "path-to-regexp": "^6.1.0"
1616 + "path-to-regexp": "^6.2.1"
1617 },
1618 "engines": {
1619 - "node": ">= 8.0.0"
1619 + "node": ">= 12"
1620 + }
1621 + },
1622 + "node_modules/@koa/router/node_modules/depd": {
1623 + "version": "2.0.0",
1624 + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
1625 + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
1626 + "dev": true,
1627 + "engines": {
1628 + "node": ">= 0.8"
1629 + }
1630 + },
1631 + "node_modules/@koa/router/node_modules/http-errors": {
1632 + "version": "2.0.0",
1633 + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
1634 + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
1635 + "dev": true,
1636 + "dependencies": {
1637 + "depd": "2.0.0",
1638 + "inherits": "2.0.4",
1639 + "setprototypeof": "1.2.0",
1640 + "statuses": "2.0.1",
1641 + "toidentifier": "1.0.1"
1642 + },
1643 + "engines": {
1644 + "node": ">= 0.8"
1645 + }
1646 + },
1647 + "node_modules/@koa/router/node_modules/statuses": {
1648 + "version": "2.0.1",
1649 + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1650 + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1651 + "dev": true,
1652 + "engines": {
1653 + "node": ">= 0.8"
1654 }
1655 },
1656 "node_modules/@kurkle/color": {
@@ -3174,9 +3208,9 @@
3208 }
3209 },
3210 "node_modules/@types/bytes": {
3177 - "version": "3.1.3",
3178 - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.3.tgz",
3179 - "integrity": "sha512-eEgZiWn6cjG8tc+AkI3FIa9ub9zhLMSRHqbecHe5yffqws+848zoHdbgFYxvUks4RElfJB9cupvqcd1gvDFQig==",
3211 + "version": "3.1.4",
3212 + "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.4.tgz",
3213 + "integrity": "sha512-A0uYgOj3zNc4hNjHc5lYUfJQ/HVyBXiUMKdXd7ysclaE6k9oJdavQzODHuwjpUu2/boCP8afjQYi8z/GtvNCWA==",
3214 "dev": true
3215 },
3216 "node_modules/@types/chai": {
@@ -3234,9 +3268,9 @@
3268 }
3269 },
3270 "node_modules/@types/jsdom": {
3237 - "version": "21.1.4",
3238 - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.4.tgz",
3239 - "integrity": "sha512-NzAMLEV0KQ4cBaDx3Ls8VfJUElyDUm1xrtYRmcMK0gF8L5xYbujFVaQlJ50yinQ/d47j2rEP1XUzkiYrw4YRFA==",
3271 + "version": "21.1.5",
3272 + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.5.tgz",
3273 + "integrity": "sha512-sBK/3YjS3uuPj+HzZyhB4GGTnFmk0mdyQfhzZ/sqs9ciyG41QJdZZdwcPa6OfW97OTNTwl5tBAsfEOm/dui9pQ==",
3274 "dev": true,
3275 "dependencies": {
3276 "@types/node": "*",
@@ -3306,9 +3340,9 @@
3340 "integrity": "sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ=="
3341 },
3342 "node_modules/@types/node": {
3309 - "version": "20.8.9",
3310 - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz",
3311 - "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==",
3343 + "version": "20.8.10",
3344 + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
3345 + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
3346 "dev": true,
3347 "dependencies": {
3348 "undici-types": "~5.26.4"
@@ -3903,36 +3937,36 @@
3937 }
3938 },
3939 "node_modules/@vue/compiler-core": {
3906 - "version": "3.3.7",
3907 - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.7.tgz",
3908 - "integrity": "sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==",
3940 + "version": "3.3.8",
3941 + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
3942 + "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
3943 "dependencies": {
3944 "@babel/parser": "^7.23.0",
3911 - "@vue/shared": "3.3.7",
3945 + "@vue/shared": "3.3.8",
3946 "estree-walker": "^2.0.2",
3947 "source-map-js": "^1.0.2"
3948 }
3949 },
3950 "node_modules/@vue/compiler-dom": {
3917 - "version": "3.3.7",
3918 - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.7.tgz",
3919 - "integrity": "sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==",
3951 + "version": "3.3.8",
3952 + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
3953 + "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
3954 "dependencies": {
3921 - "@vue/compiler-core": "3.3.7",
3922 - "@vue/shared": "3.3.7"
3955 + "@vue/compiler-core": "3.3.8",
3956 + "@vue/shared": "3.3.8"
3957 }
3958 },
3959 "node_modules/@vue/compiler-sfc": {
3926 - "version": "3.3.7",
3927 - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.7.tgz",
3928 - "integrity": "sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==",
3960 + "version": "3.3.8",
3961 + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
3962 + "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
3963 "dependencies": {
3964 "@babel/parser": "^7.23.0",
3931 - "@vue/compiler-core": "3.3.7",
3932 - "@vue/compiler-dom": "3.3.7",
3933 - "@vue/compiler-ssr": "3.3.7",
3934 - "@vue/reactivity-transform": "3.3.7",
3935 - "@vue/shared": "3.3.7",
3965 + "@vue/compiler-core": "3.3.8",
3966 + "@vue/compiler-dom": "3.3.8",
3967 + "@vue/compiler-ssr": "3.3.8",
3968 + "@vue/reactivity-transform": "3.3.8",
3969 + "@vue/shared": "3.3.8",
3970 "estree-walker": "^2.0.2",
3971 "magic-string": "^0.30.5",
3972 "postcss": "^8.4.31",
@@ -3940,12 +3974,12 @@
3974 }
3975 },
3976 "node_modules/@vue/compiler-ssr": {
3943 - "version": "3.3.7",
3944 - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.7.tgz",
3945 - "integrity": "sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==",
3977 + "version": "3.3.8",
3978 + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
3979 + "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
3980 "dependencies": {
3947 - "@vue/compiler-dom": "3.3.7",
3948 - "@vue/shared": "3.3.7"
3981 + "@vue/compiler-dom": "3.3.8",
3982 + "@vue/shared": "3.3.8"
3983 }
3984 },
3985 "node_modules/@vue/devtools-api": {
@@ -4040,41 +4074,41 @@
4074 }
4075 },
4076 "node_modules/@vue/reactivity": {
4043 - "version": "3.3.7",
4044 - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.7.tgz",
4045 - "integrity": "sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==",
4077 + "version": "3.3.8",
4078 + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz",
4079 + "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
4080 "dependencies": {
4047 - "@vue/shared": "3.3.7"
4081 + "@vue/shared": "3.3.8"
4082 }
4083 },
4084 "node_modules/@vue/reactivity-transform": {
4051 - "version": "3.3.7",
4052 - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.7.tgz",
4053 - "integrity": "sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==",
4085 + "version": "3.3.8",
4086 + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
4087 + "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
4088 "dependencies": {
4089 "@babel/parser": "^7.23.0",
4056 - "@vue/compiler-core": "3.3.7",
4057 - "@vue/shared": "3.3.7",
4090 + "@vue/compiler-core": "3.3.8",
4091 + "@vue/shared": "3.3.8",
4092 "estree-walker": "^2.0.2",
4093 "magic-string": "^0.30.5"
4094 }
4095 },
4096 "node_modules/@vue/runtime-core": {
4063 - "version": "3.3.7",
4064 - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.7.tgz",
4065 - "integrity": "sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==",
4097 + "version": "3.3.8",
4098 + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
4099 + "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
4100 "dependencies": {
4067 - "@vue/reactivity": "3.3.7",
4068 - "@vue/shared": "3.3.7"
4101 + "@vue/reactivity": "3.3.8",
4102 + "@vue/shared": "3.3.8"
4103 }
4104 },
4105 "node_modules/@vue/runtime-dom": {
4072 - "version": "3.3.7",
4073 - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.7.tgz",
4074 - "integrity": "sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==",
4106 + "version": "3.3.8",
4107 + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
4108 + "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
4109 "dependencies": {
4076 - "@vue/runtime-core": "3.3.7",
4077 - "@vue/shared": "3.3.7",
4110 + "@vue/runtime-core": "3.3.8",
4111 + "@vue/shared": "3.3.8",
4112 "csstype": "^3.1.2"
4113 }
4114 },
@@ -4084,21 +4118,21 @@
4118 "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
4119 },
4120 "node_modules/@vue/server-renderer": {
4087 - "version": "3.3.7",
4088 - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.7.tgz",
4089 - "integrity": "sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==",
4121 + "version": "3.3.8",
4122 + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
4123 + "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
4124 "dependencies": {
4091 - "@vue/compiler-ssr": "3.3.7",
4092 - "@vue/shared": "3.3.7"
4125 + "@vue/compiler-ssr": "3.3.8",
4126 + "@vue/shared": "3.3.8"
4127 },
4128 "peerDependencies": {
4095 - "vue": "3.3.7"
4129 + "vue": "3.3.8"
4130 }
4131 },
4132 "node_modules/@vue/shared": {
4099 - "version": "3.3.7",
4100 - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.7.tgz",
4101 - "integrity": "sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg=="
4133 + "version": "3.3.8",
4134 + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
4135 + "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
4136 },
4137 "node_modules/@vue/test-utils": {
4138 "version": "2.4.1",
@@ -5697,9 +5731,9 @@
5731 "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
5732 },
5733 "node_modules/cypress": {
5700 - "version": "13.3.3",
5701 - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.3.3.tgz",
5702 - "integrity": "sha512-mbdkojHhKB1xbrj7CrKWHi22uFx9P9vQFiR0sYDZZoK99OMp9/ZYN55TO5pjbXmV7xvCJ4JwBoADXjOJK8aCJw==",
5734 + "version": "13.4.0",
5735 + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.4.0.tgz",
5736 + "integrity": "sha512-KeWNC9xSHG/ewZURVbaQsBQg2mOKw4XhjJZFKjWbEjgZCdxpPXLpJnfq5Jns1Gvnjp6AlnIfpZfWFlDgVKXdWQ==",
5737 "dev": true,
5738 "hasInstallScript": true,
5739 "dependencies": {
@@ -6804,15 +6838,15 @@
6838 }
6839 },
6840 "node_modules/eslint": {
6807 - "version": "8.52.0",
6808 - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz",
6809 - "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==",
6841 + "version": "8.53.0",
6842 + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
6843 + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
6844 "dev": true,
6845 "dependencies": {
6846 "@eslint-community/eslint-utils": "^4.2.0",
6847 "@eslint-community/regexpp": "^4.6.1",
6814 - "@eslint/eslintrc": "^2.1.2",
6815 - "@eslint/js": "8.52.0",
6848 + "@eslint/eslintrc": "^2.1.3",
6849 + "@eslint/js": "8.53.0",
6850 "@humanwhocodes/config-array": "^0.11.13",
6851 "@humanwhocodes/module-importer": "^1.0.1",
6852 "@nodelib/fs.walk": "^1.2.8",
@@ -7472,9 +7506,9 @@
7506 "dev": true
7507 },
7508 "node_modules/follow-redirects": {
7475 - "version": "1.15.2",
7476 - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
7477 - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
7509 + "version": "1.15.3",
7510 + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
7511 + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
7512 "dev": true,
7513 "funding": [
7514 {
@@ -8852,9 +8886,9 @@
8886 }
8887 },
8888 "node_modules/joi": {
8855 - "version": "17.10.1",
8856 - "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz",
8857 - "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==",
8889 + "version": "17.11.0",
8890 + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz",
8891 + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==",
8892 "dev": true,
8893 "dependencies": {
8894 "@hapi/hoek": "^9.0.0",
@@ -8865,9 +8899,9 @@
8899 }
8900 },
8901 "node_modules/jose": {
8868 - "version": "5.0.1",
8869 - "resolved": "https://registry.npmjs.org/jose/-/jose-5.0.1.tgz",
8870 - "integrity": "sha512-gRVzy7s3RRdGbXmcTdlOswJOjhwPLx1ijIgAqLY6ktzFpOJxxYn4l0fC2vHaHHi4YBX/5FOL3aY+6W0cvQgpug==",
8902 + "version": "5.1.0",
8903 + "resolved": "https://registry.npmjs.org/jose/-/jose-5.1.0.tgz",
8904 + "integrity": "sha512-H+RVqxA6apaJ0rcQYupKYhos7uosAiF42gUcWZiwhICWMphDULFj/CRr1R0tV/JCv9DEeJaSyYYpc9luHHNT4g==",
8905 "funding": {
8906 "url": "https://github.com/sponsors/panva"
8907 }
@@ -13687,9 +13721,9 @@
13721 "dev": true
13722 },
13723 "node_modules/start-server-and-test": {
13690 - "version": "2.0.1",
13691 - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz",
13692 - "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==",
13724 + "version": "2.0.2",
13725 + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.2.tgz",
13726 + "integrity": "sha512-4sGS2QmETUwqeBUqtTLP7OqXp3PdDnevaWlPlrFQgn8+7uCgVg4Do7/H/ZhAAVyvnL3DqKyANhnLgcgxrjhrMA==",
13727 "dev": true,
13728 "dependencies": {
13729 "arg": "^5.0.2",
@@ -13699,7 +13733,7 @@
13733 "execa": "5.1.1",
13734 "lazy-ass": "1.6.0",
13735 "ps-tree": "1.2.0",
13702 - "wait-on": "7.0.1"
13736 + "wait-on": "7.1.0"
13737 },
13738 "bin": {
13739 "server-test": "src/bin/start.js",
@@ -14173,15 +14207,15 @@
14207 }
14208 },
14209 "node_modules/tailwind-config-viewer": {
14176 - "version": "1.7.2",
14177 - "resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-1.7.2.tgz",
14178 - "integrity": "sha512-3JJCeAAlvG+i/EBj+tQb0x4weo30QjdSAo4hlcnVbtD+CkpzHi/UwU9InbPMcYH+ESActoa2kCyjpLEyjEkn0Q==",
14210 + "version": "1.7.3",
14211 + "resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-1.7.3.tgz",
14212 + "integrity": "sha512-rgeFXe9vL4njtaSI1y2uUAD1aRx05RYHbReN72ARAVEVSlNmS0Zf46pj3/ORc3xQwLK/AzbaIs6UFcK7hJSIlA==",
14213 "dev": true,
14214 "dependencies": {
14181 - "@koa/router": "^9.0.1",
14215 + "@koa/router": "^12.0.1",
14216 "commander": "^6.0.0",
14217 "fs-extra": "^9.0.1",
14184 - "koa": "^2.12.0",
14218 + "koa": "^2.14.2",
14219 "koa-static": "^5.0.0",
14220 "open": "^7.0.4",
14221 "portfinder": "^1.0.26",
@@ -15620,15 +15654,15 @@
15654 }
15655 },
15656 "node_modules/vue": {
15623 - "version": "3.3.7",
15624 - "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.7.tgz",
15625 - "integrity": "sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==",
15657 + "version": "3.3.8",
15658 + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz",
15659 + "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
15660 "dependencies": {
15627 - "@vue/compiler-dom": "3.3.7",
15628 - "@vue/compiler-sfc": "3.3.7",
15629 - "@vue/runtime-dom": "3.3.7",
15630 - "@vue/server-renderer": "3.3.7",
15631 - "@vue/shared": "3.3.7"
15661 + "@vue/compiler-dom": "3.3.8",
15662 + "@vue/compiler-sfc": "3.3.8",
15663 + "@vue/runtime-dom": "3.3.8",
15664 + "@vue/server-renderer": "3.3.8",
15665 + "@vue/shared": "3.3.8"
15666 },
15667 "peerDependencies": {
15668 "typescript": "*"
@@ -15751,12 +15785,12 @@
15785 }
15786 },
15787 "node_modules/vue-i18n": {
15754 - "version": "9.6.2",
15755 - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.6.2.tgz",
15756 - "integrity": "sha512-J43grTQjPR8LCUxvx3mkoM+11xhTnej1Al4lvJCEeKmQqf8eqbuYPQb54HXnEg/UzZyaxLBAwPAUTbrZ8V7hcg==",
15788 + "version": "9.6.5",
15789 + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.6.5.tgz",
15790 + "integrity": "sha512-dpUEjKHg7pEsaS7ZPPxp1CflaR7bGmsvZJEhnszHPKl9OTNyno5j/DvMtMSo41kpddq4felLA7GK2prjpnXVlw==",
15791 "dependencies": {
15758 - "@intlify/core-base": "9.6.2",
15759 - "@intlify/shared": "9.6.2",
15792 + "@intlify/core-base": "9.6.5",
15793 + "@intlify/shared": "9.6.5",
15794 "@vue/devtools-api": "^6.5.0"
15795 },
15796 "engines": {
@@ -15947,16 +15981,16 @@
15981 }
15982 },
15983 "node_modules/wait-on": {
15950 - "version": "7.0.1",
15951 - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz",
15952 - "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==",
15984 + "version": "7.1.0",
15985 + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.1.0.tgz",
15986 + "integrity": "sha512-U7TF/OYYzAg+OoiT/B8opvN48UHt0QYMi4aD3PjRFpybQ+o6czQF8Ig3SKCCMJdxpBrCalIJ4O00FBof27Fu9Q==",
15987 "dev": true,
15988 "dependencies": {
15989 "axios": "^0.27.2",
15956 - "joi": "^17.7.0",
15990 + "joi": "^17.11.0",
15991 "lodash": "^4.17.21",
15958 - "minimist": "^1.2.7",
15959 - "rxjs": "^7.8.0"
15992 + "minimist": "^1.2.8",
15993 + "rxjs": "^7.8.1"
15994 },
15995 "bin": {
15996 "wait-on": "bin/wait-on"
@@ -17016,9 +17050,9 @@
17050 "dev": true
17051 },
17052 "@eslint/eslintrc": {
17019 - "version": "2.1.2",
17020 - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
17021 - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
17053 + "version": "2.1.3",
17054 + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
17055 + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
17056 "dev": true,
17057 "requires": {
17058 "ajv": "^6.12.4",
@@ -17033,9 +17067,9 @@
17067 },
17068 "dependencies": {
17069 "globals": {
17036 - "version": "13.21.0",
17037 - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
17038 - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
17070 + "version": "13.23.0",
17071 + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
17072 + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
17073 "dev": true,
17074 "requires": {
17075 "type-fest": "^0.20.2"
@@ -17050,9 +17084,9 @@
17084 }
17085 },
17086 "@eslint/js": {
17053 - "version": "8.52.0",
17054 - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz",
17055 - "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==",
17087 + "version": "8.53.0",
17088 + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
17089 + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
17090 "dev": true
17091 },
17092 "@faker-js/faker": {
@@ -17209,27 +17243,27 @@
17243 }
17244 },
17245 "@intlify/core-base": {
17212 - "version": "9.6.2",
17213 - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.6.2.tgz",
17214 - "integrity": "sha512-ci0j2nbEL/pamvqgcCqyIVeQ3LS41F1IRqI5rCBNnpSp0FjNnH8bpha8R3OifkhqatzlP4wGOuN/UqfLYVDv7g==",
17246 + "version": "9.6.5",
17247 + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.6.5.tgz",
17248 + "integrity": "sha512-LzbGXiZkMWPIHnHI0g6q554S87Cmh2mmCmjytK/3pDQfjI84l+dgGoeQuKj02q7EbULRuUUgYVZVqAwEUawXGg==",
17249 "requires": {
17216 - "@intlify/message-compiler": "9.6.2",
17217 - "@intlify/shared": "9.6.2"
17250 + "@intlify/message-compiler": "9.6.5",
17251 + "@intlify/shared": "9.6.5"
17252 }
17253 },
17254 "@intlify/message-compiler": {
17221 - "version": "9.6.2",
17222 - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.6.2.tgz",
17223 - "integrity": "sha512-kgZQL9zeJDeEB5vvD93Y++HvFUELnT48PjnpfCcF3EJaLLVs9he8IzODiNK42Z40lWbFyja0SXJZjsalybQygA==",
17255 + "version": "9.6.5",
17256 + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.6.5.tgz",
17257 + "integrity": "sha512-WeJ499thIj0p7JaIO1V3JaJbqdqfBykS5R8fElFs5hNeotHtPAMBs4IiA+8/KGFkAbjJusgFefCq6ajP7F7+4Q==",
17258 "requires": {
17225 - "@intlify/shared": "9.6.2",
17259 + "@intlify/shared": "9.6.5",
17260 "source-map-js": "^1.0.2"
17261 }
17262 },
17263 "@intlify/shared": {
17230 - "version": "9.6.2",
17231 - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.6.2.tgz",
17232 - "integrity": "sha512-9KBcXmJNxElp7QMnU8V0/tScTOitDqyFi4HceEZqJyyDkMi8K5DBPMTIuXIAMmtMlXpe/nj5pke7tRw97VeQRA=="
17264 + "version": "9.6.5",
17265 + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.6.5.tgz",
17266 + "integrity": "sha512-gD7Ey47Xi4h/t6P+S04ymMSoA3wVRxGqjxuIMglwRO8POki9h164Epu2N8wk/GHXM/hR6ZGcsx2HArCCENjqSQ=="
17267 },
17268 "@isaacs/cliui": {
17269 "version": "8.0.2",
@@ -17349,16 +17383,43 @@
17383 "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
17384 },
17385 "@koa/router": {
17352 - "version": "9.4.0",
17353 - "resolved": "https://registry.npmjs.org/@koa/router/-/router-9.4.0.tgz",
17354 - "integrity": "sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A==",
17386 + "version": "12.0.1",
17387 + "resolved": "https://registry.npmjs.org/@koa/router/-/router-12.0.1.tgz",
17388 + "integrity": "sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==",
17389 "dev": true,
17390 "requires": {
17357 - "debug": "^4.1.1",
17358 - "http-errors": "^1.7.3",
17391 + "debug": "^4.3.4",
17392 + "http-errors": "^2.0.0",
17393 "koa-compose": "^4.1.0",
17394 "methods": "^1.1.2",
17361 - "path-to-regexp": "^6.1.0"
17395 + "path-to-regexp": "^6.2.1"
17396 + },
17397 + "dependencies": {
17398 + "depd": {
17399 + "version": "2.0.0",
17400 + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
17401 + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
17402 + "dev": true
17403 + },
17404 + "http-errors": {
17405 + "version": "2.0.0",
17406 + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
17407 + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
17408 + "dev": true,
17409 + "requires": {
17410 + "depd": "2.0.0",
17411 + "inherits": "2.0.4",
17412 + "setprototypeof": "1.2.0",
17413 + "statuses": "2.0.1",
17414 + "toidentifier": "1.0.1"
17415 + }
17416 + },
17417 + "statuses": {
17418 + "version": "2.0.1",
17419 + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
17420 + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
17421 + "dev": true
17422 + }
17423 }
17424 },
17425 "@kurkle/color": {
@@ -18463,9 +18524,9 @@
18524 }
18525 },
18526 "@types/bytes": {
18466 - "version": "3.1.3",
18467 - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.3.tgz",
18468 - "integrity": "sha512-eEgZiWn6cjG8tc+AkI3FIa9ub9zhLMSRHqbecHe5yffqws+848zoHdbgFYxvUks4RElfJB9cupvqcd1gvDFQig==",
18527 + "version": "3.1.4",
18528 + "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.4.tgz",
18529 + "integrity": "sha512-A0uYgOj3zNc4hNjHc5lYUfJQ/HVyBXiUMKdXd7ysclaE6k9oJdavQzODHuwjpUu2/boCP8afjQYi8z/GtvNCWA==",
18530 "dev": true
18531 },
18532 "@types/chai": {
@@ -18523,9 +18584,9 @@
18584 }
18585 },
18586 "@types/jsdom": {
18526 - "version": "21.1.4",
18527 - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.4.tgz",
18528 - "integrity": "sha512-NzAMLEV0KQ4cBaDx3Ls8VfJUElyDUm1xrtYRmcMK0gF8L5xYbujFVaQlJ50yinQ/d47j2rEP1XUzkiYrw4YRFA==",
18587 + "version": "21.1.5",
18588 + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.5.tgz",
18589 + "integrity": "sha512-sBK/3YjS3uuPj+HzZyhB4GGTnFmk0mdyQfhzZ/sqs9ciyG41QJdZZdwcPa6OfW97OTNTwl5tBAsfEOm/dui9pQ==",
18590 "dev": true,
18591 "requires": {
18592 "@types/node": "*",
@@ -18595,9 +18656,9 @@
18656 "integrity": "sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ=="
18657 },
18658 "@types/node": {
18598 - "version": "20.8.9",
18599 - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz",
18600 - "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==",
18659 + "version": "20.8.10",
18660 + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
18661 + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
18662 "dev": true,
18663 "requires": {
18664 "undici-types": "~5.26.4"
@@ -19032,36 +19093,36 @@
19093 }
19094 },
19095 "@vue/compiler-core": {
19035 - "version": "3.3.7",
19036 - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.7.tgz",
19037 - "integrity": "sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==",
19096 + "version": "3.3.8",
19097 + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
19098 + "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
19099 "requires": {
19100 "@babel/parser": "^7.23.0",
19040 - "@vue/shared": "3.3.7",
19101 + "@vue/shared": "3.3.8",
19102 "estree-walker": "^2.0.2",
19103 "source-map-js": "^1.0.2"
19104 }
19105 },
19106 "@vue/compiler-dom": {
19046 - "version": "3.3.7",
19047 - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.7.tgz",
19048 - "integrity": "sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==",
19107 + "version": "3.3.8",
19108 + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
19109 + "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
19110 "requires": {
19050 - "@vue/compiler-core": "3.3.7",
19051 - "@vue/shared": "3.3.7"
19111 + "@vue/compiler-core": "3.3.8",
19112 + "@vue/shared": "3.3.8"
19113 }
19114 },
19115 "@vue/compiler-sfc": {
19055 - "version": "3.3.7",
19056 - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.7.tgz",
19057 - "integrity": "sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==",
19116 + "version": "3.3.8",
19117 + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
19118 + "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
19119 "requires": {
19120 "@babel/parser": "^7.23.0",
19060 - "@vue/compiler-core": "3.3.7",
19061 - "@vue/compiler-dom": "3.3.7",
19062 - "@vue/compiler-ssr": "3.3.7",
19063 - "@vue/reactivity-transform": "3.3.7",
19064 - "@vue/shared": "3.3.7",
19121 + "@vue/compiler-core": "3.3.8",
19122 + "@vue/compiler-dom": "3.3.8",
19123 + "@vue/compiler-ssr": "3.3.8",
19124 + "@vue/reactivity-transform": "3.3.8",
19125 + "@vue/shared": "3.3.8",
19126 "estree-walker": "^2.0.2",
19127 "magic-string": "^0.30.5",
19128 "postcss": "^8.4.31",
@@ -19069,12 +19130,12 @@
19130 }
19131 },
19132 "@vue/compiler-ssr": {
19072 - "version": "3.3.7",
19073 - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.7.tgz",
19074 - "integrity": "sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==",
19133 + "version": "3.3.8",
19134 + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
19135 + "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
19136 "requires": {
19076 - "@vue/compiler-dom": "3.3.7",
19077 - "@vue/shared": "3.3.7"
19137 + "@vue/compiler-dom": "3.3.8",
19138 + "@vue/shared": "3.3.8"
19139 }
19140 },
19141 "@vue/devtools-api": {
@@ -19140,41 +19201,41 @@
19201 }
19202 },
19203 "@vue/reactivity": {
19143 - "version": "3.3.7",
19144 - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.7.tgz",
19145 - "integrity": "sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==",
19204 + "version": "3.3.8",
19205 + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz",
19206 + "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
19207 "requires": {
19147 - "@vue/shared": "3.3.7"
19208 + "@vue/shared": "3.3.8"
19209 }
19210 },
19211 "@vue/reactivity-transform": {
19151 - "version": "3.3.7",
19152 - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.7.tgz",
19153 - "integrity": "sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==",
19212 + "version": "3.3.8",
19213 + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
19214 + "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
19215 "requires": {
19216 "@babel/parser": "^7.23.0",
19156 - "@vue/compiler-core": "3.3.7",
19157 - "@vue/shared": "3.3.7",
19217 + "@vue/compiler-core": "3.3.8",
19218 + "@vue/shared": "3.3.8",
19219 "estree-walker": "^2.0.2",
19220 "magic-string": "^0.30.5"
19221 }
19222 },
19223 "@vue/runtime-core": {
19163 - "version": "3.3.7",
19164 - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.7.tgz",
19165 - "integrity": "sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==",
19224 + "version": "3.3.8",
19225 + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
19226 + "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
19227 "requires": {
19167 - "@vue/reactivity": "3.3.7",
19168 - "@vue/shared": "3.3.7"
19228 + "@vue/reactivity": "3.3.8",
19229 + "@vue/shared": "3.3.8"
19230 }
19231 },
19232 "@vue/runtime-dom": {
19172 - "version": "3.3.7",
19173 - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.7.tgz",
19174 - "integrity": "sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==",
19233 + "version": "3.3.8",
19234 + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
19235 + "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
19236 "requires": {
19176 - "@vue/runtime-core": "3.3.7",
19177 - "@vue/shared": "3.3.7",
19237 + "@vue/runtime-core": "3.3.8",
19238 + "@vue/shared": "3.3.8",
19239 "csstype": "^3.1.2"
19240 },
19241 "dependencies": {
@@ -19186,18 +19247,18 @@
19247 }
19248 },
19249 "@vue/server-renderer": {
19189 - "version": "3.3.7",
19190 - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.7.tgz",
19191 - "integrity": "sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==",
19250 + "version": "3.3.8",
19251 + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
19252 + "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
19253 "requires": {
19193 - "@vue/compiler-ssr": "3.3.7",
19194 - "@vue/shared": "3.3.7"
19254 + "@vue/compiler-ssr": "3.3.8",
19255 + "@vue/shared": "3.3.8"
19256 }
19257 },
19258 "@vue/shared": {
19198 - "version": "3.3.7",
19199 - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.7.tgz",
19200 - "integrity": "sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg=="
19259 + "version": "3.3.8",
19260 + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
19261 + "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
19262 },
19263 "@vue/test-utils": {
19264 "version": "2.4.1",
@@ -20329,9 +20390,9 @@
20390 "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
20391 },
20392 "cypress": {
20332 - "version": "13.3.3",
20333 - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.3.3.tgz",
20334 - "integrity": "sha512-mbdkojHhKB1xbrj7CrKWHi22uFx9P9vQFiR0sYDZZoK99OMp9/ZYN55TO5pjbXmV7xvCJ4JwBoADXjOJK8aCJw==",
20393 + "version": "13.4.0",
20394 + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.4.0.tgz",
20395 + "integrity": "sha512-KeWNC9xSHG/ewZURVbaQsBQg2mOKw4XhjJZFKjWbEjgZCdxpPXLpJnfq5Jns1Gvnjp6AlnIfpZfWFlDgVKXdWQ==",
20396 "dev": true,
20397 "requires": {
20398 "@cypress/request": "^3.0.0",
@@ -21163,15 +21224,15 @@
21224 "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
21225 },
21226 "eslint": {
21166 - "version": "8.52.0",
21167 - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz",
21168 - "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==",
21227 + "version": "8.53.0",
21228 + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
21229 + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
21230 "dev": true,
21231 "requires": {
21232 "@eslint-community/eslint-utils": "^4.2.0",
21233 "@eslint-community/regexpp": "^4.6.1",
21173 - "@eslint/eslintrc": "^2.1.2",
21174 - "@eslint/js": "8.52.0",
21234 + "@eslint/eslintrc": "^2.1.3",
21235 + "@eslint/js": "8.53.0",
21236 "@humanwhocodes/config-array": "^0.11.13",
21237 "@humanwhocodes/module-importer": "^1.0.1",
21238 "@nodelib/fs.walk": "^1.2.8",
@@ -21655,9 +21716,9 @@
21716 "dev": true
21717 },
21718 "follow-redirects": {
21658 - "version": "1.15.2",
21659 - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
21660 - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
21719 + "version": "1.15.3",
21720 + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
21721 + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
21722 "dev": true
21723 },
21724 "for-each": {
@@ -22622,9 +22683,9 @@
22683 "dev": true
22684 },
22685 "joi": {
22625 - "version": "17.10.1",
22626 - "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz",
22627 - "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==",
22686 + "version": "17.11.0",
22687 + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz",
22688 + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==",
22689 "dev": true,
22690 "requires": {
22691 "@hapi/hoek": "^9.0.0",
@@ -22635,9 +22696,9 @@
22696 }
22697 },
22698 "jose": {
22638 - "version": "5.0.1",
22639 - "resolved": "https://registry.npmjs.org/jose/-/jose-5.0.1.tgz",
22640 - "integrity": "sha512-gRVzy7s3RRdGbXmcTdlOswJOjhwPLx1ijIgAqLY6ktzFpOJxxYn4l0fC2vHaHHi4YBX/5FOL3aY+6W0cvQgpug=="
22699 + "version": "5.1.0",
22700 + "resolved": "https://registry.npmjs.org/jose/-/jose-5.1.0.tgz",
22701 + "integrity": "sha512-H+RVqxA6apaJ0rcQYupKYhos7uosAiF42gUcWZiwhICWMphDULFj/CRr1R0tV/JCv9DEeJaSyYYpc9luHHNT4g=="
22702 },
22703 "js-beautify": {
22704 "version": "1.14.9",
@@ -26214,9 +26275,9 @@
26275 "dev": true
26276 },
26277 "start-server-and-test": {
26217 - "version": "2.0.1",
26218 - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz",
26219 - "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==",
26278 + "version": "2.0.2",
26279 + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.2.tgz",
26280 + "integrity": "sha512-4sGS2QmETUwqeBUqtTLP7OqXp3PdDnevaWlPlrFQgn8+7uCgVg4Do7/H/ZhAAVyvnL3DqKyANhnLgcgxrjhrMA==",
26281 "dev": true,
26282 "requires": {
26283 "arg": "^5.0.2",
@@ -26226,7 +26287,7 @@
26287 "execa": "5.1.1",
26288 "lazy-ass": "1.6.0",
26289 "ps-tree": "1.2.0",
26229 - "wait-on": "7.0.1"
26290 + "wait-on": "7.1.0"
26291 },
26292 "dependencies": {
26293 "execa": {
@@ -26570,15 +26631,15 @@
26631 }
26632 },
26633 "tailwind-config-viewer": {
26573 - "version": "1.7.2",
26574 - "resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-1.7.2.tgz",
26575 - "integrity": "sha512-3JJCeAAlvG+i/EBj+tQb0x4weo30QjdSAo4hlcnVbtD+CkpzHi/UwU9InbPMcYH+ESActoa2kCyjpLEyjEkn0Q==",
26634 + "version": "1.7.3",
26635 + "resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-1.7.3.tgz",
26636 + "integrity": "sha512-rgeFXe9vL4njtaSI1y2uUAD1aRx05RYHbReN72ARAVEVSlNmS0Zf46pj3/ORc3xQwLK/AzbaIs6UFcK7hJSIlA==",
26637 "dev": true,
26638 "requires": {
26578 - "@koa/router": "^9.0.1",
26639 + "@koa/router": "^12.0.1",
26640 "commander": "^6.0.0",
26641 "fs-extra": "^9.0.1",
26581 - "koa": "^2.12.0",
26642 + "koa": "^2.14.2",
26643 "koa-static": "^5.0.0",
26644 "open": "^7.0.4",
26645 "portfinder": "^1.0.26",
@@ -27569,15 +27630,15 @@
27630 }
27631 },
27632 "vue": {
27572 - "version": "3.3.7",
27573 - "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.7.tgz",
27574 - "integrity": "sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==",
27633 + "version": "3.3.8",
27634 + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz",
27635 + "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
27636 "requires": {
27576 - "@vue/compiler-dom": "3.3.7",
27577 - "@vue/compiler-sfc": "3.3.7",
27578 - "@vue/runtime-dom": "3.3.7",
27579 - "@vue/server-renderer": "3.3.7",
27580 - "@vue/shared": "3.3.7"
27637 + "@vue/compiler-dom": "3.3.8",
27638 + "@vue/compiler-sfc": "3.3.8",
27639 + "@vue/runtime-dom": "3.3.8",
27640 + "@vue/server-renderer": "3.3.8",
27641 + "@vue/shared": "3.3.8"
27642 }
27643 },
27644 "vue-advanced-cropper": {
@@ -27658,12 +27719,12 @@
27719 }
27720 },
27721 "vue-i18n": {
27661 - "version": "9.6.2",
27662 - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.6.2.tgz",
27663 - "integrity": "sha512-J43grTQjPR8LCUxvx3mkoM+11xhTnej1Al4lvJCEeKmQqf8eqbuYPQb54HXnEg/UzZyaxLBAwPAUTbrZ8V7hcg==",
27722 + "version": "9.6.5",
27723 + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.6.5.tgz",
27724 + "integrity": "sha512-dpUEjKHg7pEsaS7ZPPxp1CflaR7bGmsvZJEhnszHPKl9OTNyno5j/DvMtMSo41kpddq4felLA7GK2prjpnXVlw==",
27725 "requires": {
27665 - "@intlify/core-base": "9.6.2",
27666 - "@intlify/shared": "9.6.2",
27726 + "@intlify/core-base": "9.6.5",
27727 + "@intlify/shared": "9.6.5",
27728 "@vue/devtools-api": "^6.5.0"
27729 }
27730 },
@@ -27798,16 +27859,16 @@
27859 }
27860 },
27861 "wait-on": {
27801 - "version": "7.0.1",
27802 - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz",
27803 - "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==",
27862 + "version": "7.1.0",
27863 + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.1.0.tgz",
27864 + "integrity": "sha512-U7TF/OYYzAg+OoiT/B8opvN48UHt0QYMi4aD3PjRFpybQ+o6czQF8Ig3SKCCMJdxpBrCalIJ4O00FBof27Fu9Q==",
27865 "dev": true,
27866 "requires": {
27867 "axios": "^0.27.2",
27807 - "joi": "^17.7.0",
27868 + "joi": "^17.11.0",
27869 "lodash": "^4.17.21",
27809 - "minimist": "^1.2.7",
27810 - "rxjs": "^7.8.0"
27870 + "minimist": "^1.2.8",
27871 + "rxjs": "^7.8.1"
27872 }
27873 },
27874 "walk-up-path": {
package.json
+10 -10
@@ -72,7 +72,7 @@
72 "echarts": "^5.4.3",
73 "geojson": "^0.5.0",
74 "highlight.js": "^11.9.0",
75 - "jose": "^5.0.1",
75 + "jose": "^5.1.0",
76 "lodash": "^4.17.21",
77 "maplibre-gl": "^3.5.2",
78 "mitt": "^3.0.1",
@@ -85,12 +85,12 @@
85 "shepherd.js": "^11.2.0",
86 "v-calendar": "^3.1.2",
87 "validator": "^13.11.0",
88 - "vue": "^3.3.7",
88 + "vue": "^3.3.8",
89 "vue-advanced-cropper": "^2.8.8",
90 "vue-cal": "^4.8.1",
91 "vue-chartjs": "^5.2.0",
92 "vue-highlight-words": "^3.0.1",
93 - "vue-i18n": "^9.6.2",
93 + "vue-i18n": "^9.6.5",
94 "vue-maplibre-gl": "^3.0.3",
95 "vue-router": "^4.2.5",
96 "vue-sjv": "^0.0.6",
@@ -106,12 +106,12 @@
106 "@iconify/vue": "^4.1.1",
107 "@rushstack/eslint-patch": "^1.5.1",
108 "@tsconfig/node18": "^18.2.2",
109 - "@types/bytes": "^3.1.3",
109 + "@types/bytes": "^3.1.4",
110 "@types/fs-extra": "^11.0.3",
111 "@types/inquirer": "^9.0.6",
112 - "@types/jsdom": "^21.1.4",
112 + "@types/jsdom": "^21.1.5",
113 "@types/lodash": "^4.14.200",
114 - "@types/node": "^20.8.9",
114 + "@types/node": "^20.8.10",
115 "@types/validator": "^13.11.5",
116 "@vitejs/plugin-vue": "^4.4.0",
117 "@vitejs/plugin-vue-jsx": "^3.0.2",
@@ -121,8 +121,8 @@
121 "@vue/test-utils": "^2.4.1",
122 "@vue/tsconfig": "^0.4.0",
123 "autoprefixer": "^10.4.16",
124 - "cypress": "^13.3.3",
125 - "eslint": "^8.52.0",
124 + "cypress": "^13.4.0",
125 + "eslint": "^8.53.0",
126 "eslint-plugin-cypress": "^2.15.1",
127 "eslint-plugin-vue": "^9.18.1",
128 "fs-extra": "^11.1.1",
@@ -134,8 +134,8 @@
134 "postcss": "^8.4.31",
135 "prettier": "^3.0.3",
136 "sass": "^1.69.5",
137 - "start-server-and-test": "^2.0.1",
138 - "tailwind-config-viewer": "^1.7.2",
137 + "start-server-and-test": "^2.0.2",
138 + "tailwind-config-viewer": "^1.7.3",
139 "tailwindcss": "^3.3.5",
140 "taze": "^0.12.0",
141 "ts-node": "^10.9.1",
src/api/alerts.ts new
+97
@@ -0,0 +1,97 @@
1 +import { HttpClient } from "./httpClient"
2 +import type { FlaskBaseResponse } from "@/types/flask.d"
3 +import type { AlertsByHost, AlertsByRule, AlertsByRulePerHost, AlertsSummary } from "@/types/alerts.d"
4 +
5 +export type AlertsQueryTimeRange = `${number}${"h" | "d" | "w"}`
6 +
7 +interface AlertsQuery {
8 + size: number
9 + timerange: AlertsQueryTimeRange
10 + alert_field?: string
11 + alert_value?: string
12 + timestamp_field: "timestamp_utc"
13 + agent_name?: string
14 + index_name?: string
15 +}
16 +
17 +export interface AlertsSummaryQuery {
18 + agentHostname?: string
19 + indexName?: string
20 + maxAlerts?: number
21 + timerange?: AlertsQueryTimeRange
22 + alertField?: string
23 + alertValue?: string
24 +}
25 +
26 +function getQueryByFilter(filter?: AlertsSummaryQuery): AlertsQuery {
27 + const query: AlertsQuery = {
28 + size: filter?.maxAlerts || 10,
29 + timerange: filter?.timerange || "24h",
30 + timestamp_field: "timestamp_utc"
31 + }
32 +
33 + filter?.agentHostname && (query.agent_name = filter.agentHostname)
34 + filter?.indexName && (query.index_name = filter.indexName)
35 +
36 + if (filter?.alertField && filter?.alertValue) {
37 + query.alert_field = filter.alertField
38 + query.alert_value = filter.alertValue
39 + }
40 +
41 + return query
42 +}
43 +
44 +export default {
45 + getAll(filter?: AlertsSummaryQuery, signal?: AbortSignal) {
46 + const query = getQueryByFilter(filter)
47 +
48 + let url = "/alerts"
49 +
50 + if (filter?.agentHostname) {
51 + url = "/alerts/host"
52 + }
53 + if (filter?.indexName) {
54 + url = "/alerts/index"
55 + }
56 +
57 + return HttpClient.post<FlaskBaseResponse & { alerts_summary: AlertsSummary[] }>(
58 + url,
59 + query,
60 + signal ? { signal } : {}
61 + )
62 + },
63 + getCountByHost(filter?: AlertsSummaryQuery, signal?: AbortSignal) {
64 + const query = getQueryByFilter(filter)
65 + return HttpClient.post<FlaskBaseResponse & { alerts_by_host: AlertsByHost[] }>(
66 + `/alerts/hosts/all`,
67 + query,
68 + signal ? { signal } : {}
69 + )
70 + },
71 + getCountByRule(filter?: AlertsSummaryQuery, signal?: AbortSignal) {
72 + const query = getQueryByFilter(filter)
73 + return HttpClient.post<FlaskBaseResponse & { alerts_by_rule: AlertsByRule[] }>(
74 + `/alerts/rules/all`,
75 + query,
76 + signal ? { signal } : {}
77 + )
78 + },
79 + getCountByRuleHost(filter?: AlertsSummaryQuery, signal?: AbortSignal) {
80 + const query = getQueryByFilter(filter)
81 + return HttpClient.post<FlaskBaseResponse & { alerts_by_rule_per_host: AlertsByRulePerHost[] }>(
82 + `/alerts/rules/hosts/all`,
83 + query,
84 + signal ? { signal } : {}
85 + )
86 + },
87 + create(indexName: string, alertId: string) {
88 + const body = {
89 + index_name: indexName,
90 + alert_id: alertId
91 + }
92 + return HttpClient.post<FlaskBaseResponse & { alert_id: number; alert_url: string }>(
93 + `/soc/general_alert/create`,
94 + body
95 + )
96 + }
97 +}
src/api/httpClient.ts
+1 -1
@@ -23,7 +23,7 @@ HttpClient.interceptors.request.use(
23 __TOKEN_REFRESHING = true
24 __TOKEN_LAST_CHECK = new Date()
25
26 - store.refreshToken().then(res => {
26 + store.refreshToken().then(() => {
27 __TOKEN_REFRESHING = false
28 })
29 }
src/api/index.ts
+2
@@ -2,6 +2,7 @@ import connectors from "./connectors"
2 import indices from "./indices"
3 import agents from "./agents"
4 import graylog from "./graylog"
5 +import alerts from "./alerts"
6 import auth from "./auth"
7
8 export default {
@@ -9,5 +10,6 @@ export default {
10 indices,
11 agents,
12 graylog,
13 + alerts,
14 auth
15 }
src/assets/scss/helpers.scss
+8
@@ -34,3 +34,11 @@
34 .font-mono {
35 font-family: var(--font-family-mono);
36 }
37 +
38 +.text-primary-color {
39 + color: var(--primary-color);
40 +}
41 +
42 +.text-secondary-color {
43 + color: var(--fg-secondary-color);
44 +}
src/assets/scss/vuesjv-override.scss
+6
@@ -55,4 +55,10 @@
55 background-color: transparent;
56 }
57 }
58 +
59 + .flex.flex-col > .flex {
60 + & > div:first-child {
61 + white-space: nowrap;
62 + }
63 + }
64 }
src/components/AuthForm/index.vue
+9 -5
@@ -1,9 +1,6 @@
1 <template>
2 <div class="form-wrap">
3 - <div class="logo mb-4">
4 - <img src="@/assets/images/socfortress_logo.svg?url" v-if="isDark" />
5 - <img src="@/assets/images/socfortress_logo.svg?url" v-else />
6 - </div>
3 + <Logo mini :dark="isDark" class="mb-4" />
4 <div class="title mb-4">{{ title }}</div>
5 <div class="text mb-12">
6 Today is a new day. It's your day. You shape it. Sign in to start managing your projects.
@@ -38,7 +35,8 @@ import { useThemeStore } from "@/stores/theme"
35 import SignIn from "./SignIn.vue"
36 import ForgotPassword from "./ForgotPassword.vue"
37 import SignUp from "./SignUp.vue"
41 -import { NButton, NDivider } from "naive-ui"
38 +import Logo from "@/layouts/common/Logo.vue"
39 +import { NButton } from "naive-ui"
40 import { ref, onBeforeMount, computed } from "vue"
41
42 export type FormType = "signin" | "signup" | "forgotpassword"
@@ -77,6 +75,12 @@ onBeforeMount(() => {
75 min-width: 270px;
76 max-width: 400px;
77
78 + .logo {
79 + :deep(img) {
80 + max-height: 37px;
81 + }
82 + }
83 +
84 .title {
85 font-size: 36px;
86 font-family: var(--font-family-display);
src/components/agents/AgentCard.vue
-18
@@ -135,25 +135,7 @@ function toggleCritical(agentId: string, criticalStatus: boolean) {
135 max-width: 100%;
136 box-sizing: border-box;
137 cursor: pointer;
138 - opacity: 0;
138 transition: all 0.3s;
140 - animation: agent-card-fade 0.3s forwards;
141 -
142 - @for $i from 0 through 20 {
143 - &:nth-child(#{$i}) {
144 - animation-delay: $i * 0.05s;
145 - }
146 - }
147 -
148 - @keyframes agent-card-fade {
149 - from {
150 - opacity: 0;
151 - transform: translateY(10px);
152 - }
153 - to {
154 - opacity: 1;
155 - }
156 - }
139
140 .wrapper {
141 display: flex;
src/components/agents/AgentToolbar.vue
+4 -4
@@ -13,7 +13,7 @@
13 <div class="search-info">
14 <strong v-if="agentsFilteredLength !== agentsLength">{{ agentsFilteredLength }}</strong>
15 <span class="mh-5" v-if="agentsFilteredLength !== agentsLength">/</span>
16 - <strong>{{ agentsLength }}</strong>
16 + <strong class="font-mono">{{ agentsLength }}</strong>
17 Agents
18 </div>
19 </div>
@@ -23,7 +23,7 @@
23 <div class="agents-critical-list" v-if="agentsCritical?.length">
24 <div class="title">
25 Critical Assets
26 - <small class="opacity-50">({{ agentsCritical.length }})</small>
26 + <small class="text-secondary-color font-mono">({{ agentsCritical.length }})</small>
27 </div>
28 <div class="list">
29 <div
@@ -39,7 +39,7 @@
39 <div class="agents-online-list" v-if="agentsOnline?.length">
40 <div class="title">
41 Online Agents
42 - <small class="opacity-50">({{ agentsOnline.length }})</small>
42 + <small class="text-secondary-color font-mono">({{ agentsOnline.length }})</small>
43 </div>
44 <div class="list">
45 <div
@@ -104,7 +104,7 @@ const textFilter = computed<string>({
104 height: 100%;
105
106 .search-info {
107 - opacity: 0.5;
107 + color: var(--fg-secondary-color);
108 }
109 .agents-list {
110 .title {
src/components/alerts/Alert.vue new
+339
@@ -0,0 +1,339 @@
1 +<template>
2 + <div class="alert-details flex flex-col gap-2 px-5 py-4">
3 + <div class="header-box flex justify-between">
4 + <div class="id flex items-center gap-2 cursor-pointer" @click="showDetails = true">
5 + <span>#{{ alert._id }}</span>
6 + <Icon :name="InfoIcon" :size="16"></Icon>
7 + </div>
8 + <div class="time">
9 + {{ formatDate(alert._source.timestamp_utc) }}
10 + </div>
11 + </div>
12 + <div class="main-box flex justify-between gap-4">
13 + <div class="content">
14 + <div class="rule-description">{{ alert._source.rule_description }}</div>
15 + <div class="rule-groups">{{ alert._source.rule_groups }}</div>
16 +
17 + <div class="badges-box flex flex-wrap items-center gap-3">
18 + <!--
19 + <div class="badge cursor">
20 + <Icon :name="InfoIcon" :size="14"></Icon>
21 + </div>
22 + -->
23 + <div class="badge splitted">
24 + <span class="flex items-center gap-2">
25 + <Icon :name="TargetIcon" :size="13" class="!opacity-80"></Icon>
26 + Fired times
27 + </span>
28 + <span class="font-mono">{{ alert._source.rule_firedtimes }}</span>
29 + </div>
30 + <div class="badge" :class="{ active: alert._source.rule_mail }">
31 + <span>Rule mail</span>
32 + <Icon :name="alert._source.rule_mail ? MailIcon : DisabledIcon" :size="14"></Icon>
33 + </div>
34 + <n-popover overlap placement="bottom-start">
35 + <template #trigger>
36 + <div class="badge splitted cursor-help">
37 + <span class="flex items-center gap-2">
38 + <Icon :name="AgentIcon" :size="13" class="!opacity-80"></Icon>
39 + Agent
40 + </span>
41 + <span>{{ alert._source.agent_name }} / {{ alert._source.agent_labels_customer }}</span>
42 + </div>
43 + </template>
44 + <div class="flex flex-col gap-1">
45 + <div class="box">
46 + agent_id:
47 + <code
48 + class="cursor-pointer text-primary-color"
49 + @click="gotoAgentPage(alert._source.agent_id)"
50 + >
51 + {{ alert._source.agent_id }}
52 + </code>
53 + </div>
54 + <div class="box">
55 + agent_ip:
56 + <code>{{ alert._source.agent_ip }}</code>
57 + </div>
58 + <div class="box">
59 + agent_name:
60 + <code>{{ alert._source.agent_name }}</code>
61 + </div>
62 + <div class="box">
63 + agent_labels_customer:
64 + <code>{{ alert._source.agent_labels_customer }}</code>
65 + </div>
66 + </div>
67 + </n-popover>
68 + <div class="badge splitted">
69 + <span>syslog</span>
70 + <span>{{ alert._source.syslog_type }} / {{ alert._source.syslog_level }}</span>
71 + </div>
72 + <div class="badge splitted hide-on-small">
73 + <span>manager</span>
74 + <span>{{ alert._source.manager_name }}</span>
75 + </div>
76 + <div class="badge splitted hide-on-small">
77 + <span>decoder</span>
78 + <span>{{ alert._source.decoder_name }}</span>
79 + </div>
80 + <div class="badge splitted hide-on-small">
81 + <span>source</span>
82 + <span>{{ alert._source.source }}</span>
83 + </div>
84 + </div>
85 + </div>
86 + <div class="actions-box flex flex-col justify-end">
87 + <n-button type="primary" secondary v-if="alertUrl" tag="a" :href="alertUrl" target="_blank">
88 + <template #icon><Icon :name="ViewIcon"></Icon></template>
89 + View Alert
90 + </n-button>
91 + <n-button :loading="loading" type="warning" secondary @click="createAlert()" v-else>
92 + <template #icon><Icon :name="DangerIcon"></Icon></template>
93 + Create SOC Alert
94 + </n-button>
95 + </div>
96 + </div>
97 + <div class="footer-box flex justify-between items-center gap-4">
98 + <div class="actions-box flex flex-col justify-end">
99 + <n-button
100 + type="primary"
101 + secondary
102 + size="small"
103 + v-if="alertUrl"
104 + tag="a"
105 + :href="alertUrl"
106 + target="_blank"
107 + >
108 + <template #icon><Icon :name="ViewIcon"></Icon></template>
109 + View Alert
110 + </n-button>
111 + <n-button :loading="loading" type="warning" secondary size="small" @click="createAlert()" v-else>
112 + <template #icon><Icon :name="DangerIcon"></Icon></template>
113 + Create SOC Alert
114 + </n-button>
115 + </div>
116 +
117 + <div class="time">{{ formatDate(alert._source.timestamp_utc) }}</div>
118 + </div>
119 +
120 + <n-modal
121 + v-model:show="showDetails"
122 + preset="card"
123 + :style="{ maxWidth: 'min(800px, 90vw)', overflow: 'hidden' }"
124 + :title="`Alert: ${alert._id}`"
125 + :bordered="false"
126 + segmented
127 + >
128 + <SimpleJsonViewer class="vuesjv-override" :model-value="alert._source" :initialExpandedDepth="2" />
129 + </n-modal>
130 + </div>
131 +</template>
132 +
133 +<script setup lang="ts">
134 +import { NButton, NPopover, NModal } from "naive-ui"
135 +import { useSettingsStore } from "@/stores/settings"
136 +import dayjs from "@/utils/dayjs"
137 +import Icon from "@/components/common/Icon.vue"
138 +import type { Alert } from "@/types/alerts.d"
139 +import { SimpleJsonViewer } from "vue-sjv"
140 +import "@/assets/scss/vuesjv-override.scss"
141 +import { useRouter } from "vue-router"
142 +import Api from "@/api"
143 +import { onBeforeMount, ref } from "vue"
144 +import { useMessage } from "naive-ui/lib"
145 +
146 +const { alert } = defineProps<{ alert: Alert }>()
147 +
148 +const InfoIcon = "carbon:information"
149 +const TargetIcon = "zondicons:target"
150 +const DangerIcon = "majesticons:exclamation-line"
151 +const DisabledIcon = "ph:minus-bold"
152 +const MailIcon = "carbon:email"
153 +const AgentIcon = "carbon:police"
154 +const ViewIcon = "iconoir:eye-alt"
155 +
156 +const message = useMessage()
157 +const router = useRouter()
158 +const loading = ref(false)
159 +const showDetails = ref(false)
160 +const dFormats = useSettingsStore().dateFormat
161 +
162 +const alertUrl = ref("")
163 +
164 +function formatDate(timestamp: string): string {
165 + return dayjs(timestamp).format(dFormats.datetimesec)
166 +}
167 +
168 +function gotoAgentPage(agentId: string) {
169 + router.push(`/agent/${agentId}`).catch(() => {})
170 +}
171 +
172 +function createAlert() {
173 + loading.value = true
174 +
175 + Api.alerts
176 + .create(alert._index, alert._id)
177 + .then(res => {
178 + if (res.data.success) {
179 + res.data.alert_url && (alertUrl.value = res.data.alert_url)
180 + message.success(res.data?.message || "SOC Alert created.")
181 + } else {
182 + message.warning(res.data?.message || "An error occurred. Please try again later.")
183 + }
184 + })
185 + .catch(err => {
186 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
187 + })
188 + .finally(() => {
189 + loading.value = false
190 + })
191 +}
192 +
193 +onBeforeMount(() => {
194 + alert._source.alert_url && (alertUrl.value = alert._source.alert_url)
195 +})
196 +</script>
197 +
198 +<style lang="scss" scoped>
199 +.alert-details {
200 + transition: all 0.2s var(--bezier-ease);
201 +
202 + .header-box {
203 + font-family: var(--font-family-mono);
204 + font-size: 13px;
205 + .id {
206 + word-break: break-word;
207 + color: var(--fg-secondary-color);
208 + line-height: 1;
209 +
210 + &:hover {
211 + color: var(--primary-color);
212 + }
213 + }
214 + .time {
215 + color: var(--fg-secondary-color);
216 + }
217 + }
218 + .main-box {
219 + .content {
220 + word-break: break-word;
221 +
222 + .rule-groups {
223 + color: var(--fg-secondary-color);
224 + font-size: 13px;
225 + }
226 +
227 + .badges-box {
228 + margin-top: 16px;
229 +
230 + .badge {
231 + border-radius: var(--border-radius);
232 + border: var(--border-small-100);
233 + display: flex;
234 + align-items: center;
235 + font-size: 14px;
236 + padding: 0px 6px;
237 + height: 26px;
238 + line-height: 1;
239 + gap: 6px;
240 + transition: all 0.3s var(--bezier-ease);
241 +
242 + span,
243 + i {
244 + opacity: 0.5;
245 + }
246 +
247 + &.active {
248 + color: var(--primary-color);
249 + background-color: var(--primary-005-color);
250 +
251 + span,
252 + i {
253 + opacity: 1;
254 + }
255 +
256 + border-color: var(--primary-color);
257 + }
258 +
259 + &.cursor {
260 + cursor: pointer;
261 +
262 + i {
263 + opacity: 1;
264 + }
265 +
266 + &:hover {
267 + color: var(--primary-color);
268 + border-color: var(--primary-color);
269 + }
270 + }
271 +
272 + &.splitted {
273 + padding: 0px;
274 + gap: 0;
275 + overflow: hidden;
276 +
277 + span {
278 + padding: 0px 8px;
279 + height: 100%;
280 + line-height: 24px;
281 + opacity: 1;
282 +
283 + &:first-child {
284 + border-right: var(--border-small-100);
285 + background-color: var(--primary-005-color);
286 + }
287 + }
288 + }
289 + &.default {
290 + span,
291 + i {
292 + opacity: 1;
293 + }
294 + }
295 + }
296 + }
297 + }
298 + }
299 + .footer-box {
300 + display: none;
301 + font-size: 13px;
302 + margin-top: 10px;
303 +
304 + .time {
305 + font-family: var(--font-family-mono);
306 + color: var(--fg-secondary-color);
307 + text-align: right;
308 + }
309 + }
310 +
311 + &:hover {
312 + background-color: var(--primary-005-color);
313 + }
314 +
315 + @container (max-width: 650px) {
316 + .header-box {
317 + .time {
318 + display: none;
319 + }
320 + }
321 + .main-box {
322 + .actions-box {
323 + display: none;
324 + }
325 +
326 + .badges-box {
327 + .badge {
328 + &.hide-on-small {
329 + display: none;
330 + }
331 + }
332 + }
333 + }
334 + .footer-box {
335 + display: flex;
336 + }
337 + }
338 +}
339 +</style>
src/components/alerts/AlertsFilters.vue new
+147
@@ -0,0 +1,147 @@
1 +<template>
2 + <div class="alerts-filters flex flex-col gap-2">
3 + <div class="flex gap-2">
4 + <n-form-item label="Filter key/value" class="grow">
5 + <n-input-group>
6 + <n-select
7 + v-model:value="filters.alertField"
8 + :options="alertFieldOptions"
9 + filterable
10 + clearable
11 + tag
12 + :render-tag="renderFieldTag"
13 + :render-label="renderFieldLabel"
14 + placeholder="Alert Field"
15 + class="basis-1/2"
16 + >
17 + <template #action>
18 + <n-button @click="clearFieldsHistory()" size="tiny" quaternary class="!w-full">
19 + <template #icon>
20 + <Icon :name="ClearIcon"></Icon>
21 + </template>
22 + Clear history
23 + </n-button>
24 + </template>
25 + <template #empty>
26 + <n-empty description="Empty Field history" class="text-center"></n-empty>
27 + </template>
28 + </n-select>
29 + <n-input v-model:value="filters.alertValue" clearable placeholder="Field value" class="basis-1/2" />
30 + </n-input-group>
31 + </n-form-item>
32 + </div>
33 + <slot />
34 +
35 + <div class="flex gap-2">
36 + <n-form-item label="Alerts for group" class="basis-1/2">
37 + <n-select v-model:value="filters.maxAlerts" :options="maxAlertsOptions" />
38 + </n-form-item>
39 + <n-form-item label="Time range" class="basis-1/2">
40 + <n-select v-model:value="filters.timerange" :options="timerangeOptions" />
41 + </n-form-item>
42 + </div>
43 +
44 + <div class="flex justify-end">
45 + <n-button strong secondary type="primary" @click="emit('search')">
46 + <template #icon>
47 + <Icon :name="SearchIcon"></Icon>
48 + </template>
49 + Search
50 + </n-button>
51 + </div>
52 + </div>
53 +</template>
54 +
55 +<script setup lang="ts">
56 +import { onBeforeMount, toRefs, watch, type VNodeChild, h } from "vue"
57 +import { NSelect, NButton, NInput, NInputGroup, NEmpty, NFormItem, type SelectOption } from "naive-ui"
58 +import Icon from "@/components/common/Icon.vue"
59 +import { useStorage } from "@vueuse/core"
60 +import _uniqBy from "lodash/uniqBy"
61 +import type { AlertsQueryTimeRange, AlertsSummaryQuery } from "@/api/alerts"
62 +
63 +const props = defineProps<{ filters: AlertsSummaryQuery }>()
64 +const { filters } = toRefs(props)
65 +
66 +const emit = defineEmits<{
67 + (e: "search"): void
68 +}>()
69 +
70 +const ClearIcon = "mdi:broom"
71 +const SearchIcon = "carbon:search"
72 +
73 +const timerangeOptions: { label: string; value: AlertsQueryTimeRange }[] = [
74 + { label: "1 Hour", value: "1h" },
75 + { label: "6 Hours", value: "6h" },
76 + { label: "12 Hours", value: "12h" },
77 + { label: "1 Day", value: "1d" },
78 + { label: "2 Day", value: "2d" },
79 + { label: "5 Day", value: "5d" },
80 + { label: "1 Week", value: "1w" },
81 + { label: "2 Week", value: "2w" },
82 + { label: "3 Week", value: "3w" },
83 + { label: "4 Week", value: "4w" }
84 +]
85 +
86 +const maxAlertsOptions = [
87 + { label: "1 Alert", value: 1 },
88 + { label: "5 Alert", value: 5 },
89 + { label: "10 Alert", value: 10 },
90 + { label: "20 Alert", value: 20 }
91 +]
92 +
93 +const alertFieldOptions = useStorage<{ label: string; value: string }[]>("alert-fields-history", [], localStorage)
94 +
95 +function clearFieldsHistory(field?: string) {
96 + if (!field) {
97 + alertFieldOptions.value = []
98 + } else {
99 + alertFieldOptions.value = alertFieldOptions.value.filter(o => o.label !== field)
100 + }
101 +}
102 +
103 +function renderFieldTag({ option }: { option: SelectOption; handleClose: () => void }): VNodeChild {
104 + return h("div", {}, [option.label as string])
105 +}
106 +
107 +function renderFieldLabel(option: SelectOption): VNodeChild {
108 + if (option.type === "group") return option.label + "(Cool!)"
109 + return [
110 + h(Icon, {
111 + style: {
112 + verticalAlign: "-0.20em",
113 + marginRight: "4px",
114 + opacity: 0.6
115 + },
116 + name: `carbon:close`,
117 + onClick(e: Event) {
118 + e.stopImmediatePropagation()
119 + e.stopPropagation()
120 + clearFieldsHistory(option.label?.toString())
121 + }
122 + }),
123 + option.label?.toString()
124 + ]
125 +}
126 +
127 +watch(
128 + () => filters.value.alertField,
129 + val => {
130 + if (val) {
131 + alertFieldOptions.value = _uniqBy(
132 + [...JSON.parse(JSON.stringify(alertFieldOptions.value)), { label: val, value: val }],
133 + o => o.label
134 + )
135 + }
136 + }
137 +)
138 +
139 +onBeforeMount(() => {
140 + if (!filters.value.timerange) {
141 + filters.value.timerange = timerangeOptions[3].value
142 + }
143 + if (!filters.value.maxAlerts) {
144 + filters.value.maxAlerts = maxAlertsOptions[2].value
145 + }
146 +})
147 +</script>
src/components/alerts/AlertsList.vue new
+359
@@ -0,0 +1,359 @@
1 +<template>
2 + <div class="alerts-list">
3 + <div class="header flex items-center justify-end gap-2">
4 + <div class="info grow flex gap-5">
5 + <n-popover overlap placement="bottom-start">
6 + <template #trigger>
7 + <div class="bg-color border-radius">
8 + <n-button size="small" class="!cursor-help">
9 + <template #icon>
10 + <Icon :name="InfoIcon"></Icon>
11 + </template>
12 + </n-button>
13 + </div>
14 + </template>
15 + <div class="flex flex-col gap-2">
16 + <div class="box">
17 + Total Summaries:
18 + <code>{{ totalAlertsSummary }}</code>
19 + </div>
20 + <div class="box">
21 + Total Alerts:
22 + <code>{{ totalAlerts }}</code>
23 + </div>
24 + </div>
25 + </n-popover>
26 + </div>
27 + <div class="actions flex gap-2 items-center">
28 + <n-button size="small" @click="showStatsDrawer = true" v-if="!isFilterPreselected">
29 + <template #icon>
30 + <Icon :name="StatsIcon" :size="14"></Icon>
31 + </template>
32 + Stats
33 + </n-button>
34 + <n-button size="small" @click="showFiltersDrawer = true">
35 + <template #icon>
36 + <Icon :name="FilterIcon" :size="15"></Icon>
37 + </template>
38 + Filters
39 + </n-button>
40 + </div>
41 + </div>
42 + <n-spin :show="loading">
43 + <template #description>Alerts are being fetched, this may take up to 1 minute.</template>
44 +
45 + <div class="list my-3">
46 + <template v-if="alertsSummaryList.length">
47 + <AlertsSummaryItem
48 + v-for="alertsSummary of alertsSummaryList"
49 + :key="alertsSummary.index_name"
50 + :alertsSummary="alertsSummary"
51 + class="mb-2"
52 + />
53 + </template>
54 + <template v-else>
55 + <n-empty description="No items found" v-if="!loading" />
56 + </template>
57 + </div>
58 + </n-spin>
59 +
60 + <n-drawer
61 + v-model:show="showStatsDrawer"
62 + :width="700"
63 + style="max-width: 90vw"
64 + :trap-focus="false"
65 + display-directive="show"
66 + >
67 + <n-drawer-content title="Alerts stats" closable body-content-style="padding:0" :native-scrollbar="false">
68 + <AlertsStats :filters="filters" @mounted="alertsStatsCTX = $event" />
69 + </n-drawer-content>
70 + </n-drawer>
71 +
72 + <n-drawer
73 + v-model:show="showFiltersDrawer"
74 + display-directive="show"
75 + :trap-focus="false"
76 + style="max-width: 90vw; width: 500px"
77 + :show-mask="loadingFilters ? 'transparent' : undefined"
78 + :class="{ 'opacity-0': loadingFilters }"
79 + >
80 + <n-drawer-content title="Alerts filters" closable :native-scrollbar="false">
81 + <AlertsFilters :filters="filters" @search="startSearch(true)">
82 + <n-form-item label="Agent" v-if="!isFilterPreselected">
83 + <n-select
84 + v-model:value="filters.agentHostname"
85 + :options="agentHostnameOptions"
86 + placeholder="Agents list"
87 + clearable
88 + filterable
89 + :loading="loadingAgents"
90 + />
91 + </n-form-item>
92 + <n-form-item label="Index" v-if="!isFilterPreselected">
93 + <n-select
94 + v-model:value="filters.indexName"
95 + :options="indexNameOptions"
96 + clearable
97 + filterable
98 + placeholder="Indices list"
99 + :loading="loadingIndex"
100 + />
101 + </n-form-item>
102 + </AlertsFilters>
103 + </n-drawer-content>
104 + </n-drawer>
105 + </div>
106 +</template>
107 +
108 +<script setup lang="ts">
109 +import { ref, onBeforeMount, toRefs, computed, nextTick, onMounted } from "vue"
110 +import { useMessage, NSpin, NPopover, NButton, NEmpty, NDrawer, NDrawerContent, NFormItem, NSelect } from "naive-ui"
111 +import Api from "@/api"
112 +import AlertsStats, { type AlertsStatsCTX } from "./AlertsStats.vue"
113 +import AlertsFilters from "./AlertsFilters.vue"
114 +import AlertsSummaryItem, { type AlertsSummaryExt } from "./AlertsSummary.vue"
115 +import Icon from "@/components/common/Icon.vue"
116 +import type { AlertsSummaryQuery } from "@/api/alerts"
117 +// import { alerts_summary } from "./mock"
118 +// import type { AlertsSummary } from "@/types/alerts"
119 +import type { IndexStats } from "@/types/indices.d"
120 +import axios from "axios"
121 +import type { Agent } from "@/types/agents.d"
122 +import { onBeforeUnmount } from "vue"
123 +
124 +const props = defineProps<{ agentHostname?: string; indexName?: string }>()
125 +const { agentHostname, indexName } = toRefs(props)
126 +
127 +const message = useMessage()
128 +const loadingIndex = ref(false)
129 +const loadingAgents = ref(false)
130 +const loading = ref(false)
131 +const indices = ref<IndexStats[]>([])
132 +const agents = ref<Agent[]>([])
133 +const alertsSummaryList = ref<AlertsSummaryExt[]>([])
134 +const loadingFilters = ref(true)
135 +const showFiltersDrawer = ref(true)
136 +const showStatsDrawer = ref(false)
137 +let abortController: AbortController | null = null
138 +
139 +const InfoIcon = "carbon:information"
140 +const FilterIcon = "carbon:filter-edit"
141 +const StatsIcon = "carbon:chart-column"
142 +
143 +const alertsStatsCTX = ref<AlertsStatsCTX | null>(null)
144 +
145 +const totalAlertsSummary = computed<number>(() => {
146 + return alertsSummaryList.value.length || 0
147 +})
148 +const totalAlerts = computed<number>(() => {
149 + return alertsSummaryList.value.reduce((acc: number, val: AlertsSummaryExt) => {
150 + return acc + val.alerts.length
151 + }, 0)
152 +})
153 +
154 +const filters = ref<AlertsSummaryQuery>({})
155 +
156 +const isFilterPreselected = computed(() => {
157 + return !!agentHostname?.value || !!indexName?.value
158 +})
159 +
160 +const agentHostnameOptions = computed(() => {
161 + if (agentHostname?.value) {
162 + return [{ value: agentHostname.value, label: agentHostname.value }]
163 + }
164 + return (agents.value || []).map(o => ({ value: o.hostname, label: o.hostname }))
165 +})
166 +
167 +const indexNameOptions = computed(() => {
168 + if (indexName?.value) {
169 + return [{ value: indexName.value, label: indexName.value }]
170 + }
171 + return (indices.value || []).map(o => ({ value: o.index, label: o.index }))
172 +})
173 +
174 +function addIndexInfo() {
175 + if (indices.value?.length && alertsSummaryList.value.length) {
176 + for (const alert of alertsSummaryList.value) {
177 + const index = indices.value.find(o => o.index === alert.index_name)
178 + alert.indexStats = index
179 + }
180 + }
181 +}
182 +
183 +function getData() {
184 + loading.value = true
185 +
186 + abortController = new AbortController()
187 +
188 + Api.alerts
189 + .getAll(filters.value, abortController.signal)
190 + .then(res => {
191 + alertsSummaryList.value = res.data?.alerts_summary || []
192 +
193 + if (res.data.success) {
194 + nextTick(() => {
195 + addIndexInfo()
196 + })
197 + } else {
198 + message.warning(res.data?.message || "An error occurred. Please try again later.")
199 + }
200 + })
201 + .catch(err => {
202 + if (!axios.isCancel(err)) {
203 + alertsSummaryList.value = []
204 +
205 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
206 + }
207 + })
208 + .finally(() => {
209 + loading.value = false
210 + })
211 +}
212 +
213 +function getIndices() {
214 + loadingIndex.value = true
215 +
216 + Api.indices
217 + .getIndices()
218 + .then(res => {
219 + if (res.data.success) {
220 + indices.value = res.data.indices_stats || []
221 +
222 + nextTick(() => {
223 + addIndexInfo()
224 + })
225 + } else {
226 + message.error(res.data?.message || "An error occurred. Please try again later.")
227 + }
228 + })
229 + .catch(err => {
230 + if (err.response?.status === 401) {
231 + message.error(
232 + err.response?.data?.message ||
233 + "Wazuh-Indexer returned Unauthorized. Please check your connector credentials."
234 + )
235 + } else if (err.response?.status === 404) {
236 + message.error(err.response?.data?.message || "No indices were found.")
237 + } else {
238 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
239 + }
240 + })
241 + .finally(() => {
242 + loadingIndex.value = false
243 + })
244 +}
245 +
246 +function getAgents() {
247 + loadingAgents.value = true
248 +
249 + Api.agents
250 + .getAgents()
251 + .then(res => {
252 + if (res.data.success) {
253 + agents.value = res.data.agents || []
254 + } else {
255 + message.error(res.data?.message || "An error occurred. Please try again later.")
256 + }
257 + })
258 + .catch(err => {
259 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
260 + })
261 + .finally(() => {
262 + loadingAgents.value = false
263 + })
264 +}
265 +
266 +function getStatsFiltersString() {
267 + return [filters.value.alertField, filters.value.alertValue, filters.value.maxAlerts, filters.value.timerange].join(
268 + ","
269 + )
270 +}
271 +
272 +let lastStatsFilters = ""
273 +
274 +function startSearch(closeDrawer?: boolean) {
275 + cancelSearch()
276 +
277 + setTimeout(() => {
278 + getData()
279 +
280 + const statsFiltersString = getStatsFiltersString()
281 + if (lastStatsFilters !== statsFiltersString) {
282 + alertsStatsCTX.value?.startSearch()
283 + lastStatsFilters = statsFiltersString
284 + }
285 + }, 200)
286 +
287 + if (closeDrawer) {
288 + showFiltersDrawer.value = false
289 + }
290 +}
291 +
292 +function cancelSearch() {
293 + abortController?.abort()
294 +}
295 +
296 +onBeforeMount(() => {
297 + if (agentHostname?.value) {
298 + filters.value.agentHostname = agentHostname.value
299 + }
300 + if (indexName?.value) {
301 + filters.value.indexName = indexName.value
302 + }
303 +
304 + getIndices()
305 + getAgents()
306 +
307 + // alertsSummaryList.value = alerts_summary as AlertsSummary[]
308 + startSearch()
309 +})
310 +
311 +onMounted(() => {
312 + showFiltersDrawer.value = false
313 +
314 + setTimeout(() => {
315 + loadingFilters.value = false
316 + }, 1000)
317 +})
318 +
319 +onBeforeUnmount(() => {
320 + cancelSearch()
321 +})
322 +</script>
323 +
324 +<style lang="scss" scoped>
325 +.alerts-list {
326 + :deep() {
327 + .n-spin-body {
328 + top: 100px;
329 + text-align: center;
330 + width: 80%;
331 + }
332 + }
333 + .list {
334 + container-type: inline-size;
335 + min-height: 200px;
336 +
337 + .alert-summary {
338 + animation: alert-summary-fade 0.3s forwards;
339 + opacity: 0;
340 +
341 + @for $i from 0 through 20 {
342 + &:nth-child(#{$i}) {
343 + animation-delay: $i * 0.05s;
344 + }
345 + }
346 +
347 + @keyframes alert-summary-fade {
348 + from {
349 + opacity: 0;
350 + transform: translateY(10px);
351 + }
352 + to {
353 + opacity: 1;
354 + }
355 + }
356 + }
357 + }
358 +}
359 +</style>
src/components/alerts/AlertsStats.vue new
+231
@@ -0,0 +1,231 @@
1 +<template>
2 + <div class="alerts-stats">
3 + <n-tabs default-value="countByHost" animated justify-content="space-evenly" type="line">
4 + <n-tab-pane name="countByHost" tab="By Host">
5 + <n-spin :show="loadingCountByHost">
6 + <template #description>Alerts are being fetched, this may take up to 1 minute.</template>
7 +
8 + <div class="list">
9 + <template v-if="countByHost.length">
10 + <AlertsStatsItem
11 + v-for="summary of countByHost"
12 + :key="summary.agent_name"
13 + :summary="summary"
14 + class="mb-2"
15 + />
16 + </template>
17 + <template v-else>
18 + <n-empty description="No items found" v-if="!loadingCountByHost" />
19 + </template>
20 + </div>
21 + </n-spin>
22 + </n-tab-pane>
23 + <n-tab-pane name="countByRule" tab="By Rule">
24 + <n-spin :show="loadingCountByRule">
25 + <template #description>Alerts are being fetched, this may take up to 1 minute.</template>
26 +
27 + <div class="list">
28 + <template v-if="countByRule.length">
29 + <AlertsStatsItem
30 + v-for="summary of countByRule"
31 + :key="summary.rule"
32 + :summary="summary"
33 + class="mb-2"
34 + />
35 + </template>
36 + <template v-else>
37 + <n-empty description="No items found" v-if="!loadingCountByRule" />
38 + </template>
39 + </div>
40 + </n-spin>
41 + </n-tab-pane>
42 + <n-tab-pane name="countByRuleHost" tab="By Rule & Host">
43 + <n-spin :show="loadingCountByRuleHost">
44 + <template #description>Alerts are being fetched, this may take up to 1 minute.</template>
45 +
46 + <div class="list">
47 + <template v-if="countByRuleHost.length">
48 + <AlertsStatsItem
49 + v-for="summary of countByRuleHost"
50 + :key="summary.agent_name + summary.rule"
51 + :summary="summary"
52 + class="mb-2"
53 + />
54 + </template>
55 + <template v-else>
56 + <n-empty description="No items found" v-if="!loadingCountByRuleHost" />
57 + </template>
58 + </div>
59 + </n-spin>
60 + </n-tab-pane>
61 + </n-tabs>
62 + </div>
63 +</template>
64 +
65 +<script setup lang="ts">
66 +import { ref, onBeforeMount, toRefs, onBeforeUnmount } from "vue"
67 +import { useMessage, NSpin, NEmpty, NTabs, NTabPane } from "naive-ui"
68 +import Api from "@/api"
69 +import AlertsStatsItem from "./AlertsStatsItem.vue"
70 +import type { AlertsByHost, AlertsByRule, AlertsByRulePerHost } from "@/types/alerts.d"
71 +import type { AlertsSummaryQuery } from "@/api/alerts"
72 +import axios from "axios"
73 +import { onMounted } from "vue"
74 +// import { alerts_by_host, alerts_by_rule, alerts_by_rule_per_host } from "./mock"
75 +
76 +const props = withDefaults(defineProps<{ filters?: AlertsSummaryQuery }>(), {
77 + filters: () => ({})
78 +})
79 +const { filters } = toRefs(props)
80 +
81 +export interface AlertsStatsCTX {
82 + startSearch: () => void
83 +}
84 +
85 +const emit = defineEmits<{
86 + (e: "mounted", value: AlertsStatsCTX): void
87 +}>()
88 +
89 +const message = useMessage()
90 +const countByHost = ref<AlertsByHost[]>([])
91 +const countByRule = ref<AlertsByRule[]>([])
92 +const countByRuleHost = ref<AlertsByRulePerHost[]>([])
93 +const loadingCountByHost = ref(false)
94 +const loadingCountByRule = ref(false)
95 +const loadingCountByRuleHost = ref(false)
96 +let abortControllerByHost: AbortController | null = null
97 +let abortControllerByRule: AbortController | null = null
98 +let abortControllerByRuleHost: AbortController | null = null
99 +
100 +function getCountByHost() {
101 + loadingCountByHost.value = true
102 +
103 + abortControllerByHost = new AbortController()
104 +
105 + Api.alerts
106 + .getCountByHost(filters.value, abortControllerByHost.signal)
107 + .then(res => {
108 + if (res.data.success) {
109 + countByHost.value = res.data?.alerts_by_host || []
110 + } else {
111 + message.warning(res.data?.message || "An error occurred. Please try again later.")
112 + }
113 + })
114 + .catch(err => {
115 + if (!axios.isCancel(err)) {
116 + countByHost.value = []
117 +
118 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
119 + }
120 + })
121 + .finally(() => {
122 + loadingCountByHost.value = false
123 + })
124 +}
125 +
126 +function getCountByRule() {
127 + loadingCountByRule.value = true
128 +
129 + abortControllerByRule = new AbortController()
130 +
131 + Api.alerts
132 + .getCountByRule(filters.value, abortControllerByRule.signal)
133 + .then(res => {
134 + if (res.data.success) {
135 + countByRule.value = res.data?.alerts_by_rule || []
136 + } else {
137 + message.warning(res.data?.message || "An error occurred. Please try again later.")
138 + }
139 + })
140 + .catch(err => {
141 + if (!axios.isCancel(err)) {
142 + countByRule.value = []
143 +
144 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
145 + }
146 + })
147 + .finally(() => {
148 + loadingCountByRule.value = false
149 + })
150 +}
151 +
152 +function getCountByRuleHost() {
153 + loadingCountByRuleHost.value = true
154 +
155 + abortControllerByRuleHost = new AbortController()
156 +
157 + Api.alerts
158 + .getCountByRuleHost(filters.value, abortControllerByRuleHost.signal)
159 + .then(res => {
160 + if (res.data.success) {
161 + countByRuleHost.value = res.data?.alerts_by_rule_per_host || []
162 + } else {
163 + message.warning(res.data?.message || "An error occurred. Please try again later.")
164 + }
165 + })
166 + .catch(err => {
167 + if (!axios.isCancel(err)) {
168 + countByRuleHost.value = []
169 +
170 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
171 + }
172 + })
173 + .finally(() => {
174 + loadingCountByRuleHost.value = false
175 + })
176 +}
177 +
178 +function startSearch() {
179 + cancelSearch()
180 +
181 + setTimeout(() => {
182 + getCountByHost()
183 + getCountByRule()
184 + getCountByRuleHost()
185 + }, 200)
186 +}
187 +
188 +function cancelSearch() {
189 + abortControllerByHost?.abort()
190 + abortControllerByRule?.abort()
191 + abortControllerByRuleHost?.abort()
192 +}
193 +
194 +onBeforeMount(() => {
195 + /*
196 + countByHost.value = alerts_by_host as AlertsByHost[]
197 + countByRule.value = alerts_by_rule as AlertsByRule[]
198 + countByRuleHost.value = alerts_by_rule_per_host as AlertsByRulePerHost[]
199 + */
200 +
201 + startSearch()
202 +})
203 +
204 +onMounted(() => {
205 + emit("mounted", {
206 + startSearch
207 + })
208 +})
209 +
210 +onBeforeUnmount(() => {
211 + cancelSearch()
212 +})
213 +</script>
214 +
215 +<style lang="scss" scoped>
216 +.alerts-stats {
217 + :deep() {
218 + .n-spin-container {
219 + min-height: 200px;
220 + }
221 + .n-spin-body {
222 + top: 100px;
223 + text-align: center;
224 + width: 80%;
225 + }
226 + }
227 + .list {
228 + padding: var(--n-body-padding);
229 + }
230 +}
231 +</style>
src/components/alerts/AlertsStatsItem.vue new
+45
@@ -0,0 +1,45 @@
1 +<template>
2 + <div class="alerts-stats-item flex items-stretch">
3 + <div class="info grow flex flex-col gap-1 justify-center">
4 + <div
5 + class="agent"
6 + v-if="summary.agent_name"
7 + :class="{ 'text-secondary-color': summary.agent_name && summary.rule }"
8 + >
9 + {{ summary.agent_name }}
10 + </div>
11 + <div class="rule" v-if="summary.rule">{{ summary.rule }}</div>
12 + </div>
13 + <div class="count font-mono flex items-center justify-center">
14 + <span v-if="summary.number_of_alerts">
15 + {{ summary.number_of_alerts }}
16 + </span>
17 + </div>
18 + </div>
19 +</template>
20 +
21 +<script setup lang="ts">
22 +import type { AlertsByHost, AlertsByRule, AlertsByRulePerHost } from "@/types/alerts.d"
23 +
24 +const { summary } = defineProps<{ summary: Partial<AlertsByHost & AlertsByRule & AlertsByRulePerHost> }>()
25 +</script>
26 +
27 +<style lang="scss" scoped>
28 +.alerts-stats-item {
29 + border-radius: var(--border-radius);
30 + overflow: hidden;
31 + background-color: var(--bg-secondary-color);
32 + border: var(--border-small-100);
33 +
34 + .info {
35 + border-right: var(--border-small-100);
36 + padding: 16px 20px;
37 + }
38 +
39 + .count {
40 + min-width: 50px;
41 + background-color: var(--bg-color);
42 + font-weight: bold;
43 + }
44 +}
45 +</style>
src/components/alerts/AlertsSummary.vue new
+173
@@ -0,0 +1,173 @@
1 +<template>
2 + <div class="alert-summary flex flex-col">
3 + <div class="header-box flex justify-between gap-4">
4 + <div class="id flex items-center gap-2" @click="gotoIndicesPage(alertsSummary.index_name)">
5 + <IndexIcon :health="alertsSummary.indexStats?.health" color v-if="alertsSummary.indexStats?.health" />
6 + <Icon :name="PlaceholderIcon" v-else :size="18" />
7 +
8 + {{ alertsSummary.index_name }}
9 + </div>
10 + <div class="total-alerts flex items-center flex-wrap justify-end">
11 + <n-button
12 + v-if="alertsSummary.alerts.length > 3 && showAllAlerts"
13 + @click="showAllAlerts = false"
14 + class="show-less"
15 + size="tiny"
16 + >
17 + Show less
18 + </n-button>
19 + <span>
20 + Alerts:
21 + <strong class="font-mono ml-2">{{ alertsSummary.total_alerts }}</strong>
22 + </span>
23 + </div>
24 + </div>
25 + <div class="main-box">
26 + <div class="alert-list" :class="{ expand: showAllAlerts }">
27 + <n-scrollbar class="list-scroll" trigger="none">
28 + <Alert v-for="alert of alertsSummary.alerts" :key="alert._id" :alert="alert" />
29 + </n-scrollbar>
30 +
31 + <div class="load-more" v-if="alertsSummary.alerts.length > 3" @click="showAllAlerts = true">
32 + <n-button size="small" text class="!w-full">
33 + <template #icon>
34 + <Icon :name="ExpandIcon"></Icon>
35 + </template>
36 + See all alerts
37 + </n-button>
38 + </div>
39 + </div>
40 + </div>
41 + </div>
42 +</template>
43 +
44 +<script setup lang="ts">
45 +import { type AlertsSummary } from "@/types/alerts.d"
46 +import { NScrollbar, NButton } from "naive-ui"
47 +import Alert from "./Alert.vue"
48 +import IndexIcon from "@/components/indices/IndexIcon.vue"
49 +import Icon from "@/components/common/Icon.vue"
50 +import type { IndexStats } from "@/types/indices.d"
51 +import { useRouter } from "vue-router"
52 +import { ref } from "vue"
53 +
54 +export interface AlertsSummaryExt extends AlertsSummary {
55 + indexStats?: IndexStats
56 +}
57 +
58 +const { alertsSummary } = defineProps<{ alertsSummary: AlertsSummaryExt }>()
59 +
60 +const ExpandIcon = "carbon:chevron-down"
61 +const PlaceholderIcon = "ph:question"
62 +
63 +const router = useRouter()
64 +const showAllAlerts = ref(false)
65 +
66 +function gotoIndicesPage(index: string) {
67 + router.push(`/indices?index_name=${index}`).catch(() => {})
68 +}
69 +</script>
70 +
71 +<style lang="scss" scoped>
72 +.alert-summary {
73 + border-radius: var(--border-radius);
74 + background-color: var(--bg-color);
75 + overflow: hidden;
76 + transition: all 0.2s var(--bezier-ease);
77 + border: var(--border-small-050);
78 +
79 + .header-box {
80 + font-size: 15px;
81 + padding: 14px 16px;
82 + min-height: 52px;
83 + border-bottom: var(--border-small-100);
84 +
85 + .id {
86 + word-break: break-word;
87 + cursor: pointer;
88 +
89 + &:hover {
90 + color: var(--primary-color);
91 + }
92 + }
93 + .total-alerts {
94 + text-align: right;
95 + gap: 7px;
96 +
97 + .show-less {
98 + opacity: 0;
99 + animation: show-less-fade 0.3s forwards;
100 +
101 + @keyframes show-less-fade {
102 + from {
103 + opacity: 0;
104 + }
105 + to {
106 + opacity: 1;
107 + }
108 + }
109 + }
110 + }
111 + }
112 +
113 + .main-box {
114 + .alert-list {
115 + position: relative;
116 + overflow: hidden;
117 + transition: all 0.2s var(--bezier-ease);
118 +
119 + .alert-details {
120 + border-bottom: var(--border-small-100);
121 +
122 + &:last-child {
123 + border-bottom: none;
124 + border-bottom-left-radius: var(--border-radius);
125 + border-bottom-right-radius: var(--border-radius);
126 + }
127 + }
128 +
129 + .load-more {
130 + position: absolute;
131 + top: 0;
132 + bottom: 0;
133 + left: 0;
134 + right: 0;
135 + height: 100%;
136 + width: 100%;
137 + background: rgba(var(--bg-color-rgb), 0.6);
138 + background: linear-gradient(transparent 0%, var(--bg-color) 85%);
139 + display: flex;
140 + align-items: center;
141 + text-align: center;
142 + padding: 10px;
143 + flex-direction: column;
144 + justify-content: flex-end;
145 + cursor: pointer;
146 + transition: all 0.6s var(--bezier-ease);
147 + }
148 +
149 + :deep() {
150 + .list-scroll {
151 + max-height: 250px;
152 + transition: all 0.4s var(--bezier-ease);
153 + }
154 + }
155 + &.expand {
156 + :deep() {
157 + .list-scroll {
158 + max-height: 600px;
159 + }
160 + }
161 +
162 + .load-more {
163 + transform: translateY(100%);
164 + }
165 + }
166 + }
167 + }
168 +
169 + &:hover {
170 + border-color: var(--primary-color);
171 + }
172 +}
173 +</style>
src/components/alerts/mock.ts new
+2105
@@ -0,0 +1,2105 @@
1 +const alerts_summary = [
2 + {
3 + index_name: "wazuh-wso4vxhq_7",
4 + total_alerts: 5,
5 + alerts: [
6 + {
7 + _index: "wazuh-wso4vxhq_7",
8 + _id: "54842c0a-7bd6-11ee-93bc-86000046278a",
9 + _score: null,
10 + _source: {
11 + rule_level: 12,
12 + rule_description:
13 + "Possible code injection on explorer.exe by C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe",
14 + rule_groups: "sysmon, sysmon_eid8_detections, windows",
15 + rule_firedtimes: 1,
16 + rule_id: "92400",
17 + rule_mail: true,
18 +
19 + syslog_type: "wazuh",
20 + syslog_level: "ALERT",
21 +
22 + agent_id: "070",
23 + agent_ip: "202.43.110.138",
24 + agent_name: "web1",
25 + agent_labels_customer: "wso4vxhq",
26 +
27 + alert_url:
28 + "https://ashirs01.socfortress.local/alerts?cid=1&page=1&per_page=10&sort=desc&alert_ids=2751",
29 +
30 + source: "10.255.255.13",
31 + streams: ["650d3da25e9a2d550c6d6491"],
32 + decoder_name: "windows_eventchannel",
33 + manager_name: "ASHWZHMA",
34 + location: "EventChannel",
35 +
36 + data_win_eventdata_newThreadId: "16400",
37 + source_reserved_ip: true,
38 + data_win_system_eventRecordID: "4879057",
39 + gl2_remote_ip: "10.255.255.13",
40 + data_win_system_eventID: "8",
41 + gl2_remote_port: 50576,
42 + agent_ip_city_name: "N/A",
43 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
44 + gl2_source_input: "6459151dea00fd5d3da2df91",
45 + data_win_eventdata_sourceUser: "NT AUTHORITY\\\\SYSTEM",
46 + data_win_system_task: "8",
47 + timestamp_utc: "2023-11-05T12:24:50.567Z",
48 + data_win_system_threadID: "5576",
49 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
50 + id: "1699187091.698192264",
51 + data_win_eventdata_sourceImage: "C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe",
52 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
53 + data_win_eventdata_targetProcessGuid: "{d9ab9ebb-62bb-6547-601c-020000003000}",
54 + gl2_accounted_message_size: 5134,
55 + data_win_eventdata_utcTime: "2023-11-05 12:24:50.567",
56 + rule_mitre_id: "T1055",
57 + gl2_message_id: "01HEFQ2Z1GF61945NDN156XZRD",
58 + data_win_system_computer: "web1",
59 + data_win_eventdata_ruleName: "technique_id=T1055,technique_name=Process Injection",
60 + data_win_eventdata_startAddress: "0x00007FFDE5CCE720",
61 + true: 1699187091.846419,
62 + data_win_system_keywords: "0x8000000000000000",
63 + data_win_system_level: "4",
64 + data_win_system_severityValue: "INFORMATION",
65 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
66 + agent_ip_geolocation: "16.1667,107.8333",
67 + rule_mitre_technique: "Process Injection",
68 + data_win_system_systemTime: "2023-11-05T12:24:50.567370300Z",
69 + agent_ip_country_code: "VN",
70 + data_win_system_processID: "3468",
71 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
72 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
73 + data_win_system_version: "2",
74 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
75 + timestamp: "2023-11-05 12:24:56.624",
76 + data_win_system_opcode: "0",
77 + gl2_processing_error:
78 + 'Replaced invalid timestamp value in message <54842c0a-7bd6-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:24:51.691+0000> caused exception: Invalid format: "2023-11-05T12:24:51.691+0000" is malformed at "T12:24:51.691+0000".',
79 + data_win_eventdata_sourceProcessId: "3368",
80 + data_win_eventdata_startModule: "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL",
81 + message:
82 + '{"true":1699187091.846419,"timestamp":"2023-11-05T12:24:51.691+0000","rule":{"level":12,"description":"Possible code injection on explorer.exe by C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe","id":"92400","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":1,"mail":true,"groups":["sysmon","sysmon_eid8_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699187091.698192264","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"8","version":"2","level":"4","task":"8","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:24:50.567370300Z","eventRecordID":"4879057","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"CreateRemoteThread detected:\\r\\nRuleName: technique_id=T1055,technique_name=Process Injection\\r\\nUtcTime: 2023-11-05 12:24:50.567\\r\\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\\r\\nSourceProcessId: 3368\\r\\nSourceImage: C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe\\r\\nTargetProcessGuid: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nNewThreadId: 16400\\r\\nStartAddress: 0x00007FFDE5CCE720\\r\\nStartModule: C:\\\\Windows\\\\System32\\\\KERNEL32.DLL\\r\\nStartFunction: GetCommandLineW\\r\\nSourceUser: NT AUTHORITY\\\\SYSTEM\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1055,technique_name=Process Injection","utcTime":"2023-11-05 12:24:50.567","sourceProcessGuid":"{d9ab9ebb-48da-652f-4900-000000003000}","sourceProcessId":"3368","sourceImage":"C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe","targetProcessGuid":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","newThreadId":"16400","startAddress":"0x00007FFDE5CCE720","startModule":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNEL32.DLL","startFunction":"GetCommandLineW","sourceUser":"NT AUTHORITY\\\\\\\\SYSTEM","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
83 + data_win_eventdata_sourceProcessGuid: "{d9ab9ebb-48da-652f-4900-000000003000}",
84 + data_win_eventdata_targetProcessId: "4384",
85 + data_win_eventdata_startFunction: "GetCommandLineW",
86 + rule_group3: "windows",
87 + data_win_system_message:
88 + '"CreateRemoteThread detected:\r\nRuleName: technique_id=T1055,technique_name=Process Injection\r\nUtcTime: 2023-11-05 12:24:50.567\r\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\r\nSourceProcessId: 3368\r\nSourceImage: C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe\r\nTargetProcessGuid: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nNewThreadId: 16400\r\nStartAddress: 0x00007FFDE5CCE720\r\nStartModule: C:\\Windows\\System32\\KERNEL32.DLL\r\nStartFunction: GetCommandLineW\r\nSourceUser: NT AUTHORITY\\SYSTEM\r\nTargetUser: WEB1\\Administrator"',
89 + msg_timestamp: "2023-11-05T12:24:51.691Z",
90 + rule_group2: "sysmon_eid8_detections",
91 + rule_group1: "sysmon"
92 + },
93 + sort: [1699187090567]
94 + },
95 + {
96 + _index: "wazuh-wso4vxhq_7",
97 + _id: "48a2f142-7bd6-11ee-93bc-86000046278a",
98 + _score: null,
99 + _source: {
100 + data_win_eventdata_newThreadId: "17292",
101 + source_reserved_ip: true,
102 + data_win_system_eventRecordID: "4878905",
103 + agent_id: "070",
104 + agent_name: "web1",
105 + gl2_remote_ip: "10.255.255.13",
106 + data_win_system_eventID: "8",
107 + gl2_remote_port: 57222,
108 + agent_labels_customer: "wso4vxhq",
109 + agent_ip_city_name: "N/A",
110 + source: "10.255.255.13",
111 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\System32\\\\lsass.exe",
112 + gl2_source_input: "6459151dea00fd5d3da2df91",
113 + rule_level: 12,
114 + data_win_eventdata_sourceUser: "NT AUTHORITY\\\\SYSTEM",
115 + data_win_system_task: "8",
116 + timestamp_utc: "2023-11-05T12:24:32.015Z",
117 + syslog_type: "wazuh",
118 + data_win_system_threadID: "5576",
119 + rule_description:
120 + "Local Security Authority Subsystem Service (LSASS) process was accessed by C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe, possible code injection for credential dumping",
121 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
122 + id: "1699187072.697473214",
123 + data_win_eventdata_sourceImage: "C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe",
124 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
125 + data_win_eventdata_targetProcessGuid: "{d9ab9ebb-48d7-652f-0c00-000000003000}",
126 + gl2_accounted_message_size: 5323,
127 + data_win_eventdata_utcTime: "2023-11-05 12:24:32.013",
128 + streams: ["650d3da25e9a2d550c6d6491"],
129 + rule_mitre_id: "T1055",
130 + gl2_message_id: "01HEFQ2BJMQ6D8ZQD9SW96GKRJ",
131 + data_win_system_computer: "web1",
132 + data_win_eventdata_ruleName: "technique_id=T1055,technique_name=Process Injection",
133 + agent_ip: "202.43.110.138",
134 + data_win_eventdata_startAddress: "0x00007FFDE5CCE720",
135 + true: 1699187072.493505,
136 + rule_groups: "sysmon, sysmon_eid8_detections, windows",
137 + data_win_system_keywords: "0x8000000000000000",
138 + data_win_system_level: "4",
139 + data_win_system_severityValue: "INFORMATION",
140 + data_win_eventdata_targetUser: "NT AUTHORITY\\\\SYSTEM",
141 + agent_ip_geolocation: "16.1667,107.8333",
142 + rule_mitre_technique: "Process Injection",
143 + rule_firedtimes: 1,
144 + data_win_system_systemTime: "2023-11-05T12:24:32.015055900Z",
145 + rule_mail: true,
146 + decoder_name: "windows_eventchannel",
147 + agent_ip_country_code: "VN",
148 + data_win_system_processID: "3468",
149 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
150 + syslog_level: "ALERT",
151 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
152 + data_win_system_version: "2",
153 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
154 + timestamp: "2023-11-05 12:24:36.692",
155 + data_win_system_opcode: "0",
156 + gl2_processing_error:
157 + 'Replaced invalid timestamp value in message <48a2f142-7bd6-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:24:32.489+0000> caused exception: Invalid format: "2023-11-05T12:24:32.489+0000" is malformed at "T12:24:32.489+0000".',
158 + data_win_eventdata_sourceProcessId: "3368",
159 + data_win_eventdata_startModule: "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL",
160 + message:
161 + '{"true":1699187072.493505,"timestamp":"2023-11-05T12:24:32.489+0000","rule":{"level":12,"description":"Local Security Authority Subsystem Service (LSASS) process was accessed by C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe, possible code injection for credential dumping","id":"92403","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":1,"mail":true,"groups":["sysmon","sysmon_eid8_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699187072.697473214","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"8","version":"2","level":"4","task":"8","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:24:32.015055900Z","eventRecordID":"4878905","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"CreateRemoteThread detected:\\r\\nRuleName: technique_id=T1055,technique_name=Process Injection\\r\\nUtcTime: 2023-11-05 12:24:32.013\\r\\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\\r\\nSourceProcessId: 3368\\r\\nSourceImage: C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe\\r\\nTargetProcessGuid: {d9ab9ebb-48d7-652f-0c00-000000003000}\\r\\nTargetProcessId: 740\\r\\nTargetImage: C:\\\\Windows\\\\System32\\\\lsass.exe\\r\\nNewThreadId: 17292\\r\\nStartAddress: 0x00007FFDE5CCE720\\r\\nStartModule: C:\\\\Windows\\\\System32\\\\KERNEL32.DLL\\r\\nStartFunction: GetCommandLineW\\r\\nSourceUser: NT AUTHORITY\\\\SYSTEM\\r\\nTargetUser: NT AUTHORITY\\\\SYSTEM\\""},"eventdata":{"ruleName":"technique_id=T1055,technique_name=Process Injection","utcTime":"2023-11-05 12:24:32.013","sourceProcessGuid":"{d9ab9ebb-48da-652f-4900-000000003000}","sourceProcessId":"3368","sourceImage":"C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe","targetProcessGuid":"{d9ab9ebb-48d7-652f-0c00-000000003000}","targetProcessId":"740","targetImage":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\lsass.exe","newThreadId":"17292","startAddress":"0x00007FFDE5CCE720","startModule":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNEL32.DLL","startFunction":"GetCommandLineW","sourceUser":"NT AUTHORITY\\\\\\\\SYSTEM","targetUser":"NT AUTHORITY\\\\\\\\SYSTEM"}}},"location":"EventChannel"}',
162 + rule_id: "92403",
163 + manager_name: "ASHWZHMA",
164 + data_win_eventdata_sourceProcessGuid: "{d9ab9ebb-48da-652f-4900-000000003000}",
165 + location: "EventChannel",
166 + data_win_eventdata_targetProcessId: "740",
167 + data_win_eventdata_startFunction: "GetCommandLineW",
168 + rule_group3: "windows",
169 + data_win_system_message:
170 + '"CreateRemoteThread detected:\r\nRuleName: technique_id=T1055,technique_name=Process Injection\r\nUtcTime: 2023-11-05 12:24:32.013\r\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\r\nSourceProcessId: 3368\r\nSourceImage: C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe\r\nTargetProcessGuid: {d9ab9ebb-48d7-652f-0c00-000000003000}\r\nTargetProcessId: 740\r\nTargetImage: C:\\Windows\\System32\\lsass.exe\r\nNewThreadId: 17292\r\nStartAddress: 0x00007FFDE5CCE720\r\nStartModule: C:\\Windows\\System32\\KERNEL32.DLL\r\nStartFunction: GetCommandLineW\r\nSourceUser: NT AUTHORITY\\SYSTEM\r\nTargetUser: NT AUTHORITY\\SYSTEM"',
171 + msg_timestamp: "2023-11-05T12:24:32.489Z",
172 + rule_group2: "sysmon_eid8_detections",
173 + rule_group1: "sysmon"
174 + },
175 + sort: [1699187072015]
176 + },
177 + {
178 + _index: "wazuh-wso4vxhq_7",
179 + _id: "18e9b012-7bd6-11ee-93bc-86000046278a",
180 + _score: null,
181 + _source: {
182 + source_reserved_ip: true,
183 + data_win_system_eventRecordID: "4878784",
184 + agent_id: "070",
185 + agent_name: "web1",
186 + data_win_eventdata_sourceProcessGUID: "{d9ab9ebb-7e1a-6544-44e0-010000003000}",
187 + gl2_remote_ip: "10.255.255.13",
188 + data_win_system_eventID: "10",
189 + gl2_remote_port: 60342,
190 + agent_labels_customer: "wso4vxhq",
191 + agent_ip_city_name: "N/A",
192 + source: "10.255.255.13",
193 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
194 + gl2_source_input: "6459151dea00fd5d3da2df91",
195 + rule_level: 12,
196 + data_win_eventdata_sourceUser: "WEB1\\\\Administrator",
197 + data_win_system_task: "10",
198 + timestamp_utc: "2023-11-05T12:23:11.139Z",
199 + syslog_type: "wazuh",
200 + data_win_system_threadID: "5576",
201 + rule_description:
202 + "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
203 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
204 + id: "1699186992.695685886",
205 + data_win_eventdata_grantedAccess: "0x40",
206 + data_win_eventdata_sourceImage:
207 + "C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe",
208 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
209 + gl2_accounted_message_size: 11630,
210 + data_win_eventdata_utcTime: "2023-11-05 12:23:11.138",
211 + streams: ["650d3da25e9a2d550c6d6491"],
212 + rule_mitre_id: "T1055",
213 + gl2_message_id: "01HEFPZXCJZA24HYV8G8E45ND1",
214 + data_win_system_computer: "web1",
215 + data_win_eventdata_ruleName: "technique_id=T1036,technique_name=Masquerading",
216 + agent_ip: "202.43.110.138",
217 + true: 1699186992.569563,
218 + rule_groups: "sysmon, sysmon_eid10_detections, windows",
219 + data_win_system_keywords: "0x8000000000000000",
220 + data_win_system_level: "4",
221 + data_win_eventdata_targetProcessGUID: "{d9ab9ebb-62bb-6547-601c-020000003000}",
222 + data_win_system_severityValue: "INFORMATION",
223 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
224 + agent_ip_geolocation: "16.1667,107.8333",
225 + rule_mitre_technique: "Process Injection",
226 + rule_firedtimes: 33,
227 + data_win_system_systemTime: "2023-11-05T12:23:11.139487000Z",
228 + rule_mail: true,
229 + decoder_name: "windows_eventchannel",
230 + agent_ip_country_code: "VN",
231 + data_win_system_processID: "3468",
232 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
233 + syslog_level: "ALERT",
234 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
235 + data_win_system_version: "3",
236 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
237 + timestamp: "2023-11-05 12:23:16.626",
238 + data_win_eventdata_callTrace:
239 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba77|C:\\\\Windows\\\\System32\\\\shcore.dll+b967|C:\\\\Windows\\\\System32\\\\shcore.dll+b8f1|C:\\\\Windows\\\\System32\\\\shcore.dll+b61a|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b9d0|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9",
240 + data_win_system_opcode: "0",
241 + gl2_processing_error:
242 + 'Replaced invalid timestamp value in message <18e9b012-7bd6-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:23:12.276+0000> caused exception: Invalid format: "2023-11-05T12:23:12.276+0000" is malformed at "T12:23:12.276+0000".',
243 + data_win_eventdata_sourceProcessId: "1652",
244 + message:
245 + '{"true":1699186992.569563,"timestamp":"2023-11-05T12:23:12.276+0000","rule":{"level":12,"description":"Explorer process was accessed by C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe, possible process injection","id":"92910","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":33,"mail":true,"groups":["sysmon","sysmon_eid10_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699186992.695685886","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"10","version":"3","level":"4","task":"10","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:23:11.139487000Z","eventRecordID":"4878784","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"Process accessed:\\r\\nRuleName: technique_id=T1036,technique_name=Masquerading\\r\\nUtcTime: 2023-11-05 12:23:11.138\\r\\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\\r\\nSourceProcessId: 1652\\r\\nSourceThreadId: 1284\\r\\nSourceImage: C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe\\r\\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nGrantedAccess: 0x40\\r\\nCallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba77|C:\\\\Windows\\\\System32\\\\shcore.dll+b967|C:\\\\Windows\\\\System32\\\\shcore.dll+b8f1|C:\\\\Windows\\\\System32\\\\shcore.dll+b61a|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b9d0|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9\\r\\nSourceUser: WEB1\\\\Administrator\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1036,technique_name=Masquerading","utcTime":"2023-11-05 12:23:11.138","sourceProcessGUID":"{d9ab9ebb-7e1a-6544-44e0-010000003000}","sourceProcessId":"1652","sourceThreadId":"1284","sourceImage":"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe","targetProcessGUID":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","grantedAccess":"0x40","callTrace":"C:\\\\\\\\Windows\\\\\\\\SYSTEM32\\\\\\\\ntdll.dll+9ff24|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNELBASE.dll+1668e|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+cca8|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+ba77|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b967|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b8f1|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b61a|C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\explorerframe.dll+12b9d0|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+157bd8c|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+18051ce|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2e1a17|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2dfdfe|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+130216a|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a566ec|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a5695f|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3e93b|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c40433|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb8e|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3d62541|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb68|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c942dd|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3db94|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a52491|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a3e4a9","sourceUser":"WEB1\\\\\\\\Administrator","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
246 + rule_id: "92910",
247 + manager_name: "ASHWZHMA",
248 + location: "EventChannel",
249 + data_win_eventdata_targetProcessId: "4384",
250 + rule_group3: "windows",
251 + data_win_system_message:
252 + '"Process accessed:\r\nRuleName: technique_id=T1036,technique_name=Masquerading\r\nUtcTime: 2023-11-05 12:23:11.138\r\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\r\nSourceProcessId: 1652\r\nSourceThreadId: 1284\r\nSourceImage: C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe\r\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nGrantedAccess: 0x40\r\nCallTrace: C:\\Windows\\SYSTEM32\\ntdll.dll+9ff24|C:\\Windows\\System32\\KERNELBASE.dll+1668e|C:\\Windows\\System32\\shcore.dll+cca8|C:\\Windows\\System32\\shcore.dll+ba77|C:\\Windows\\System32\\shcore.dll+b967|C:\\Windows\\System32\\shcore.dll+b8f1|C:\\Windows\\System32\\shcore.dll+b61a|C:\\Windows\\system32\\explorerframe.dll+12b9d0|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+157bd8c|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+18051ce|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2e1a17|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2dfdfe|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+130216a|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a566ec|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a5695f|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3e93b|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c40433|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb8e|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3d62541|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb68|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c942dd|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3db94|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a52491|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a3e4a9\r\nSourceUser: WEB1\\Administrator\r\nTargetUser: WEB1\\Administrator"',
253 + data_win_eventdata_sourceThreadId: "1284",
254 + msg_timestamp: "2023-11-05T12:23:12.276Z",
255 + rule_group2: "sysmon_eid10_detections",
256 + rule_group1: "sysmon"
257 + },
258 + sort: [1699186991139]
259 + },
260 + {
261 + _index: "wazuh-wso4vxhq_7",
262 + _id: "18e9b010-7bd6-11ee-93bc-86000046278a",
263 + _score: null,
264 + _source: {
265 + source_reserved_ip: true,
266 + data_win_system_eventRecordID: "4878783",
267 + agent_id: "070",
268 + agent_name: "web1",
269 + data_win_eventdata_sourceProcessGUID: "{d9ab9ebb-7e1a-6544-44e0-010000003000}",
270 + gl2_remote_ip: "10.255.255.13",
271 + data_win_system_eventID: "10",
272 + gl2_remote_port: 60342,
273 + agent_labels_customer: "wso4vxhq",
274 + agent_ip_city_name: "N/A",
275 + source: "10.255.255.13",
276 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
277 + gl2_source_input: "6459151dea00fd5d3da2df91",
278 + rule_level: 12,
279 + data_win_eventdata_sourceUser: "WEB1\\\\Administrator",
280 + data_win_system_task: "10",
281 + timestamp_utc: "2023-11-05T12:23:11.139Z",
282 + syslog_type: "wazuh",
283 + data_win_system_threadID: "5576",
284 + rule_description:
285 + "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
286 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
287 + id: "1699186992.695678238",
288 + data_win_eventdata_grantedAccess: "0x40",
289 + data_win_eventdata_sourceImage:
290 + "C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe",
291 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
292 + gl2_accounted_message_size: 11455,
293 + data_win_eventdata_utcTime: "2023-11-05 12:23:11.137",
294 + streams: ["650d3da25e9a2d550c6d6491"],
295 + rule_mitre_id: "T1055",
296 + gl2_message_id: "01HEFPZXCJWE7EJ5BK46QKR67G",
297 + data_win_system_computer: "web1",
298 + data_win_eventdata_ruleName: "technique_id=T1036,technique_name=Masquerading",
299 + agent_ip: "202.43.110.138",
300 + true: 1699186992.277071,
301 + rule_groups: "sysmon, sysmon_eid10_detections, windows",
302 + data_win_system_keywords: "0x8000000000000000",
303 + data_win_system_level: "4",
304 + data_win_eventdata_targetProcessGUID: "{d9ab9ebb-62bb-6547-601c-020000003000}",
305 + data_win_system_severityValue: "INFORMATION",
306 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
307 + agent_ip_geolocation: "16.1667,107.8333",
308 + rule_mitre_technique: "Process Injection",
309 + rule_firedtimes: 32,
310 + data_win_system_systemTime: "2023-11-05T12:23:11.139039000Z",
311 + rule_mail: true,
312 + decoder_name: "windows_eventchannel",
313 + agent_ip_country_code: "VN",
314 + data_win_system_processID: "3468",
315 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
316 + syslog_level: "ALERT",
317 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
318 + data_win_system_version: "3",
319 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
320 + timestamp: "2023-11-05 12:23:16.626",
321 + data_win_eventdata_callTrace:
322 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba62|C:\\\\Windows\\\\System32\\\\shcore.dll+b585|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491",
323 + data_win_system_opcode: "0",
324 + gl2_processing_error:
325 + 'Replaced invalid timestamp value in message <18e9b010-7bd6-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:23:12.272+0000> caused exception: Invalid format: "2023-11-05T12:23:12.272+0000" is malformed at "T12:23:12.272+0000".',
326 + data_win_eventdata_sourceProcessId: "1652",
327 + message:
328 + '{"true":1699186992.277071,"timestamp":"2023-11-05T12:23:12.272+0000","rule":{"level":12,"description":"Explorer process was accessed by C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe, possible process injection","id":"92910","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":32,"mail":true,"groups":["sysmon","sysmon_eid10_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699186992.695678238","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"10","version":"3","level":"4","task":"10","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:23:11.139039000Z","eventRecordID":"4878783","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"Process accessed:\\r\\nRuleName: technique_id=T1036,technique_name=Masquerading\\r\\nUtcTime: 2023-11-05 12:23:11.137\\r\\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\\r\\nSourceProcessId: 1652\\r\\nSourceThreadId: 1284\\r\\nSourceImage: C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe\\r\\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nGrantedAccess: 0x40\\r\\nCallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba62|C:\\\\Windows\\\\System32\\\\shcore.dll+b585|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491\\r\\nSourceUser: WEB1\\\\Administrator\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1036,technique_name=Masquerading","utcTime":"2023-11-05 12:23:11.137","sourceProcessGUID":"{d9ab9ebb-7e1a-6544-44e0-010000003000}","sourceProcessId":"1652","sourceThreadId":"1284","sourceImage":"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe","targetProcessGUID":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","grantedAccess":"0x40","callTrace":"C:\\\\\\\\Windows\\\\\\\\SYSTEM32\\\\\\\\ntdll.dll+9ff24|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNELBASE.dll+1668e|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+cca8|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+ba62|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b585|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b275|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b209|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b104|C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\explorerframe.dll+12b986|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+157bd8c|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+18051ce|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2e1a17|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2dfdfe|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+130216a|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a566ec|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a5695f|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3e93b|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c40433|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb8e|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3d62541|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb68|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c942dd|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3db94|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a52491","sourceUser":"WEB1\\\\\\\\Administrator","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
329 + rule_id: "92910",
330 + manager_name: "ASHWZHMA",
331 + location: "EventChannel",
332 + data_win_eventdata_targetProcessId: "4384",
333 + rule_group3: "windows",
334 + data_win_system_message:
335 + '"Process accessed:\r\nRuleName: technique_id=T1036,technique_name=Masquerading\r\nUtcTime: 2023-11-05 12:23:11.137\r\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\r\nSourceProcessId: 1652\r\nSourceThreadId: 1284\r\nSourceImage: C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe\r\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nGrantedAccess: 0x40\r\nCallTrace: C:\\Windows\\SYSTEM32\\ntdll.dll+9ff24|C:\\Windows\\System32\\KERNELBASE.dll+1668e|C:\\Windows\\System32\\shcore.dll+cca8|C:\\Windows\\System32\\shcore.dll+ba62|C:\\Windows\\System32\\shcore.dll+b585|C:\\Windows\\System32\\shcore.dll+b275|C:\\Windows\\System32\\shcore.dll+b209|C:\\Windows\\System32\\shcore.dll+b104|C:\\Windows\\system32\\explorerframe.dll+12b986|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+157bd8c|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+18051ce|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2e1a17|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2dfdfe|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+130216a|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a566ec|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a5695f|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3e93b|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c40433|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb8e|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3d62541|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb68|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c942dd|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3db94|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a52491\r\nSourceUser: WEB1\\Administrator\r\nTargetUser: WEB1\\Administrator"',
336 + data_win_eventdata_sourceThreadId: "1284",
337 + msg_timestamp: "2023-11-05T12:23:12.272Z",
338 + rule_group2: "sysmon_eid10_detections",
339 + rule_group1: "sysmon"
340 + },
341 + sort: [1699186991139]
342 + },
343 + {
344 + _index: "wazuh-wso4vxhq_7",
345 + _id: "18e98900-7bd6-11ee-93bc-86000046278a",
346 + _score: null,
347 + _source: {
348 + source_reserved_ip: true,
349 + data_win_system_eventRecordID: "4878782",
350 + agent_id: "070",
351 + agent_name: "web1",
352 + data_win_eventdata_sourceProcessGUID: "{d9ab9ebb-7e1a-6544-44e0-010000003000}",
353 + gl2_remote_ip: "10.255.255.13",
354 + data_win_system_eventID: "10",
355 + gl2_remote_port: 60342,
356 + agent_labels_customer: "wso4vxhq",
357 + agent_ip_city_name: "N/A",
358 + source: "10.255.255.13",
359 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
360 + gl2_source_input: "6459151dea00fd5d3da2df91",
361 + rule_level: 12,
362 + data_win_eventdata_sourceUser: "WEB1\\\\Administrator",
363 + data_win_system_task: "10",
364 + timestamp_utc: "2023-11-05T12:23:11.138Z",
365 + syslog_type: "wazuh",
366 + data_win_system_threadID: "5576",
367 + rule_description:
368 + "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
369 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
370 + id: "1699186992.695672487",
371 + data_win_eventdata_grantedAccess: "0x40",
372 + data_win_eventdata_sourceImage:
373 + "C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe",
374 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
375 + gl2_accounted_message_size: 11630,
376 + data_win_eventdata_utcTime: "2023-11-05 12:23:11.137",
377 + streams: ["650d3da25e9a2d550c6d6491"],
378 + rule_mitre_id: "T1055",
379 + gl2_message_id: "01HEFPZXCH0JEEF6TT8BQK4XJR",
380 + data_win_system_computer: "web1",
381 + data_win_eventdata_ruleName: "technique_id=T1036,technique_name=Masquerading",
382 + agent_ip: "202.43.110.138",
383 + true: 1699186992.273304,
384 + rule_groups: "sysmon, sysmon_eid10_detections, windows",
385 + data_win_system_keywords: "0x8000000000000000",
386 + data_win_system_level: "4",
387 + data_win_eventdata_targetProcessGUID: "{d9ab9ebb-62bb-6547-601c-020000003000}",
388 + data_win_system_severityValue: "INFORMATION",
389 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
390 + agent_ip_geolocation: "16.1667,107.8333",
391 + rule_mitre_technique: "Process Injection",
392 + rule_firedtimes: 31,
393 + data_win_system_systemTime: "2023-11-05T12:23:11.138753100Z",
394 + rule_mail: true,
395 + decoder_name: "windows_eventchannel",
396 + agent_ip_country_code: "VN",
397 + data_win_system_processID: "3468",
398 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
399 + syslog_level: "ALERT",
400 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
401 + data_win_system_version: "3",
402 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
403 + timestamp: "2023-11-05 12:23:16.625",
404 + data_win_eventdata_callTrace:
405 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+b55c|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9",
406 + data_win_system_opcode: "0",
407 + gl2_processing_error:
408 + 'Replaced invalid timestamp value in message <18e98900-7bd6-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:23:12.272+0000> caused exception: Invalid format: "2023-11-05T12:23:12.272+0000" is malformed at "T12:23:12.272+0000".',
409 + data_win_eventdata_sourceProcessId: "1652",
410 + message:
411 + '{"true":1699186992.273304,"timestamp":"2023-11-05T12:23:12.272+0000","rule":{"level":12,"description":"Explorer process was accessed by C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe, possible process injection","id":"92910","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":31,"mail":true,"groups":["sysmon","sysmon_eid10_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699186992.695672487","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"10","version":"3","level":"4","task":"10","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:23:11.138753100Z","eventRecordID":"4878782","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"Process accessed:\\r\\nRuleName: technique_id=T1036,technique_name=Masquerading\\r\\nUtcTime: 2023-11-05 12:23:11.137\\r\\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\\r\\nSourceProcessId: 1652\\r\\nSourceThreadId: 1284\\r\\nSourceImage: C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe\\r\\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nGrantedAccess: 0x40\\r\\nCallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+b55c|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9\\r\\nSourceUser: WEB1\\\\Administrator\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1036,technique_name=Masquerading","utcTime":"2023-11-05 12:23:11.137","sourceProcessGUID":"{d9ab9ebb-7e1a-6544-44e0-010000003000}","sourceProcessId":"1652","sourceThreadId":"1284","sourceImage":"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe","targetProcessGUID":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","grantedAccess":"0x40","callTrace":"C:\\\\\\\\Windows\\\\\\\\SYSTEM32\\\\\\\\ntdll.dll+9ff24|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNELBASE.dll+1668e|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+cca8|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b55c|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b275|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b209|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b104|C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\explorerframe.dll+12b986|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+157bd8c|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+18051ce|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2e1a17|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2dfdfe|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+130216a|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a566ec|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a5695f|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3e93b|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c40433|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb8e|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3d62541|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb68|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c942dd|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3db94|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a52491|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a3e4a9","sourceUser":"WEB1\\\\\\\\Administrator","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
412 + rule_id: "92910",
413 + manager_name: "ASHWZHMA",
414 + location: "EventChannel",
415 + data_win_eventdata_targetProcessId: "4384",
416 + rule_group3: "windows",
417 + data_win_system_message:
418 + '"Process accessed:\r\nRuleName: technique_id=T1036,technique_name=Masquerading\r\nUtcTime: 2023-11-05 12:23:11.137\r\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\r\nSourceProcessId: 1652\r\nSourceThreadId: 1284\r\nSourceImage: C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe\r\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nGrantedAccess: 0x40\r\nCallTrace: C:\\Windows\\SYSTEM32\\ntdll.dll+9ff24|C:\\Windows\\System32\\KERNELBASE.dll+1668e|C:\\Windows\\System32\\shcore.dll+cca8|C:\\Windows\\System32\\shcore.dll+b55c|C:\\Windows\\System32\\shcore.dll+b275|C:\\Windows\\System32\\shcore.dll+b209|C:\\Windows\\System32\\shcore.dll+b104|C:\\Windows\\system32\\explorerframe.dll+12b986|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+157bd8c|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+18051ce|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2e1a17|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2dfdfe|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+130216a|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a566ec|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a5695f|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3e93b|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c40433|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb8e|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3d62541|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb68|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c942dd|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3db94|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a52491|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a3e4a9\r\nSourceUser: WEB1\\Administrator\r\nTargetUser: WEB1\\Administrator"',
419 + data_win_eventdata_sourceThreadId: "1284",
420 + msg_timestamp: "2023-11-05T12:23:12.272Z",
421 + rule_group2: "sysmon_eid10_detections",
422 + rule_group1: "sysmon"
423 + },
424 + sort: [1699186991138]
425 + }
426 + ]
427 + },
428 + {
429 + index_name: "wazuh-zaff3p5c_0",
430 + total_alerts: 1,
431 + alerts: [
432 + {
433 + _index: "wazuh-zaff3p5c_0",
434 + _id: "4978eea4-7bc5-11ee-93bc-86000046278a",
435 + _score: null,
436 + _source: {
437 + parent_process_id: "3565733",
438 + source_reserved_ip: true,
439 + agent_id: "072",
440 + agent_name: "ip-178-216-201-141",
441 + gl2_remote_ip: "10.255.255.13",
442 + gl2_remote_port: 46242,
443 + agent_labels_customer: "zaff3p5c",
444 + agent_ip_city_name: "N/A",
445 + source: "10.255.255.13",
446 + gl2_source_input: "6459151dea00fd5d3da2df91",
447 + rule_level: 12,
448 + data_calendarTime: "Sun Nov 5 10:22:52 2023 UTC",
449 + data_counter: "8287",
450 + data_columns_duration: "132210",
451 + timestamp_utc: "2023-11-05T10:22:52.000Z",
452 + syslog_type: "wazuh",
453 + process_name: "/usr/bin/chmod",
454 + process_cmd_line: "chmod +r /var/lib/update-notifier/updates-available",
455 + data_hostIdentifier: "ip-178-216-201-141",
456 + data_columns_probe_error: "0",
457 + rule_description: "Detects file and folder permission changes.",
458 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
459 + id: "1699179774.599228705",
460 + rule_mitre_tactic: "Defense Evasion",
461 + process_image: "/usr/bin/chmod",
462 + gl2_accounted_message_size: 2584,
463 + data_columns_uid: "0",
464 + streams: ["651176ee5e9a2d550c79fa32"],
465 + rule_mitre_id: "T1222",
466 + gl2_message_id: "01HEFG3JMB8FK2YN4EYWBQ5FSS",
467 + agent_ip: "178.216.201.141",
468 + data_columns_gid: "0",
469 + data_columns_syscall: "exec",
470 + true: 1699179774.197991,
471 + data_columns_cid: "30181",
472 + rule_groups: "osquery, bpf_process_events",
473 + data_columns_exit_code: "0",
474 + process_id: "3565780",
475 + agent_ip_geolocation: "52.2394,21.0362",
476 + rule_mitre_technique: "File and Directory Permissions Modification",
477 + rule_firedtimes: 1,
478 + rule_mail: true,
479 + data_name: "bpf_process_events",
480 + decoder_name: "json",
481 + agent_ip_country_code: "PL",
482 + data_columns_ntime: "3515969404589692",
483 + syslog_level: "ALERT",
484 + timestamp: "2023-11-05 10:22:56.651",
485 + data_columns_cmdline: "chmod +r /var/lib/update-notifier/updates-available",
486 + data_columns_tid: "3565780",
487 + gl2_processing_error:
488 + 'Replaced invalid timestamp value in message <4978eea4-7bc5-11ee-93bc-86000046278a> with current time - Value <2023-11-05T10:22:54.197+0000> caused exception: Invalid format: "2023-11-05T10:22:54.197+0000" is malformed at "T10:22:54.197+0000".',
489 + data_columns_pid: "3565780",
490 + message:
491 + '{"true":1699179774.197991,"timestamp":"2023-11-05T10:22:54.197+0000","rule":{"level":12,"description":"Detects file and folder permission changes.","id":"200259","mitre":{"id":["T1222"],"tactic":["Defense Evasion"],"technique":["File and Directory Permissions Modification"]},"firedtimes":1,"mail":true,"groups":["osquery","bpf_process_events"]},"agent":{"id":"072","name":"ip-178-216-201-141","ip":"178.216.201.141","labels":{"customer":"zaff3p5c"}},"manager":{"name":"ASHWZHMA"},"id":"1699179774.599228705","decoder":{"name":"json"},"data":{"action":"added","name":"bpf_process_events","hostIdentifier":"ip-178-216-201-141","calendarTime":"Sun Nov 5 10:22:52 2023 UTC","unixTime":"1699179772","epoch":"0","counter":"8287","numerics":"false","columns":{"cid":"30181","cmdline":"chmod +r /var/lib/update-notifier/updates-available","duration":"132210","exit_code":"0","gid":"0","ntime":"3515969404589692","parent":"3565733","path":"/usr/bin/chmod","pid":"3565780","probe_error":"0","syscall":"exec","tid":"3565780","uid":"0"}},"location":"/var/log/osquery/osqueryd.results.log"}',
492 + data_numerics: "false",
493 + rule_id: "200259",
494 + manager_name: "ASHWZHMA",
495 + data_columns_path: "/usr/bin/chmod",
496 + data_unixTime: "1699179772",
497 + data_action: "added",
498 + data_epoch: "0",
499 + location: "/var/log/osquery/osqueryd.results.log",
500 + data_columns_parent: "3565733",
501 + msg_timestamp: "2023-11-05T10:22:54.197Z",
502 + rule_group2: "bpf_process_events",
503 + rule_group1: "osquery"
504 + },
505 + sort: [1699179772000]
506 + }
507 + ]
508 + },
509 + {
510 + index_name: "wazuh_00002_201",
511 + total_alerts: 5,
512 + alerts: [
513 + {
514 + _index: "wazuh_00002_201",
515 + _id: "366ba9a0-7bd5-11ee-93bc-86000046278a",
516 + _score: null,
517 + _source: {
518 + data_system_Task: "11",
519 + source_reserved_ip: true,
520 + agent_id: "097",
521 + agent_name: "ANSYDWDC01",
522 + data_system_Correlation: "null",
523 + gl2_remote_ip: "10.255.255.13",
524 + gl2_remote_port: 50678,
525 + agent_labels_customer: "00002",
526 + data_system_Version: "2",
527 + agent_ip_city_name: "Singapore",
528 + source: "10.255.255.13",
529 + gl2_source_input: "6459151dea00fd5d3da2df91",
530 + rule_level: 12,
531 + data_level: "high",
532 + timestamp_utc: "2023-11-05T12:16:02.043Z",
533 + data_event_ProcessId: "4684",
534 + syslog_type: "wazuh",
535 + data_system_Opcode: "0",
536 + rule_description: "Process Explorer Driver Creation By Non-Sysinternals Binary",
537 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
538 + id: "1699186616.690805701",
539 + data_status: "experimental",
540 + data_system_Computer: "ANSYDWDC01.ANMS.LOCAL",
541 + gl2_accounted_message_size: 10712,
542 + data_document:
543 + '{"kind":"evtx","path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","data":{"Event":{"EventData":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-8781-6547-C4BB-000000004200","ProcessId":4684,"RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 12:16:02.043"},"System":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":null,"EventID":11,"EventRecordID":18141034,"Execution_attributes":{"ProcessID":2564,"ThreadID":3804},"Keywords":"0x8000000000000000","Level":4,"Opcode":0,"Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":11,"TimeCreated_attributes":{"SystemTime":"2023-11-05T12:16:02.045670Z"},"Version":2}},"Event_attributes":{"xmlns":"http://schemas.microsoft.com/win/2004/08/events/event"}}}',
544 + data_event_UtcTime: "2023-11-05 12:16:02.043",
545 + streams: ["645a3a6123e5cc30bbc0e5dc"],
546 + gl2_message_id: "01HEFPMAE4N8YW57K4Q6P44CKB",
547 + data_source: "sigma",
548 + agent_ip: "139.180.134.102",
549 + data_system_Security_attributes_UserID: "S-1-5-18",
550 + true: 1699186616.142398,
551 + data_timestamp: "2023-11-05T12:16:02.045670+00:00",
552 + data_system_Level: "4",
553 + data_event_CreationUtcTime: "2023-11-01 06:52:11.733",
554 + data_system_Execution_attributes_ProcessID: "2564",
555 + rule_groups: "windows, chainsaw, sigma",
556 + data_system_EventRecordID: "18141034",
557 + process_id: "4684",
558 + data_system_TimeCreated_attributes_SystemTime: "2023-11-05T12:16:02.045670Z",
559 + data_event_RuleName: "-",
560 + data_logsource_category: "file_event",
561 + data_system_Keywords: "0x8000000000000000",
562 + sigma_name_encoded: "Process%20Explorer%20Driver%20Creation%20By%20Non-Sysinternals%20Binary",
563 + agent_ip_geolocation: "1.3078,103.6818",
564 + data_group: "Sigma",
565 + rule_firedtimes: 1,
566 + data_event_User: "NT AUTHORITY\\SYSTEM",
567 + data_path: "C:\\Windows\\System32\\winevt\\Logs\\Microsoft-Windows-Sysmon%4Operational.evtx",
568 + rule_mail: true,
569 + data_system_Provider_attributes_Name: "Microsoft-Windows-Sysmon",
570 + data_name: "Process Explorer Driver Creation By Non-Sysinternals Binary",
571 + decoder_name: "json",
572 + data_id: "de46c52b-0bf8-4936-a327-aace94f94ac6",
573 + agent_ip_country_code: "SG",
574 + syslog_level: "ALERT",
575 + data_tags: "attack.persistence, attack.privilege_escalation, attack.t1068",
576 + data_kind: "individual",
577 + data_logsource_product: "windows",
578 + timestamp: "2023-11-05 12:16:56.772",
579 + ask_socfortress_message:
580 + "The SIGMA alert \"Process Explorer Driver Creation By Non-Sysinternals Binary\" suggests that a non-Sysinternals binary has attempted to create a Process Explorer driver on a Windows endpoint. Process Explorer is a widely used system monitoring tool developed by Sysinternals (now part of Microsoft). The creation of a Process Explorer driver by a non-Sysinternals binary could indicate suspicious or potentially malicious activity.\n\nTo investigate this alert and determine an appropriate response, you should focus on the following key aspects:\n\n1. Validate the Alert: Verify the accuracy of the alert by checking if it was triggered by legitimate activity or if it is a false positive. Ensure that your detection system is properly configured and up to date.\n\n2. Identify the Binary: Determine which specific binary attempted to create the Process Explorer driver. Look for any unusual or suspicious characteristics such as unfamiliar names, file paths, digital signatures, or hash values.\n\n3. Analyze Process Execution Context: Investigate the context in which the binary executed and attempted to create the driver. Review process execution details, such as parent processes, command-line arguments, process creation time, and associated network connections.\n\n4. Assess System Impact: Evaluate whether any abnormal behavior occurred on the endpoint after this event took place. Look for signs of system instability, crashes, performance degradation, or other anomalous activities that may indicate malicious intent.\n\n5. Conduct File Analysis: Perform an in-depth analysis of the binary itself using appropriate tools like antivirus scanners and sandboxing environments to identify any malware indicators such as malicious code patterns or known signatures associated with malware families.\n\n6. Check Reputation: Research information about the binary's reputation online using threat intelligence platforms, virus total scanners, or security forums to determine if it has been previously identified as malicious.\n\n7. Perform Behavioral Analysis: If feasible, conduct dynamic analysis by executing the binary in an isolated environment while monitoring its behavior for any suspicious activities like network communication, file system changes, or attempts to escalate privileges.\n\n8. Review System Logs: Examine relevant logs such as event logs, process creation logs, driver loading logs, and network logs to identify any additional indicators of compromise or related activities.\n\nWhen assessing this alert, you should also ask yourself the following additional questions:\n\n1. Is the binary a legitimate tool that is commonly used in your organization? If not, why would it be present on the endpoint?\n\n2. Is there a business justification for creating a Process Explorer driver with this specific binary? Are there any documented cases or known legitimate reasons for doing so?\n\n3. Are there any other security events or alerts related to this binary or associated processes that can provide further context?\n\n4. Has the binary been whitelisted or approved by your organization's security policies? If not, why was it allowed to execute on the endpoint?\n\n5. Do you have sufficient visibility into other endpoints within your environment? Have similar events been observed elsewhere? This could indicate a larger-scale attack.\n\nRemember that each investigation may vary based on your organization's specific context and requirements.",
581 + data_system_Channel: "Microsoft-Windows-Sysmon/Operational",
582 + data_references:
583 + "https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer, https://github.com/Yaxser/Backstab, https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks, https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/",
584 + data_event_ProcessGuid: "6D0AAEFA-8781-6547-C4BB-000000004200",
585 + gl2_processing_error:
586 + 'Replaced invalid timestamp value in message <366ba9a0-7bd5-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:16:56.030+0000> caused exception: Invalid format: "2023-11-05T12:16:56.030+0000" is malformed at "T12:16:56.030+0000".',
587 + data_falsepositives:
588 + "Some false positives may occur with legitimate renamed process explorer binaries",
589 + data_event_Image: "C:\\Program Files\\socfortress\\sysinternals\\logonsessions64.exe",
590 + data_system_Execution_attributes_ThreadID: "3804",
591 + message:
592 + '{"true":1699186616.142398,"timestamp":"2023-11-05T12:16:56.030+0000","rule":{"level":12,"description":"Process Explorer Driver Creation By Non-Sysinternals Binary","id":"200051","firedtimes":1,"mail":true,"groups":["windows","chainsaw","sigma"]},"agent":{"id":"097","name":"ANSYDWDC01","ip":"139.180.134.102","labels":{"customer":"00002"}},"manager":{"name":"ASHWZHMA"},"id":"1699186616.690805701","decoder":{"name":"json"},"data":{"id":"de46c52b-0bf8-4936-a327-aace94f94ac6","status":"experimental","group":"Sigma","kind":"individual","document":"{\\"kind\\":\\"evtx\\",\\"path\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\winevt\\\\\\\\Logs\\\\\\\\Microsoft-Windows-Sysmon%4Operational.evtx\\",\\"data\\":{\\"Event\\":{\\"EventData\\":{\\"CreationUtcTime\\":\\"2023-11-01 06:52:11.733\\",\\"Image\\":\\"C:\\\\\\\\Program Files\\\\\\\\socfortress\\\\\\\\sysinternals\\\\\\\\logonsessions64.exe\\",\\"ProcessGuid\\":\\"6D0AAEFA-8781-6547-C4BB-000000004200\\",\\"ProcessId\\":4684,\\"RuleName\\":\\"-\\",\\"TargetFilename\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\drivers\\\\\\\\PROCEXP152.SYS\\",\\"User\\":\\"NT AUTHORITY\\\\\\\\SYSTEM\\",\\"UtcTime\\":\\"2023-11-05 12:16:02.043\\"},\\"System\\":{\\"Channel\\":\\"Microsoft-Windows-Sysmon/Operational\\",\\"Computer\\":\\"ANSYDWDC01.ANMS.LOCAL\\",\\"Correlation\\":null,\\"EventID\\":11,\\"EventRecordID\\":18141034,\\"Execution_attributes\\":{\\"ProcessID\\":2564,\\"ThreadID\\":3804},\\"Keywords\\":\\"0x8000000000000000\\",\\"Level\\":4,\\"Opcode\\":0,\\"Provider_attributes\\":{\\"Guid\\":\\"5770385F-C22A-43E0-BF4C-06F5698FFBD9\\",\\"Name\\":\\"Microsoft-Windows-Sysmon\\"},\\"Security_attributes\\":{\\"UserID\\":\\"S-1-5-18\\"},\\"Task\\":11,\\"TimeCreated_attributes\\":{\\"SystemTime\\":\\"2023-11-05T12:16:02.045670Z\\"},\\"Version\\":2}},\\"Event_attributes\\":{\\"xmlns\\":\\"http://schemas.microsoft.com/win/2004/08/events/event\\"}}}","event":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-8781-6547-C4BB-000000004200","ProcessId":"4684","RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 12:16:02.043"},"path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","system":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":"null","EventID":"11","EventRecordID":"18141034","Execution_attributes":{"ProcessID":"2564","ThreadID":"3804"},"Keywords":"0x8000000000000000","Level":"4","Opcode":"0","Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":"11","TimeCreated_attributes":{"SystemTime":"2023-11-05T12:16:02.045670Z"},"Version":"2"},"name":"Process Explorer Driver Creation By Non-Sysinternals Binary","timestamp":"2023-11-05T12:16:02.045670+00:00","authors":["Florian Roth (Nextron Systems)"],"level":"high","source":"sigma","falsepositives":["Some false positives may occur with legitimate renamed process explorer binaries"],"logsource":{"category":"file_event","product":"windows"},"references":["https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer","https://github.com/Yaxser/Backstab","https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks","https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/"],"tags":["attack.persistence","attack.privilege_escalation","attack.t1068"]},"location":"active-response\\\\active-responses.log"}',
593 + data_system_EventID: "11",
594 + data_system_Provider_attributes_Guid: "5770385F-C22A-43E0-BF4C-06F5698FFBD9",
595 + rule_id: "200051",
596 + manager_name: "ASHWZHMA",
597 + data_event_TargetFilename: "C:\\Windows\\System32\\drivers\\PROCEXP152.SYS",
598 + location: "active-response\\active-responses.log",
599 + data_authors: "Florian Roth (Nextron Systems)",
600 + rule_group3: "sigma",
601 + msg_timestamp: "2023-11-05T12:16:56.030Z",
602 + rule_group2: "chainsaw",
603 + rule_group1: "windows"
604 + },
605 + sort: [1699186562043]
606 + },
607 + {
608 + _index: "wazuh_00002_201",
609 + _id: "2e1383c2-7bd7-11ee-93bc-86000046278a",
610 + _score: null,
611 + _source: {
612 + data_system_Task: "11",
613 + source_reserved_ip: true,
614 + agent_id: "097",
615 + agent_name: "ANSYDWDC01",
616 + data_system_Correlation: "null",
617 + gl2_remote_ip: "10.255.255.13",
618 + gl2_remote_port: 35304,
619 + agent_labels_customer: "00002",
620 + data_system_Version: "2",
621 + agent_ip_city_name: "Singapore",
622 + source: "10.255.255.13",
623 + gl2_source_input: "6459151dea00fd5d3da2df91",
624 + rule_level: 12,
625 + data_level: "high",
626 + timestamp_utc: "2023-11-05T12:16:02.043Z",
627 + data_event_ProcessId: "4684",
628 + syslog_type: "wazuh",
629 + data_system_Opcode: "0",
630 + rule_description: "Process Explorer Driver Creation By Non-Sysinternals Binary",
631 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
632 + id: "1699187457.705230523",
633 + data_status: "experimental",
634 + data_system_Computer: "ANSYDWDC01.ANMS.LOCAL",
635 + gl2_accounted_message_size: 10712,
636 + data_document:
637 + '{"kind":"evtx","path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","data":{"Event":{"EventData":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-8781-6547-C4BB-000000004200","ProcessId":4684,"RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 12:16:02.043"},"System":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":null,"EventID":11,"EventRecordID":18141034,"Execution_attributes":{"ProcessID":2564,"ThreadID":3804},"Keywords":"0x8000000000000000","Level":4,"Opcode":0,"Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":11,"TimeCreated_attributes":{"SystemTime":"2023-11-05T12:16:02.045670Z"},"Version":2}},"Event_attributes":{"xmlns":"http://schemas.microsoft.com/win/2004/08/events/event"}}}',
638 + data_event_UtcTime: "2023-11-05 12:16:02.043",
639 + streams: ["645a3a6123e5cc30bbc0e5dc"],
640 + gl2_message_id: "01HEFQE3FX6ARYDEQYMHJPGTE9",
641 + data_source: "sigma",
642 + agent_ip: "139.180.134.102",
643 + data_system_Security_attributes_UserID: "S-1-5-18",
644 + true: 1699187457.773739,
645 + data_timestamp: "2023-11-05T12:16:02.045670+00:00",
646 + data_system_Level: "4",
647 + data_event_CreationUtcTime: "2023-11-01 06:52:11.733",
648 + data_system_Execution_attributes_ProcessID: "2564",
649 + rule_groups: "windows, chainsaw, sigma",
650 + data_system_EventRecordID: "18141034",
651 + process_id: "4684",
652 + data_system_TimeCreated_attributes_SystemTime: "2023-11-05T12:16:02.045670Z",
653 + data_event_RuleName: "-",
654 + data_logsource_category: "file_event",
655 + data_system_Keywords: "0x8000000000000000",
656 + sigma_name_encoded: "Process%20Explorer%20Driver%20Creation%20By%20Non-Sysinternals%20Binary",
657 + agent_ip_geolocation: "1.3078,103.6818",
658 + data_group: "Sigma",
659 + rule_firedtimes: 2,
660 + data_event_User: "NT AUTHORITY\\SYSTEM",
661 + data_path: "C:\\Windows\\System32\\winevt\\Logs\\Microsoft-Windows-Sysmon%4Operational.evtx",
662 + rule_mail: true,
663 + data_system_Provider_attributes_Name: "Microsoft-Windows-Sysmon",
664 + data_name: "Process Explorer Driver Creation By Non-Sysinternals Binary",
665 + decoder_name: "json",
666 + data_id: "de46c52b-0bf8-4936-a327-aace94f94ac6",
667 + agent_ip_country_code: "SG",
668 + syslog_level: "ALERT",
669 + data_tags: "attack.persistence, attack.privilege_escalation, attack.t1068",
670 + data_kind: "individual",
671 + data_logsource_product: "windows",
672 + timestamp: "2023-11-05 12:31:01.629",
673 + ask_socfortress_message:
674 + "The SIGMA alert \"Process Explorer Driver Creation By Non-Sysinternals Binary\" suggests that a non-Sysinternals binary has attempted to create a Process Explorer driver on a Windows endpoint. Process Explorer is a widely used system monitoring tool developed by Sysinternals (now part of Microsoft). The creation of a Process Explorer driver by a non-Sysinternals binary could indicate suspicious or potentially malicious activity.\n\nTo investigate this alert and determine an appropriate response, you should focus on the following key aspects:\n\n1. Validate the Alert: Verify the accuracy of the alert by checking if it was triggered by legitimate activity or if it is a false positive. Ensure that your detection system is properly configured and up to date.\n\n2. Identify the Binary: Determine which specific binary attempted to create the Process Explorer driver. Look for any unusual or suspicious characteristics such as unfamiliar names, file paths, digital signatures, or hash values.\n\n3. Analyze Process Execution Context: Investigate the context in which the binary executed and attempted to create the driver. Review process execution details, such as parent processes, command-line arguments, process creation time, and associated network connections.\n\n4. Assess System Impact: Evaluate whether any abnormal behavior occurred on the endpoint after this event took place. Look for signs of system instability, crashes, performance degradation, or other anomalous activities that may indicate malicious intent.\n\n5. Conduct File Analysis: Perform an in-depth analysis of the binary itself using appropriate tools like antivirus scanners and sandboxing environments to identify any malware indicators such as malicious code patterns or known signatures associated with malware families.\n\n6. Check Reputation: Research information about the binary's reputation online using threat intelligence platforms, virus total scanners, or security forums to determine if it has been previously identified as malicious.\n\n7. Perform Behavioral Analysis: If feasible, conduct dynamic analysis by executing the binary in an isolated environment while monitoring its behavior for any suspicious activities like network communication, file system changes, or attempts to escalate privileges.\n\n8. Review System Logs: Examine relevant logs such as event logs, process creation logs, driver loading logs, and network logs to identify any additional indicators of compromise or related activities.\n\nWhen assessing this alert, you should also ask yourself the following additional questions:\n\n1. Is the binary a legitimate tool that is commonly used in your organization? If not, why would it be present on the endpoint?\n\n2. Is there a business justification for creating a Process Explorer driver with this specific binary? Are there any documented cases or known legitimate reasons for doing so?\n\n3. Are there any other security events or alerts related to this binary or associated processes that can provide further context?\n\n4. Has the binary been whitelisted or approved by your organization's security policies? If not, why was it allowed to execute on the endpoint?\n\n5. Do you have sufficient visibility into other endpoints within your environment? Have similar events been observed elsewhere? This could indicate a larger-scale attack.\n\nRemember that each investigation may vary based on your organization's specific context and requirements.",
675 + data_system_Channel: "Microsoft-Windows-Sysmon/Operational",
676 + data_references:
677 + "https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer, https://github.com/Yaxser/Backstab, https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks, https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/",
678 + data_event_ProcessGuid: "6D0AAEFA-8781-6547-C4BB-000000004200",
679 + gl2_processing_error:
680 + 'Replaced invalid timestamp value in message <2e1383c2-7bd7-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:30:57.704+0000> caused exception: Invalid format: "2023-11-05T12:30:57.704+0000" is malformed at "T12:30:57.704+0000".',
681 + data_falsepositives:
682 + "Some false positives may occur with legitimate renamed process explorer binaries",
683 + data_event_Image: "C:\\Program Files\\socfortress\\sysinternals\\logonsessions64.exe",
684 + data_system_Execution_attributes_ThreadID: "3804",
685 + message:
686 + '{"true":1699187457.773739,"timestamp":"2023-11-05T12:30:57.704+0000","rule":{"level":12,"description":"Process Explorer Driver Creation By Non-Sysinternals Binary","id":"200051","firedtimes":2,"mail":true,"groups":["windows","chainsaw","sigma"]},"agent":{"id":"097","name":"ANSYDWDC01","ip":"139.180.134.102","labels":{"customer":"00002"}},"manager":{"name":"ASHWZHMA"},"id":"1699187457.705230523","decoder":{"name":"json"},"data":{"id":"de46c52b-0bf8-4936-a327-aace94f94ac6","status":"experimental","group":"Sigma","kind":"individual","document":"{\\"kind\\":\\"evtx\\",\\"path\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\winevt\\\\\\\\Logs\\\\\\\\Microsoft-Windows-Sysmon%4Operational.evtx\\",\\"data\\":{\\"Event\\":{\\"EventData\\":{\\"CreationUtcTime\\":\\"2023-11-01 06:52:11.733\\",\\"Image\\":\\"C:\\\\\\\\Program Files\\\\\\\\socfortress\\\\\\\\sysinternals\\\\\\\\logonsessions64.exe\\",\\"ProcessGuid\\":\\"6D0AAEFA-8781-6547-C4BB-000000004200\\",\\"ProcessId\\":4684,\\"RuleName\\":\\"-\\",\\"TargetFilename\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\drivers\\\\\\\\PROCEXP152.SYS\\",\\"User\\":\\"NT AUTHORITY\\\\\\\\SYSTEM\\",\\"UtcTime\\":\\"2023-11-05 12:16:02.043\\"},\\"System\\":{\\"Channel\\":\\"Microsoft-Windows-Sysmon/Operational\\",\\"Computer\\":\\"ANSYDWDC01.ANMS.LOCAL\\",\\"Correlation\\":null,\\"EventID\\":11,\\"EventRecordID\\":18141034,\\"Execution_attributes\\":{\\"ProcessID\\":2564,\\"ThreadID\\":3804},\\"Keywords\\":\\"0x8000000000000000\\",\\"Level\\":4,\\"Opcode\\":0,\\"Provider_attributes\\":{\\"Guid\\":\\"5770385F-C22A-43E0-BF4C-06F5698FFBD9\\",\\"Name\\":\\"Microsoft-Windows-Sysmon\\"},\\"Security_attributes\\":{\\"UserID\\":\\"S-1-5-18\\"},\\"Task\\":11,\\"TimeCreated_attributes\\":{\\"SystemTime\\":\\"2023-11-05T12:16:02.045670Z\\"},\\"Version\\":2}},\\"Event_attributes\\":{\\"xmlns\\":\\"http://schemas.microsoft.com/win/2004/08/events/event\\"}}}","event":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-8781-6547-C4BB-000000004200","ProcessId":"4684","RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 12:16:02.043"},"path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","system":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":"null","EventID":"11","EventRecordID":"18141034","Execution_attributes":{"ProcessID":"2564","ThreadID":"3804"},"Keywords":"0x8000000000000000","Level":"4","Opcode":"0","Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":"11","TimeCreated_attributes":{"SystemTime":"2023-11-05T12:16:02.045670Z"},"Version":"2"},"name":"Process Explorer Driver Creation By Non-Sysinternals Binary","timestamp":"2023-11-05T12:16:02.045670+00:00","authors":["Florian Roth (Nextron Systems)"],"level":"high","source":"sigma","falsepositives":["Some false positives may occur with legitimate renamed process explorer binaries"],"logsource":{"category":"file_event","product":"windows"},"references":["https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer","https://github.com/Yaxser/Backstab","https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks","https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/"],"tags":["attack.persistence","attack.privilege_escalation","attack.t1068"]},"location":"active-response\\\\active-responses.log"}',
687 + data_system_EventID: "11",
688 + data_system_Provider_attributes_Guid: "5770385F-C22A-43E0-BF4C-06F5698FFBD9",
689 + rule_id: "200051",
690 + manager_name: "ASHWZHMA",
691 + data_event_TargetFilename: "C:\\Windows\\System32\\drivers\\PROCEXP152.SYS",
692 + location: "active-response\\active-responses.log",
693 + data_authors: "Florian Roth (Nextron Systems)",
694 + rule_group3: "sigma",
695 + msg_timestamp: "2023-11-05T12:30:57.704Z",
696 + rule_group2: "chainsaw",
697 + rule_group1: "windows"
698 + },
699 + sort: [1699186562043]
700 + },
701 + {
702 + _index: "wazuh_00002_201",
703 + _id: "d4a60924-7bcc-11ee-93bc-86000046278a",
704 + _score: null,
705 + _source: {
706 + data_system_Task: "11",
707 + source_reserved_ip: true,
708 + agent_id: "097",
709 + agent_name: "ANSYDWDC01",
710 + data_system_Correlation: "null",
711 + gl2_remote_ip: "10.255.255.13",
712 + gl2_remote_port: 38584,
713 + agent_labels_customer: "00002",
714 + data_system_Version: "2",
715 + agent_ip_city_name: "Singapore",
716 + source: "10.255.255.13",
717 + gl2_source_input: "6459151dea00fd5d3da2df91",
718 + rule_level: 12,
719 + data_level: "high",
720 + timestamp_utc: "2023-11-05T11:16:02.307Z",
721 + data_event_ProcessId: "6712",
722 + syslog_type: "wazuh",
723 + data_system_Opcode: "0",
724 + rule_description: "Process Explorer Driver Creation By Non-Sysinternals Binary",
725 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
726 + id: "1699183014.641989382",
727 + data_status: "experimental",
728 + data_system_Computer: "ANSYDWDC01.ANMS.LOCAL",
729 + gl2_accounted_message_size: 10712,
730 + data_document:
731 + '{"kind":"evtx","path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","data":{"Event":{"EventData":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-7972-6547-E6B9-000000004200","ProcessId":6712,"RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 11:16:02.307"},"System":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":null,"EventID":11,"EventRecordID":18140573,"Execution_attributes":{"ProcessID":2564,"ThreadID":3804},"Keywords":"0x8000000000000000","Level":4,"Opcode":0,"Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":11,"TimeCreated_attributes":{"SystemTime":"2023-11-05T11:16:02.321071Z"},"Version":2}},"Event_attributes":{"xmlns":"http://schemas.microsoft.com/win/2004/08/events/event"}}}',
732 + data_event_UtcTime: "2023-11-05 11:16:02.307",
733 + streams: ["645a3a6123e5cc30bbc0e5dc"],
734 + gl2_message_id: "01HEFK6ESE3ZHZY3GH5THZQ7VS",
735 + data_source: "sigma",
736 + agent_ip: "139.180.134.102",
737 + data_system_Security_attributes_UserID: "S-1-5-18",
738 + true: 1699183014.546914,
739 + data_timestamp: "2023-11-05T11:16:02.321071+00:00",
740 + data_system_Level: "4",
741 + data_event_CreationUtcTime: "2023-11-01 06:52:11.733",
742 + data_system_Execution_attributes_ProcessID: "2564",
743 + rule_groups: "windows, chainsaw, sigma",
744 + data_system_EventRecordID: "18140573",
745 + process_id: "6712",
746 + data_system_TimeCreated_attributes_SystemTime: "2023-11-05T11:16:02.321071Z",
747 + data_event_RuleName: "-",
748 + data_logsource_category: "file_event",
749 + data_system_Keywords: "0x8000000000000000",
750 + sigma_name_encoded: "Process%20Explorer%20Driver%20Creation%20By%20Non-Sysinternals%20Binary",
751 + agent_ip_geolocation: "1.3078,103.6818",
752 + data_group: "Sigma",
753 + rule_firedtimes: 1,
754 + data_event_User: "NT AUTHORITY\\SYSTEM",
755 + data_path: "C:\\Windows\\System32\\winevt\\Logs\\Microsoft-Windows-Sysmon%4Operational.evtx",
756 + rule_mail: true,
757 + data_system_Provider_attributes_Name: "Microsoft-Windows-Sysmon",
758 + data_name: "Process Explorer Driver Creation By Non-Sysinternals Binary",
759 + decoder_name: "json",
760 + data_id: "de46c52b-0bf8-4936-a327-aace94f94ac6",
761 + agent_ip_country_code: "SG",
762 + syslog_level: "ALERT",
763 + data_tags: "attack.persistence, attack.privilege_escalation, attack.t1068",
764 + data_kind: "individual",
765 + data_logsource_product: "windows",
766 + timestamp: "2023-11-05 11:16:56.750",
767 + ask_socfortress_message:
768 + "The SIGMA alert \"Process Explorer Driver Creation By Non-Sysinternals Binary\" suggests that a non-Sysinternals binary has attempted to create a Process Explorer driver on a Windows endpoint. Process Explorer is a widely used system monitoring tool developed by Sysinternals (now part of Microsoft). The creation of a Process Explorer driver by a non-Sysinternals binary could indicate suspicious or potentially malicious activity.\n\nTo investigate this alert and determine an appropriate response, you should focus on the following key aspects:\n\n1. Validate the Alert: Verify the accuracy of the alert by checking if it was triggered by legitimate activity or if it is a false positive. Ensure that your detection system is properly configured and up to date.\n\n2. Identify the Binary: Determine which specific binary attempted to create the Process Explorer driver. Look for any unusual or suspicious characteristics such as unfamiliar names, file paths, digital signatures, or hash values.\n\n3. Analyze Process Execution Context: Investigate the context in which the binary executed and attempted to create the driver. Review process execution details, such as parent processes, command-line arguments, process creation time, and associated network connections.\n\n4. Assess System Impact: Evaluate whether any abnormal behavior occurred on the endpoint after this event took place. Look for signs of system instability, crashes, performance degradation, or other anomalous activities that may indicate malicious intent.\n\n5. Conduct File Analysis: Perform an in-depth analysis of the binary itself using appropriate tools like antivirus scanners and sandboxing environments to identify any malware indicators such as malicious code patterns or known signatures associated with malware families.\n\n6. Check Reputation: Research information about the binary's reputation online using threat intelligence platforms, virus total scanners, or security forums to determine if it has been previously identified as malicious.\n\n7. Perform Behavioral Analysis: If feasible, conduct dynamic analysis by executing the binary in an isolated environment while monitoring its behavior for any suspicious activities like network communication, file system changes, or attempts to escalate privileges.\n\n8. Review System Logs: Examine relevant logs such as event logs, process creation logs, driver loading logs, and network logs to identify any additional indicators of compromise or related activities.\n\nWhen assessing this alert, you should also ask yourself the following additional questions:\n\n1. Is the binary a legitimate tool that is commonly used in your organization? If not, why would it be present on the endpoint?\n\n2. Is there a business justification for creating a Process Explorer driver with this specific binary? Are there any documented cases or known legitimate reasons for doing so?\n\n3. Are there any other security events or alerts related to this binary or associated processes that can provide further context?\n\n4. Has the binary been whitelisted or approved by your organization's security policies? If not, why was it allowed to execute on the endpoint?\n\n5. Do you have sufficient visibility into other endpoints within your environment? Have similar events been observed elsewhere? This could indicate a larger-scale attack.\n\nRemember that each investigation may vary based on your organization's specific context and requirements.",
769 + data_system_Channel: "Microsoft-Windows-Sysmon/Operational",
770 + data_references:
771 + "https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer, https://github.com/Yaxser/Backstab, https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks, https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/",
772 + data_event_ProcessGuid: "6D0AAEFA-7972-6547-E6B9-000000004200",
773 + gl2_processing_error:
774 + 'Replaced invalid timestamp value in message <d4a60924-7bcc-11ee-93bc-86000046278a> with current time - Value <2023-11-05T11:16:54.542+0000> caused exception: Invalid format: "2023-11-05T11:16:54.542+0000" is malformed at "T11:16:54.542+0000".',
775 + data_falsepositives:
776 + "Some false positives may occur with legitimate renamed process explorer binaries",
777 + data_event_Image: "C:\\Program Files\\socfortress\\sysinternals\\logonsessions64.exe",
778 + data_system_Execution_attributes_ThreadID: "3804",
779 + message:
780 + '{"true":1699183014.546914,"timestamp":"2023-11-05T11:16:54.542+0000","rule":{"level":12,"description":"Process Explorer Driver Creation By Non-Sysinternals Binary","id":"200051","firedtimes":1,"mail":true,"groups":["windows","chainsaw","sigma"]},"agent":{"id":"097","name":"ANSYDWDC01","ip":"139.180.134.102","labels":{"customer":"00002"}},"manager":{"name":"ASHWZHMA"},"id":"1699183014.641989382","decoder":{"name":"json"},"data":{"id":"de46c52b-0bf8-4936-a327-aace94f94ac6","status":"experimental","group":"Sigma","kind":"individual","document":"{\\"kind\\":\\"evtx\\",\\"path\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\winevt\\\\\\\\Logs\\\\\\\\Microsoft-Windows-Sysmon%4Operational.evtx\\",\\"data\\":{\\"Event\\":{\\"EventData\\":{\\"CreationUtcTime\\":\\"2023-11-01 06:52:11.733\\",\\"Image\\":\\"C:\\\\\\\\Program Files\\\\\\\\socfortress\\\\\\\\sysinternals\\\\\\\\logonsessions64.exe\\",\\"ProcessGuid\\":\\"6D0AAEFA-7972-6547-E6B9-000000004200\\",\\"ProcessId\\":6712,\\"RuleName\\":\\"-\\",\\"TargetFilename\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\drivers\\\\\\\\PROCEXP152.SYS\\",\\"User\\":\\"NT AUTHORITY\\\\\\\\SYSTEM\\",\\"UtcTime\\":\\"2023-11-05 11:16:02.307\\"},\\"System\\":{\\"Channel\\":\\"Microsoft-Windows-Sysmon/Operational\\",\\"Computer\\":\\"ANSYDWDC01.ANMS.LOCAL\\",\\"Correlation\\":null,\\"EventID\\":11,\\"EventRecordID\\":18140573,\\"Execution_attributes\\":{\\"ProcessID\\":2564,\\"ThreadID\\":3804},\\"Keywords\\":\\"0x8000000000000000\\",\\"Level\\":4,\\"Opcode\\":0,\\"Provider_attributes\\":{\\"Guid\\":\\"5770385F-C22A-43E0-BF4C-06F5698FFBD9\\",\\"Name\\":\\"Microsoft-Windows-Sysmon\\"},\\"Security_attributes\\":{\\"UserID\\":\\"S-1-5-18\\"},\\"Task\\":11,\\"TimeCreated_attributes\\":{\\"SystemTime\\":\\"2023-11-05T11:16:02.321071Z\\"},\\"Version\\":2}},\\"Event_attributes\\":{\\"xmlns\\":\\"http://schemas.microsoft.com/win/2004/08/events/event\\"}}}","event":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-7972-6547-E6B9-000000004200","ProcessId":"6712","RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 11:16:02.307"},"path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","system":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":"null","EventID":"11","EventRecordID":"18140573","Execution_attributes":{"ProcessID":"2564","ThreadID":"3804"},"Keywords":"0x8000000000000000","Level":"4","Opcode":"0","Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":"11","TimeCreated_attributes":{"SystemTime":"2023-11-05T11:16:02.321071Z"},"Version":"2"},"name":"Process Explorer Driver Creation By Non-Sysinternals Binary","timestamp":"2023-11-05T11:16:02.321071+00:00","authors":["Florian Roth (Nextron Systems)"],"level":"high","source":"sigma","falsepositives":["Some false positives may occur with legitimate renamed process explorer binaries"],"logsource":{"category":"file_event","product":"windows"},"references":["https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer","https://github.com/Yaxser/Backstab","https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks","https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/"],"tags":["attack.persistence","attack.privilege_escalation","attack.t1068"]},"location":"active-response\\\\active-responses.log"}',
781 + data_system_EventID: "11",
782 + data_system_Provider_attributes_Guid: "5770385F-C22A-43E0-BF4C-06F5698FFBD9",
783 + rule_id: "200051",
784 + manager_name: "ASHWZHMA",
785 + data_event_TargetFilename: "C:\\Windows\\System32\\drivers\\PROCEXP152.SYS",
786 + location: "active-response\\active-responses.log",
787 + data_authors: "Florian Roth (Nextron Systems)",
788 + rule_group3: "sigma",
789 + msg_timestamp: "2023-11-05T11:16:54.542Z",
790 + rule_group2: "chainsaw",
791 + rule_group1: "windows"
792 + },
793 + sort: [1699182962307]
794 + },
795 + {
796 + _index: "wazuh_00002_201",
797 + _id: "cf49be20-7bce-11ee-93bc-86000046278a",
798 + _score: null,
799 + _source: {
800 + data_system_Task: "11",
801 + source_reserved_ip: true,
802 + agent_id: "097",
803 + agent_name: "ANSYDWDC01",
804 + data_system_Correlation: "null",
805 + gl2_remote_ip: "10.255.255.13",
806 + gl2_remote_port: 49198,
807 + agent_labels_customer: "00002",
808 + data_system_Version: "2",
809 + agent_ip_city_name: "Singapore",
810 + source: "10.255.255.13",
811 + gl2_source_input: "6459151dea00fd5d3da2df91",
812 + rule_level: 12,
813 + data_level: "high",
814 + timestamp_utc: "2023-11-05T11:16:02.307Z",
815 + data_event_ProcessId: "6712",
816 + syslog_type: "wazuh",
817 + data_system_Opcode: "0",
818 + rule_description: "Process Explorer Driver Creation By Non-Sysinternals Binary",
819 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
820 + id: "1699183862.653635812",
821 + data_status: "experimental",
822 + data_system_Computer: "ANSYDWDC01.ANMS.LOCAL",
823 + gl2_accounted_message_size: 10712,
824 + data_document:
825 + '{"kind":"evtx","path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","data":{"Event":{"EventData":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-7972-6547-E6B9-000000004200","ProcessId":6712,"RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 11:16:02.307"},"System":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":null,"EventID":11,"EventRecordID":18140573,"Execution_attributes":{"ProcessID":2564,"ThreadID":3804},"Keywords":"0x8000000000000000","Level":4,"Opcode":0,"Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":11,"TimeCreated_attributes":{"SystemTime":"2023-11-05T11:16:02.321071Z"},"Version":2}},"Event_attributes":{"xmlns":"http://schemas.microsoft.com/win/2004/08/events/event"}}}',
826 + data_event_UtcTime: "2023-11-05 11:16:02.307",
827 + streams: ["645a3a6123e5cc30bbc0e5dc"],
828 + gl2_message_id: "01HEFM0CR3M66JCE9GK8X0BSHV",
829 + data_source: "sigma",
830 + agent_ip: "139.180.134.102",
831 + data_system_Security_attributes_UserID: "S-1-5-18",
832 + true: 1699183863.254522,
833 + data_timestamp: "2023-11-05T11:16:02.321071+00:00",
834 + data_system_Level: "4",
835 + data_event_CreationUtcTime: "2023-11-01 06:52:11.733",
836 + data_system_Execution_attributes_ProcessID: "2564",
837 + rule_groups: "windows, chainsaw, sigma",
838 + data_system_EventRecordID: "18140573",
839 + process_id: "6712",
840 + data_system_TimeCreated_attributes_SystemTime: "2023-11-05T11:16:02.321071Z",
841 + data_event_RuleName: "-",
842 + data_logsource_category: "file_event",
843 + data_system_Keywords: "0x8000000000000000",
844 + sigma_name_encoded: "Process%20Explorer%20Driver%20Creation%20By%20Non-Sysinternals%20Binary",
845 + agent_ip_geolocation: "1.3078,103.6818",
846 + data_group: "Sigma",
847 + rule_firedtimes: 2,
848 + data_event_User: "NT AUTHORITY\\SYSTEM",
849 + data_path: "C:\\Windows\\System32\\winevt\\Logs\\Microsoft-Windows-Sysmon%4Operational.evtx",
850 + rule_mail: true,
851 + data_system_Provider_attributes_Name: "Microsoft-Windows-Sysmon",
852 + data_name: "Process Explorer Driver Creation By Non-Sysinternals Binary",
853 + decoder_name: "json",
854 + data_id: "de46c52b-0bf8-4936-a327-aace94f94ac6",
855 + agent_ip_country_code: "SG",
856 + syslog_level: "ALERT",
857 + data_tags: "attack.persistence, attack.privilege_escalation, attack.t1068",
858 + data_kind: "individual",
859 + data_logsource_product: "windows",
860 + timestamp: "2023-11-05 11:31:06.627",
861 + ask_socfortress_message:
862 + "The SIGMA alert \"Process Explorer Driver Creation By Non-Sysinternals Binary\" suggests that a non-Sysinternals binary has attempted to create a Process Explorer driver on a Windows endpoint. Process Explorer is a widely used system monitoring tool developed by Sysinternals (now part of Microsoft). The creation of a Process Explorer driver by a non-Sysinternals binary could indicate suspicious or potentially malicious activity.\n\nTo investigate this alert and determine an appropriate response, you should focus on the following key aspects:\n\n1. Validate the Alert: Verify the accuracy of the alert by checking if it was triggered by legitimate activity or if it is a false positive. Ensure that your detection system is properly configured and up to date.\n\n2. Identify the Binary: Determine which specific binary attempted to create the Process Explorer driver. Look for any unusual or suspicious characteristics such as unfamiliar names, file paths, digital signatures, or hash values.\n\n3. Analyze Process Execution Context: Investigate the context in which the binary executed and attempted to create the driver. Review process execution details, such as parent processes, command-line arguments, process creation time, and associated network connections.\n\n4. Assess System Impact: Evaluate whether any abnormal behavior occurred on the endpoint after this event took place. Look for signs of system instability, crashes, performance degradation, or other anomalous activities that may indicate malicious intent.\n\n5. Conduct File Analysis: Perform an in-depth analysis of the binary itself using appropriate tools like antivirus scanners and sandboxing environments to identify any malware indicators such as malicious code patterns or known signatures associated with malware families.\n\n6. Check Reputation: Research information about the binary's reputation online using threat intelligence platforms, virus total scanners, or security forums to determine if it has been previously identified as malicious.\n\n7. Perform Behavioral Analysis: If feasible, conduct dynamic analysis by executing the binary in an isolated environment while monitoring its behavior for any suspicious activities like network communication, file system changes, or attempts to escalate privileges.\n\n8. Review System Logs: Examine relevant logs such as event logs, process creation logs, driver loading logs, and network logs to identify any additional indicators of compromise or related activities.\n\nWhen assessing this alert, you should also ask yourself the following additional questions:\n\n1. Is the binary a legitimate tool that is commonly used in your organization? If not, why would it be present on the endpoint?\n\n2. Is there a business justification for creating a Process Explorer driver with this specific binary? Are there any documented cases or known legitimate reasons for doing so?\n\n3. Are there any other security events or alerts related to this binary or associated processes that can provide further context?\n\n4. Has the binary been whitelisted or approved by your organization's security policies? If not, why was it allowed to execute on the endpoint?\n\n5. Do you have sufficient visibility into other endpoints within your environment? Have similar events been observed elsewhere? This could indicate a larger-scale attack.\n\nRemember that each investigation may vary based on your organization's specific context and requirements.",
863 + data_system_Channel: "Microsoft-Windows-Sysmon/Operational",
864 + data_references:
865 + "https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer, https://github.com/Yaxser/Backstab, https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks, https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/",
866 + data_event_ProcessGuid: "6D0AAEFA-7972-6547-E6B9-000000004200",
867 + gl2_processing_error:
868 + 'Replaced invalid timestamp value in message <cf49be20-7bce-11ee-93bc-86000046278a> with current time - Value <2023-11-05T11:31:02.897+0000> caused exception: Invalid format: "2023-11-05T11:31:02.897+0000" is malformed at "T11:31:02.897+0000".',
869 + data_falsepositives:
870 + "Some false positives may occur with legitimate renamed process explorer binaries",
871 + data_event_Image: "C:\\Program Files\\socfortress\\sysinternals\\logonsessions64.exe",
872 + data_system_Execution_attributes_ThreadID: "3804",
873 + message:
874 + '{"true":1699183863.254522,"timestamp":"2023-11-05T11:31:02.897+0000","rule":{"level":12,"description":"Process Explorer Driver Creation By Non-Sysinternals Binary","id":"200051","firedtimes":2,"mail":true,"groups":["windows","chainsaw","sigma"]},"agent":{"id":"097","name":"ANSYDWDC01","ip":"139.180.134.102","labels":{"customer":"00002"}},"manager":{"name":"ASHWZHMA"},"id":"1699183862.653635812","decoder":{"name":"json"},"data":{"id":"de46c52b-0bf8-4936-a327-aace94f94ac6","status":"experimental","group":"Sigma","kind":"individual","document":"{\\"kind\\":\\"evtx\\",\\"path\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\winevt\\\\\\\\Logs\\\\\\\\Microsoft-Windows-Sysmon%4Operational.evtx\\",\\"data\\":{\\"Event\\":{\\"EventData\\":{\\"CreationUtcTime\\":\\"2023-11-01 06:52:11.733\\",\\"Image\\":\\"C:\\\\\\\\Program Files\\\\\\\\socfortress\\\\\\\\sysinternals\\\\\\\\logonsessions64.exe\\",\\"ProcessGuid\\":\\"6D0AAEFA-7972-6547-E6B9-000000004200\\",\\"ProcessId\\":6712,\\"RuleName\\":\\"-\\",\\"TargetFilename\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\drivers\\\\\\\\PROCEXP152.SYS\\",\\"User\\":\\"NT AUTHORITY\\\\\\\\SYSTEM\\",\\"UtcTime\\":\\"2023-11-05 11:16:02.307\\"},\\"System\\":{\\"Channel\\":\\"Microsoft-Windows-Sysmon/Operational\\",\\"Computer\\":\\"ANSYDWDC01.ANMS.LOCAL\\",\\"Correlation\\":null,\\"EventID\\":11,\\"EventRecordID\\":18140573,\\"Execution_attributes\\":{\\"ProcessID\\":2564,\\"ThreadID\\":3804},\\"Keywords\\":\\"0x8000000000000000\\",\\"Level\\":4,\\"Opcode\\":0,\\"Provider_attributes\\":{\\"Guid\\":\\"5770385F-C22A-43E0-BF4C-06F5698FFBD9\\",\\"Name\\":\\"Microsoft-Windows-Sysmon\\"},\\"Security_attributes\\":{\\"UserID\\":\\"S-1-5-18\\"},\\"Task\\":11,\\"TimeCreated_attributes\\":{\\"SystemTime\\":\\"2023-11-05T11:16:02.321071Z\\"},\\"Version\\":2}},\\"Event_attributes\\":{\\"xmlns\\":\\"http://schemas.microsoft.com/win/2004/08/events/event\\"}}}","event":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-7972-6547-E6B9-000000004200","ProcessId":"6712","RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 11:16:02.307"},"path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","system":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":"null","EventID":"11","EventRecordID":"18140573","Execution_attributes":{"ProcessID":"2564","ThreadID":"3804"},"Keywords":"0x8000000000000000","Level":"4","Opcode":"0","Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":"11","TimeCreated_attributes":{"SystemTime":"2023-11-05T11:16:02.321071Z"},"Version":"2"},"name":"Process Explorer Driver Creation By Non-Sysinternals Binary","timestamp":"2023-11-05T11:16:02.321071+00:00","authors":["Florian Roth (Nextron Systems)"],"level":"high","source":"sigma","falsepositives":["Some false positives may occur with legitimate renamed process explorer binaries"],"logsource":{"category":"file_event","product":"windows"},"references":["https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer","https://github.com/Yaxser/Backstab","https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks","https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/"],"tags":["attack.persistence","attack.privilege_escalation","attack.t1068"]},"location":"active-response\\\\active-responses.log"}',
875 + data_system_EventID: "11",
876 + data_system_Provider_attributes_Guid: "5770385F-C22A-43E0-BF4C-06F5698FFBD9",
877 + rule_id: "200051",
878 + manager_name: "ASHWZHMA",
879 + data_event_TargetFilename: "C:\\Windows\\System32\\drivers\\PROCEXP152.SYS",
880 + location: "active-response\\active-responses.log",
881 + data_authors: "Florian Roth (Nextron Systems)",
882 + rule_group3: "sigma",
883 + msg_timestamp: "2023-11-05T11:31:02.897Z",
884 + rule_group2: "chainsaw",
885 + rule_group1: "windows"
886 + },
887 + sort: [1699182962307]
888 + },
889 + {
890 + _index: "wazuh_00002_201",
891 + _id: "75dcb8b2-7bc4-11ee-93bc-86000046278a",
892 + _score: null,
893 + _source: {
894 + data_system_Task: "11",
895 + source_reserved_ip: true,
896 + agent_id: "097",
897 + agent_name: "ANSYDWDC01",
898 + data_system_Correlation: "null",
899 + gl2_remote_ip: "10.255.255.13",
900 + gl2_remote_port: 59674,
901 + agent_labels_customer: "00002",
902 + data_system_Version: "2",
903 + agent_ip_city_name: "Singapore",
904 + source: "10.255.255.13",
905 + gl2_source_input: "6459151dea00fd5d3da2df91",
906 + rule_level: 12,
907 + data_level: "high",
908 + timestamp_utc: "2023-11-05T10:16:05.625Z",
909 + data_event_ProcessId: "5020",
910 + syslog_type: "wazuh",
911 + data_system_Opcode: "0",
912 + rule_description: "Process Explorer Driver Creation By Non-Sysinternals Binary",
913 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
914 + id: "1699179418.594692359",
915 + data_status: "experimental",
916 + data_system_Computer: "ANSYDWDC01.ANMS.LOCAL",
917 + gl2_accounted_message_size: 11648,
918 + data_document:
919 + '{"kind":"evtx","path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","data":{"Event":{"EventData":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-6B64-6547-61B8-000000004200","ProcessId":5020,"RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 10:16:05.625"},"System":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":null,"EventID":11,"EventRecordID":18140150,"Execution_attributes":{"ProcessID":2564,"ThreadID":3804},"Keywords":"0x8000000000000000","Level":4,"Opcode":0,"Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":11,"TimeCreated_attributes":{"SystemTime":"2023-11-05T10:16:05.635338Z"},"Version":2}},"Event_attributes":{"xmlns":"http://schemas.microsoft.com/win/2004/08/events/event"}}}',
920 + data_event_UtcTime: "2023-11-05 10:16:05.625",
921 + streams: ["645a3a6123e5cc30bbc0e5dc"],
922 + gl2_message_id: "01HEFFRR246C2SFHZZNDKNXRYP",
923 + data_source: "sigma",
924 + agent_ip: "139.180.134.102",
925 + data_system_Security_attributes_UserID: "S-1-5-18",
926 + true: 1699179418.395436,
927 + data_timestamp: "2023-11-05T10:16:05.635338+00:00",
928 + data_system_Level: "4",
929 + data_event_CreationUtcTime: "2023-11-01 06:52:11.733",
930 + data_system_Execution_attributes_ProcessID: "2564",
931 + rule_groups: "windows, chainsaw, sigma",
932 + data_system_EventRecordID: "18140150",
933 + process_id: "5020",
934 + data_system_TimeCreated_attributes_SystemTime: "2023-11-05T10:16:05.635338Z",
935 + data_event_RuleName: "-",
936 + data_logsource_category: "file_event",
937 + data_system_Keywords: "0x8000000000000000",
938 + sigma_name_encoded: "Process%20Explorer%20Driver%20Creation%20By%20Non-Sysinternals%20Binary",
939 + agent_ip_geolocation: "1.3078,103.6818",
940 + data_group: "Sigma",
941 + rule_firedtimes: 1,
942 + data_event_User: "NT AUTHORITY\\SYSTEM",
943 + data_path: "C:\\Windows\\System32\\winevt\\Logs\\Microsoft-Windows-Sysmon%4Operational.evtx",
944 + rule_mail: true,
945 + data_system_Provider_attributes_Name: "Microsoft-Windows-Sysmon",
946 + data_name: "Process Explorer Driver Creation By Non-Sysinternals Binary",
947 + decoder_name: "json",
948 + data_id: "de46c52b-0bf8-4936-a327-aace94f94ac6",
949 + agent_ip_country_code: "SG",
950 + syslog_level: "ALERT",
951 + data_tags: "attack.persistence, attack.privilege_escalation, attack.t1068",
952 + data_kind: "individual",
953 + data_logsource_product: "windows",
954 + timestamp: "2023-11-05 10:17:01.764",
955 + ask_socfortress_message:
956 + "The SIGMA alert \"Process Explorer Driver Creation By Non-Sysinternals Binary\" indicates that a Windows endpoint has detected an instance where a process explorer driver was created by a binary that is not associated with Microsoft Sysinternals, a legitimate software tool widely used for system monitoring and troubleshooting.\n\nThis alert suggests the possibility of malicious activity on the endpoint. Attackers often leverage process explorer drivers or similar techniques to gain unauthorized access, elevate privileges, or hide their presence on a system. As such, it is important to thoroughly investigate this alert to determine the appropriate response.\n\nHere are key aspects you should investigate when responding to this alert:\n\n1. Endpoint Details: Gather information about the affected endpoint such as its hostname, IP address, operating system version, and any other relevant details. This information will help in understanding the context and potential impact of the alert.\n\n2. Timestamp and Correlation: Identify when the event occurred and check for any correlation with other security events or alerts on the same endpoint or across your environment. This can help determine if it's an isolated incident or part of a broader attack pattern.\n\n3. Process Explorer Driver: Determine which specific driver was created and by which binary it was created. Identify its location on disk and inspect its file properties (e.g., name, size, creation date). Compare these details against known legitimate drivers associated with Microsoft Sysinternals tools.\n\n4. Binary Analysis: Conduct further analysis of the non-Sysinternals binary responsible for creating the process explorer driver. Scan it using antivirus/anti-malware tools to identify any potential malicious behavior or indicators of compromise (IOCs). Consider submitting samples to threat intelligence platforms for additional analysis.\n\n5. Process Information: Examine details about the process associated with creating this driver (e.g., process ID (PID), parent PID) to understand how it was initiated and by what means.\n\n6. System Logs: Review relevant logs such as event logs, system logs, and security logs to identify any suspicious activities or additional indicators of compromise. Look for any abnormal system behavior or unauthorized modifications.\n\n7. User Context: Determine the user account associated with the process that initiated the driver creation. Check if it is a privileged account or a standard user account. If it is a privileged account, investigate whether this activity was expected and authorized.\n\n8. Network Activity: Analyze network traffic logs to identify any communication originating from the affected endpoint during or after the event. Look for connections to suspicious IP addresses, domains, or known command-and-control servers.\n\n9. Endpoint Security Posture: Evaluate the security controls deployed on the affected endpoint, such as antivirus/anti-malware software, intrusion prevention systems (IPS), host-based firewalls, and endpoint detection and response (EDR) solutions. Determine if these controls detected or blocked any malicious activity related to this alert.\n\n10. Incident Response Plan: Assess your organization's incident response plan and determine if there are predefined steps for responding to similar alerts like this one. Follow established procedures to contain the incident, mitigate risks, remediate affected systems, and prevent future occurrences.\n\nAdditional questions you should ask yourself when assessing this alert:\n\n1. Is there any legitimate reason for a non-Sysinternals binary to create a process explorer driver on this endpoint?\n2. Have there been any recent changes in software deployment or system configuration that could explain this alert?\n3. Are there other endpoints in your environment running similar binaries that could trigger similar alerts?\n4. Are there any recent reports of malware campaigns targeting process explorer drivers or abusing legitimate tools like Sysinternals?\n5. Are there any indicators suggesting compromise on this endpoint beyond just the creation of the driver?\n\nBy thoroughly investigating these aspects and considering additional relevant questions specific to your environment, you can make an informed decision about how best to respond to this SIGMA alert and mitigate potential risks.",
957 + data_system_Channel: "Microsoft-Windows-Sysmon/Operational",
958 + data_references:
959 + "https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer, https://github.com/Yaxser/Backstab, https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks, https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/",
960 + data_event_ProcessGuid: "6D0AAEFA-6B64-6547-61B8-000000004200",
961 + gl2_processing_error:
962 + 'Replaced invalid timestamp value in message <75dcb8b2-7bc4-11ee-93bc-86000046278a> with current time - Value <2023-11-05T10:16:58.328+0000> caused exception: Invalid format: "2023-11-05T10:16:58.328+0000" is malformed at "T10:16:58.328+0000".',
963 + data_falsepositives:
964 + "Some false positives may occur with legitimate renamed process explorer binaries",
965 + data_event_Image: "C:\\Program Files\\socfortress\\sysinternals\\logonsessions64.exe",
966 + data_system_Execution_attributes_ThreadID: "3804",
967 + message:
968 + '{"true":1699179418.395436,"timestamp":"2023-11-05T10:16:58.328+0000","rule":{"level":12,"description":"Process Explorer Driver Creation By Non-Sysinternals Binary","id":"200051","firedtimes":1,"mail":true,"groups":["windows","chainsaw","sigma"]},"agent":{"id":"097","name":"ANSYDWDC01","ip":"139.180.134.102","labels":{"customer":"00002"}},"manager":{"name":"ASHWZHMA"},"id":"1699179418.594692359","decoder":{"name":"json"},"data":{"id":"de46c52b-0bf8-4936-a327-aace94f94ac6","status":"experimental","group":"Sigma","kind":"individual","document":"{\\"kind\\":\\"evtx\\",\\"path\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\winevt\\\\\\\\Logs\\\\\\\\Microsoft-Windows-Sysmon%4Operational.evtx\\",\\"data\\":{\\"Event\\":{\\"EventData\\":{\\"CreationUtcTime\\":\\"2023-11-01 06:52:11.733\\",\\"Image\\":\\"C:\\\\\\\\Program Files\\\\\\\\socfortress\\\\\\\\sysinternals\\\\\\\\logonsessions64.exe\\",\\"ProcessGuid\\":\\"6D0AAEFA-6B64-6547-61B8-000000004200\\",\\"ProcessId\\":5020,\\"RuleName\\":\\"-\\",\\"TargetFilename\\":\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\drivers\\\\\\\\PROCEXP152.SYS\\",\\"User\\":\\"NT AUTHORITY\\\\\\\\SYSTEM\\",\\"UtcTime\\":\\"2023-11-05 10:16:05.625\\"},\\"System\\":{\\"Channel\\":\\"Microsoft-Windows-Sysmon/Operational\\",\\"Computer\\":\\"ANSYDWDC01.ANMS.LOCAL\\",\\"Correlation\\":null,\\"EventID\\":11,\\"EventRecordID\\":18140150,\\"Execution_attributes\\":{\\"ProcessID\\":2564,\\"ThreadID\\":3804},\\"Keywords\\":\\"0x8000000000000000\\",\\"Level\\":4,\\"Opcode\\":0,\\"Provider_attributes\\":{\\"Guid\\":\\"5770385F-C22A-43E0-BF4C-06F5698FFBD9\\",\\"Name\\":\\"Microsoft-Windows-Sysmon\\"},\\"Security_attributes\\":{\\"UserID\\":\\"S-1-5-18\\"},\\"Task\\":11,\\"TimeCreated_attributes\\":{\\"SystemTime\\":\\"2023-11-05T10:16:05.635338Z\\"},\\"Version\\":2}},\\"Event_attributes\\":{\\"xmlns\\":\\"http://schemas.microsoft.com/win/2004/08/events/event\\"}}}","event":{"CreationUtcTime":"2023-11-01 06:52:11.733","Image":"C:\\\\Program Files\\\\socfortress\\\\sysinternals\\\\logonsessions64.exe","ProcessGuid":"6D0AAEFA-6B64-6547-61B8-000000004200","ProcessId":"5020","RuleName":"-","TargetFilename":"C:\\\\Windows\\\\System32\\\\drivers\\\\PROCEXP152.SYS","User":"NT AUTHORITY\\\\SYSTEM","UtcTime":"2023-11-05 10:16:05.625"},"path":"C:\\\\Windows\\\\System32\\\\winevt\\\\Logs\\\\Microsoft-Windows-Sysmon%4Operational.evtx","system":{"Channel":"Microsoft-Windows-Sysmon/Operational","Computer":"ANSYDWDC01.ANMS.LOCAL","Correlation":"null","EventID":"11","EventRecordID":"18140150","Execution_attributes":{"ProcessID":"2564","ThreadID":"3804"},"Keywords":"0x8000000000000000","Level":"4","Opcode":"0","Provider_attributes":{"Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9","Name":"Microsoft-Windows-Sysmon"},"Security_attributes":{"UserID":"S-1-5-18"},"Task":"11","TimeCreated_attributes":{"SystemTime":"2023-11-05T10:16:05.635338Z"},"Version":"2"},"name":"Process Explorer Driver Creation By Non-Sysinternals Binary","timestamp":"2023-11-05T10:16:05.635338+00:00","authors":["Florian Roth (Nextron Systems)"],"level":"high","source":"sigma","falsepositives":["Some false positives may occur with legitimate renamed process explorer binaries"],"logsource":{"category":"file_event","product":"windows"},"references":["https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer","https://github.com/Yaxser/Backstab","https://www.elastic.co/security-labs/stopping-vulnerable-driver-attacks","https://news.sophos.com/en-us/2023/04/19/aukill-edr-killer-malware-abuses-process-explorer-driver/"],"tags":["attack.persistence","attack.privilege_escalation","attack.t1068"]},"location":"active-response\\\\active-responses.log"}',
969 + data_system_EventID: "11",
970 + data_system_Provider_attributes_Guid: "5770385F-C22A-43E0-BF4C-06F5698FFBD9",
971 + rule_id: "200051",
972 + manager_name: "ASHWZHMA",
973 + data_event_TargetFilename: "C:\\Windows\\System32\\drivers\\PROCEXP152.SYS",
974 + location: "active-response\\active-responses.log",
975 + data_authors: "Florian Roth (Nextron Systems)",
976 + rule_group3: "sigma",
977 + msg_timestamp: "2023-11-05T10:16:58.328Z",
978 + rule_group2: "chainsaw",
979 + rule_group1: "windows"
980 + },
981 + sort: [1699179365625]
982 + }
983 + ]
984 + },
985 + {
986 + index_name: "wazuh-bkomanh1_1",
987 + total_alerts: 5,
988 + alerts: [
989 + {
990 + _index: "wazuh-bkomanh1_1",
991 + _id: "ae3aac92-7bd7-11ee-93bc-86000046278a",
992 + _score: null,
993 + _source: {
994 + data_win_eventdata_description: "Application Compatibility Database Installer",
995 + source_reserved_ip: true,
996 + data_win_system_eventRecordID: "834890",
997 + data_win_eventdata_user: "NT AUTHORITY\\\\SYSTEM",
998 + agent_id: "068",
999 + agent_name: "WinDev2308Eval",
1000 + sha256: "5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1001 + gl2_remote_ip: "10.255.255.13",
1002 + data_win_system_eventID: "1",
1003 + gl2_remote_port: 36714,
1004 + agent_labels_customer: "bkomanh1",
1005 + source: "10.255.255.13",
1006 + gl2_source_input: "6459151dea00fd5d3da2df91",
1007 + rule_level: 12,
1008 + data_win_eventdata_originalFileName: "sdbinst.exe",
1009 + data_win_eventdata_company: "Microsoft Corporation",
1010 + data_win_system_task: "1",
1011 + timestamp_utc: "2023-11-05T12:34:39.363Z",
1012 + syslog_type: "wazuh",
1013 + data_win_system_threadID: "4928",
1014 + rule_description: "Application Compatibility Database launched",
1015 + data_win_eventdata_parentUser: "NT AUTHORITY\\\\SYSTEM",
1016 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1017 + id: "1699187674.709551442",
1018 + rule_mitre_tactic: "Privilege Escalation, Persistence",
1019 + gl2_accounted_message_size: 7629,
1020 + data_win_eventdata_integrityLevel: "System",
1021 + data_win_eventdata_utcTime: "2023-11-05 12:34:39.359",
1022 + streams: ["650b315d5e9a2d550c6687ae"],
1023 + rule_mitre_id: "T1546.011",
1024 + gl2_message_id: "01HEFQMNETVW5WV1DMEJR3V2FD",
1025 + data_win_system_computer: "WinDev2308Eval",
1026 + data_win_eventdata_currentDirectory: "C:\\\\Windows\\\\system32\\\\",
1027 + agent_ip_reserved_ip: true,
1028 + data_win_eventdata_ruleName: "technique_id=T1546.011,technique_name=Application Shimming",
1029 + data_win_eventdata_hashes:
1030 + "SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD",
1031 + agent_ip: "172.26.161.217",
1032 + data_win_eventdata_image: "C:\\\\Windows\\\\System32\\\\sdbinst.exe",
1033 + data_win_eventdata_parentProcessGuid: "{10906cbf-9e07-6528-8600-000000000e00}",
1034 + true: 1699187674.749328,
1035 + data_win_eventdata_parentProcessId: "5952",
1036 + rule_groups: "sysmon, sysmon_eid1_detections, windows",
1037 + data_win_system_keywords: "0x8000000000000000",
1038 + data_win_system_level: "4",
1039 + data_win_eventdata_fileVersion: "10.0.22621.2361 (WinBuild.160101.0800)",
1040 + data_win_eventdata_parentImage: "C:\\\\Windows\\\\System32\\\\svchost.exe",
1041 + process_id: "7076",
1042 + data_win_system_severityValue: "INFORMATION",
1043 + data_win_eventdata_processGuid: "{10906cbf-8bdf-6547-fe84-010000000e00}",
1044 + rule_mitre_technique: "Application Shimming",
1045 + rule_firedtimes: 1,
1046 + data_win_system_systemTime: "2023-11-05T12:34:39.3631014Z",
1047 + rule_mail: true,
1048 + decoder_name: "windows_eventchannel",
1049 + data_win_eventdata_commandLine: "C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg",
1050 + data_win_system_processID: "3808",
1051 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1052 + syslog_level: "ALERT",
1053 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1054 + data_win_eventdata_processId: "7076",
1055 + data_win_system_version: "5",
1056 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1057 + timestamp: "2023-11-05 12:34:36.634",
1058 + data_win_eventdata_parentCommandLine:
1059 + "C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc",
1060 + data_win_system_opcode: "0",
1061 + gl2_processing_error:
1062 + 'Replaced invalid timestamp value in message <ae3aac92-7bd7-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:34:34.734+0000> caused exception: Invalid format: "2023-11-05T12:34:34.734+0000" is malformed at "T12:34:34.734+0000".',
1063 + data_win_eventdata_terminalSessionId: "0",
1064 + message:
1065 + '{"true":1699187674.749328,"timestamp":"2023-11-05T12:34:34.734+0000","rule":{"level":12,"description":"Application Compatibility Database launched","id":"92058","mitre":{"id":["T1546.011"],"tactic":["Privilege Escalation","Persistence"],"technique":["Application Shimming"]},"firedtimes":1,"mail":true,"groups":["sysmon","sysmon_eid1_detections","windows"]},"agent":{"id":"068","name":"WinDev2308Eval","ip":"172.26.161.217","labels":{"customer":"bkomanh1"}},"manager":{"name":"ASHWZHMA"},"id":"1699187674.709551442","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"1","version":"5","level":"4","task":"1","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:34:39.3631014Z","eventRecordID":"834890","processID":"3808","threadID":"4928","channel":"Microsoft-Windows-Sysmon/Operational","computer":"WinDev2308Eval","severityValue":"INFORMATION","message":"\\"Process Create:\\r\\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\\r\\nUtcTime: 2023-11-05 12:34:39.359\\r\\nProcessGuid: {10906cbf-8bdf-6547-fe84-010000000e00}\\r\\nProcessId: 7076\\r\\nImage: C:\\\\Windows\\\\System32\\\\sdbinst.exe\\r\\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\\r\\nDescription: Application Compatibility Database Installer\\r\\nProduct: Microsoft® Windows® Operating System\\r\\nCompany: Microsoft Corporation\\r\\nOriginalFileName: sdbinst.exe\\r\\nCommandLine: C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg\\r\\nCurrentDirectory: C:\\\\Windows\\\\system32\\\\\\r\\nUser: NT AUTHORITY\\\\SYSTEM\\r\\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\\r\\nLogonId: 0x3E7\\r\\nTerminalSessionId: 0\\r\\nIntegrityLevel: System\\r\\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\\r\\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\\r\\nParentProcessId: 5952\\r\\nParentImage: C:\\\\Windows\\\\System32\\\\svchost.exe\\r\\nParentCommandLine: C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\\r\\nParentUser: NT AUTHORITY\\\\SYSTEM\\""},"eventdata":{"ruleName":"technique_id=T1546.011,technique_name=Application Shimming","utcTime":"2023-11-05 12:34:39.359","processGuid":"{10906cbf-8bdf-6547-fe84-010000000e00}","processId":"7076","image":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe","fileVersion":"10.0.22621.2361 (WinBuild.160101.0800)","description":"Application Compatibility Database Installer","product":"Microsoft® Windows® Operating System","company":"Microsoft Corporation","originalFileName":"sdbinst.exe","commandLine":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe -m -bg","currentDirectory":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\","user":"NT AUTHORITY\\\\\\\\SYSTEM","logonGuid":"{10906cbf-9df1-6528-e703-000000000000}","logonId":"0x3e7","terminalSessionId":"0","integrityLevel":"System","hashes":"SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD","parentProcessGuid":"{10906cbf-9e07-6528-8600-000000000e00}","parentProcessId":"5952","parentImage":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\svchost.exe","parentCommandLine":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc","parentUser":"NT AUTHORITY\\\\\\\\SYSTEM"}}},"location":"EventChannel"}',
1066 + rule_id: "92058",
1067 + hash_sha256: "SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1068 + manager_name: "ASHWZHMA",
1069 + data_win_eventdata_logonGuid: "{10906cbf-9df1-6528-e703-000000000000}",
1070 + data_win_eventdata_logonId: "0x3e7",
1071 + location: "EventChannel",
1072 + rule_group3: "windows",
1073 + data_win_system_message:
1074 + '"Process Create:\r\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\r\nUtcTime: 2023-11-05 12:34:39.359\r\nProcessGuid: {10906cbf-8bdf-6547-fe84-010000000e00}\r\nProcessId: 7076\r\nImage: C:\\Windows\\System32\\sdbinst.exe\r\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\r\nDescription: Application Compatibility Database Installer\r\nProduct: Microsoft® Windows® Operating System\r\nCompany: Microsoft Corporation\r\nOriginalFileName: sdbinst.exe\r\nCommandLine: C:\\Windows\\System32\\sdbinst.exe -m -bg\r\nCurrentDirectory: C:\\Windows\\system32\\\r\nUser: NT AUTHORITY\\SYSTEM\r\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\r\nLogonId: 0x3E7\r\nTerminalSessionId: 0\r\nIntegrityLevel: System\r\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\r\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\r\nParentProcessId: 5952\r\nParentImage: C:\\Windows\\System32\\svchost.exe\r\nParentCommandLine: C:\\Windows\\system32\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\r\nParentUser: NT AUTHORITY\\SYSTEM"',
1075 + msg_timestamp: "2023-11-05T12:34:34.734Z",
1076 + rule_group2: "sysmon_eid1_detections",
1077 + data_win_eventdata_product: "Microsoft® Windows® Operating System",
1078 + rule_group1: "sysmon"
1079 + },
1080 + sort: [1699187679363]
1081 + },
1082 + {
1083 + _index: "wazuh-bkomanh1_1",
1084 + _id: "6d89de79-7bd0-11ee-93bc-86000046278a",
1085 + _score: null,
1086 + _source: {
1087 + source_reserved_ip: true,
1088 + data_win_system_eventRecordID: "10017",
1089 + agent_id: "068",
1090 + agent_name: "WinDev2308Eval",
1091 + gl2_remote_ip: "10.255.255.13",
1092 + data_win_system_eventID: "1116",
1093 + data_win_eventdata_fWLink:
1094 + "https://go.microsoft.com/fwlink/?linkid=37020&amp;name=Trojan:Win32/Wacatac.H!ml&amp;threatid=2147814523&amp;enterprise=0",
1095 + gl2_remote_port: 37408,
1096 + rule_tsc: "A1.2, CC7.2, CC7.3, CC6.1, CC6.8",
1097 + agent_labels_customer: "bkomanh1",
1098 + source: "10.255.255.13",
1099 + gl2_source_input: "6459151dea00fd5d3da2df91",
1100 + rule_level: 12,
1101 + data_win_system_task: "0",
1102 + timestamp_utc: "2023-11-05T11:42:41.269Z",
1103 + syslog_type: "wazuh",
1104 + data_win_system_threadID: "1424",
1105 + rule_description:
1106 + "Windows Defender: Antimalware platform detected potentially unwanted software ()",
1107 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1108 + id: "1699184556.662819009",
1109 + gl2_accounted_message_size: 6560,
1110 + streams: ["650b315d5e9a2d550c6687ae"],
1111 + gl2_message_id: "01HEFMNKESZFXFE4JGPJ43SFF6",
1112 + data_win_system_computer: "WinDev2308Eval",
1113 + agent_ip_reserved_ip: true,
1114 + agent_ip: "172.26.161.217",
1115 + true: 1699184556.769473,
1116 + rule_hipaa: "164.312.b",
1117 + rule_groups: "windows, windows_defender",
1118 + data_win_system_keywords: "0x8000000000000000",
1119 + data_win_system_level: "3",
1120 + process_id: "3772",
1121 + data_win_system_severityValue: "WARNING",
1122 + rule_gdpr: "IV_35.7.d",
1123 + rule_firedtimes: 1,
1124 + data_win_system_systemTime: "2023-11-05T11:42:41.2693093Z",
1125 + rule_mail: true,
1126 + rule_pci_dss: "5.1, 5.2, 10.6.1, 11.4",
1127 + rule_nist_800_53: "SI.3, AU.6, SI.4",
1128 + decoder_name: "windows_eventchannel",
1129 + data_win_system_processID: "3772",
1130 + data_win_system_channel: "Microsoft-Windows-Windows Defender/Operational",
1131 + syslog_level: "ALERT",
1132 + data_win_system_providerName: "Microsoft-Windows-Windows Defender",
1133 + data_win_system_version: "0",
1134 + data_win_system_providerGuid: "{11cd958a-c507-4ef3-b3f2-5fd9dfbd2c78}",
1135 + timestamp: "2023-11-05 11:42:41.625",
1136 + data_win_eventdata_path:
1137 + "containerfile:_C:\\\\Users\\\\User\\\\Desktop\\\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip; file:_C:\\\\Users\\\\User\\\\Desktop\\\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip-&gt;Setup/Email Extractor Professional Edition Full Activated.exe-&gt;(inno#000059)",
1138 + data_win_system_opcode: "0",
1139 + gl2_processing_error:
1140 + 'Replaced invalid timestamp value in message <6d89de79-7bd0-11ee-93bc-86000046278a> with current time - Value <2023-11-05T11:42:36.668+0000> caused exception: Invalid format: "2023-11-05T11:42:36.668+0000" is malformed at "T11:42:36.668+0000".',
1141 + message:
1142 + '{"true":1699184556.769473,"timestamp":"2023-11-05T11:42:36.668+0000","rule":{"level":12,"description":"Windows Defender: Antimalware platform detected potentially unwanted software ()","id":"62123","firedtimes":1,"mail":true,"groups":["windows","windows_defender"],"pci_dss":["5.1","5.2","10.6.1","11.4"],"gpg13":["4.2"],"gdpr":["IV_35.7.d"],"hipaa":["164.312.b"],"nist_800_53":["SI.3","AU.6","SI.4"],"tsc":["A1.2","CC7.2","CC7.3","CC6.1","CC6.8"]},"agent":{"id":"068","name":"WinDev2308Eval","ip":"172.26.161.217","labels":{"customer":"bkomanh1"}},"manager":{"name":"ASHWZHMA"},"id":"1699184556.662819009","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Windows Defender","providerGuid":"{11cd958a-c507-4ef3-b3f2-5fd9dfbd2c78}","eventID":"1116","version":"0","level":"3","task":"0","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T11:42:41.2693093Z","eventRecordID":"10017","processID":"3772","threadID":"1424","channel":"Microsoft-Windows-Windows Defender/Operational","computer":"WinDev2308Eval","severityValue":"WARNING","message":"\\"Microsoft Defender Antivirus has detected malware or other potentially unwanted software.\\r\\n For more information please see the following:\\r\\nhttps://go.microsoft.com/fwlink/?linkid=37020&name=Trojan:Win32/Wacatac.H!ml&threatid=2147814523&enterprise=0\\r\\n \\tName: Trojan:Win32/Wacatac.H!ml\\r\\n \\tID: 2147814523\\r\\n \\tSeverity: Severe\\r\\n \\tCategory: Trojan\\r\\n \\tPath: containerfile:_C:\\\\Users\\\\User\\\\Desktop\\\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip; file:_C:\\\\Users\\\\User\\\\Desktop\\\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip->Setup/Email Extractor Professional Edition Full Activated.exe->(inno#000059)\\r\\n \\tDetection Origin: Local machine\\r\\n \\tDetection Type: FastPath\\r\\n \\tDetection Source: System\\r\\n \\tUser: NT AUTHORITY\\\\SYSTEM\\r\\n \\tProcess Name: Unknown\\r\\n \\tSecurity intelligence Version: AV: 1.399.1651.0, AS: 1.399.1651.0, NIS: 1.399.1651.0\\r\\n \\tEngine Version: AM: 1.1.23090.2007, NIS: 1.1.23090.2007\\""},"eventdata":{"product Name":"Microsoft Defender Antivirus","product Version":"4.18.23090.2008","detection ID":"{8ECA5A94-47DE-4F30-B462-E46EE7427324}","detection Time":"2023-11-05T11:42:39.792Z","threat ID":"2147814523","threat Name":"Trojan:Win32/Wacatac.H!ml","severity ID":"5","severity Name":"Severe","category ID":"8","category Name":"Trojan","fWLink":"https://go.microsoft.com/fwlink/?linkid=37020&amp;name=Trojan:Win32/Wacatac.H!ml&amp;threatid=2147814523&amp;enterprise=0","status Code":"1","state":"1","source ID":"2","source Name":"System","process Name":"Unknown","detection User":"NT AUTHORITY\\\\\\\\SYSTEM","path":"containerfile:_C:\\\\\\\\Users\\\\\\\\User\\\\\\\\Desktop\\\\\\\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip; file:_C:\\\\\\\\Users\\\\\\\\User\\\\\\\\Desktop\\\\\\\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip-&gt;Setup/Email Extractor Professional Edition Full Activated.exe-&gt;(inno#000059)","origin ID":"1","origin Name":"Local machine","execution ID":"0","execution Name":"Unknown","type ID":"8","type Name":"FastPath","pre Execution Status":"0","action ID":"9","action Name":"Not Applicable","error Code":"0x00000000","error Description":"The operation completed successfully.","post Clean Status":"0","additional Actions ID":"0","additional Actions String":"No additional actions required","security intelligence Version":"AV: 1.399.1651.0, AS: 1.399.1651.0, NIS: 1.399.1651.0","engine Version":"AM: 1.1.23090.2007, NIS: 1.1.23090.2007"}}},"location":"EventChannel"}',
1143 + rule_id: "62123",
1144 + manager_name: "ASHWZHMA",
1145 + rule_gpg13: "4.2",
1146 + data_win_eventdata_state: "1",
1147 + location: "EventChannel",
1148 + data_win_system_message:
1149 + '"Microsoft Defender Antivirus has detected malware or other potentially unwanted software.\r\n For more information please see the following:\r\nhttps://go.microsoft.com/fwlink/?linkid=37020&name=Trojan:Win32/Wacatac.H!ml&threatid=2147814523&enterprise=0\r\n \tName: Trojan:Win32/Wacatac.H!ml\r\n \tID: 2147814523\r\n \tSeverity: Severe\r\n \tCategory: Trojan\r\n \tPath: containerfile:_C:\\Users\\User\\Desktop\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip; file:_C:\\Users\\User\\Desktop\\Email Extractor Professional Edition v7.3.3.6 Full Activated - WwW.Dr-FarFar.CoM.zip->Setup/Email Extractor Professional Edition Full Activated.exe->(inno#000059)\r\n \tDetection Origin: Local machine\r\n \tDetection Type: FastPath\r\n \tDetection Source: System\r\n \tUser: NT AUTHORITY\\SYSTEM\r\n \tProcess Name: Unknown\r\n \tSecurity intelligence Version: AV: 1.399.1651.0, AS: 1.399.1651.0, NIS: 1.399.1651.0\r\n \tEngine Version: AM: 1.1.23090.2007, NIS: 1.1.23090.2007"',
1150 + msg_timestamp: "2023-11-05T11:42:36.668Z",
1151 + rule_group2: "windows_defender",
1152 + rule_group1: "windows"
1153 + },
1154 + sort: [1699184561269]
1155 + },
1156 + {
1157 + _index: "wazuh-bkomanh1_1",
1158 + _id: "4c764493-7bcf-11ee-93bc-86000046278a",
1159 + _score: null,
1160 + _source: {
1161 + data_win_eventdata_description: "Application Compatibility Database Installer",
1162 + source_reserved_ip: true,
1163 + data_win_system_eventRecordID: "833927",
1164 + data_win_eventdata_user: "NT AUTHORITY\\\\SYSTEM",
1165 + agent_id: "068",
1166 + agent_name: "WinDev2308Eval",
1167 + sha256: "5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1168 + gl2_remote_ip: "10.255.255.13",
1169 + data_win_system_eventID: "1",
1170 + gl2_remote_port: 53864,
1171 + agent_labels_customer: "bkomanh1",
1172 + source: "10.255.255.13",
1173 + gl2_source_input: "6459151dea00fd5d3da2df91",
1174 + rule_level: 12,
1175 + data_win_eventdata_originalFileName: "sdbinst.exe",
1176 + data_win_eventdata_company: "Microsoft Corporation",
1177 + data_win_system_task: "1",
1178 + timestamp_utc: "2023-11-05T11:34:39.282Z",
1179 + syslog_type: "wazuh",
1180 + data_win_system_threadID: "4928",
1181 + rule_description: "Application Compatibility Database launched",
1182 + data_win_eventdata_parentUser: "NT AUTHORITY\\\\SYSTEM",
1183 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1184 + id: "1699184073.657048982",
1185 + rule_mitre_tactic: "Privilege Escalation, Persistence",
1186 + gl2_accounted_message_size: 7629,
1187 + data_win_eventdata_integrityLevel: "System",
1188 + data_win_eventdata_utcTime: "2023-11-05 11:34:39.276",
1189 + streams: ["650b315d5e9a2d550c6687ae"],
1190 + rule_mitre_id: "T1546.011",
1191 + gl2_message_id: "01HEFM6STTQE4PJPF9Q1AC96JZ",
1192 + data_win_system_computer: "WinDev2308Eval",
1193 + data_win_eventdata_currentDirectory: "C:\\\\Windows\\\\system32\\\\",
1194 + agent_ip_reserved_ip: true,
1195 + data_win_eventdata_ruleName: "technique_id=T1546.011,technique_name=Application Shimming",
1196 + data_win_eventdata_hashes:
1197 + "SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD",
1198 + agent_ip: "172.26.161.217",
1199 + data_win_eventdata_image: "C:\\\\Windows\\\\System32\\\\sdbinst.exe",
1200 + data_win_eventdata_parentProcessGuid: "{10906cbf-9e07-6528-8600-000000000e00}",
1201 + true: 1699184073.810394,
1202 + data_win_eventdata_parentProcessId: "5952",
1203 + rule_groups: "sysmon, sysmon_eid1_detections, windows",
1204 + data_win_system_keywords: "0x8000000000000000",
1205 + data_win_system_level: "4",
1206 + data_win_eventdata_fileVersion: "10.0.22621.2361 (WinBuild.160101.0800)",
1207 + data_win_eventdata_parentImage: "C:\\\\Windows\\\\System32\\\\svchost.exe",
1208 + process_id: "1800",
1209 + data_win_system_severityValue: "INFORMATION",
1210 + data_win_eventdata_processGuid: "{10906cbf-7dcf-6547-6284-010000000e00}",
1211 + rule_mitre_technique: "Application Shimming",
1212 + rule_firedtimes: 1,
1213 + data_win_system_systemTime: "2023-11-05T11:34:39.2824291Z",
1214 + rule_mail: true,
1215 + decoder_name: "windows_eventchannel",
1216 + data_win_eventdata_commandLine: "C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg",
1217 + data_win_system_processID: "3808",
1218 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1219 + syslog_level: "ALERT",
1220 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1221 + data_win_eventdata_processId: "1800",
1222 + data_win_system_version: "5",
1223 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1224 + timestamp: "2023-11-05 11:34:36.634",
1225 + data_win_eventdata_parentCommandLine:
1226 + "C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc",
1227 + data_win_system_opcode: "0",
1228 + gl2_processing_error:
1229 + 'Replaced invalid timestamp value in message <4c764493-7bcf-11ee-93bc-86000046278a> with current time - Value <2023-11-05T11:34:33.778+0000> caused exception: Invalid format: "2023-11-05T11:34:33.778+0000" is malformed at "T11:34:33.778+0000".',
1230 + data_win_eventdata_terminalSessionId: "0",
1231 + message:
1232 + '{"true":1699184073.810394,"timestamp":"2023-11-05T11:34:33.778+0000","rule":{"level":12,"description":"Application Compatibility Database launched","id":"92058","mitre":{"id":["T1546.011"],"tactic":["Privilege Escalation","Persistence"],"technique":["Application Shimming"]},"firedtimes":1,"mail":true,"groups":["sysmon","sysmon_eid1_detections","windows"]},"agent":{"id":"068","name":"WinDev2308Eval","ip":"172.26.161.217","labels":{"customer":"bkomanh1"}},"manager":{"name":"ASHWZHMA"},"id":"1699184073.657048982","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"1","version":"5","level":"4","task":"1","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T11:34:39.2824291Z","eventRecordID":"833927","processID":"3808","threadID":"4928","channel":"Microsoft-Windows-Sysmon/Operational","computer":"WinDev2308Eval","severityValue":"INFORMATION","message":"\\"Process Create:\\r\\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\\r\\nUtcTime: 2023-11-05 11:34:39.276\\r\\nProcessGuid: {10906cbf-7dcf-6547-6284-010000000e00}\\r\\nProcessId: 1800\\r\\nImage: C:\\\\Windows\\\\System32\\\\sdbinst.exe\\r\\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\\r\\nDescription: Application Compatibility Database Installer\\r\\nProduct: Microsoft® Windows® Operating System\\r\\nCompany: Microsoft Corporation\\r\\nOriginalFileName: sdbinst.exe\\r\\nCommandLine: C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg\\r\\nCurrentDirectory: C:\\\\Windows\\\\system32\\\\\\r\\nUser: NT AUTHORITY\\\\SYSTEM\\r\\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\\r\\nLogonId: 0x3E7\\r\\nTerminalSessionId: 0\\r\\nIntegrityLevel: System\\r\\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\\r\\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\\r\\nParentProcessId: 5952\\r\\nParentImage: C:\\\\Windows\\\\System32\\\\svchost.exe\\r\\nParentCommandLine: C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\\r\\nParentUser: NT AUTHORITY\\\\SYSTEM\\""},"eventdata":{"ruleName":"technique_id=T1546.011,technique_name=Application Shimming","utcTime":"2023-11-05 11:34:39.276","processGuid":"{10906cbf-7dcf-6547-6284-010000000e00}","processId":"1800","image":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe","fileVersion":"10.0.22621.2361 (WinBuild.160101.0800)","description":"Application Compatibility Database Installer","product":"Microsoft® Windows® Operating System","company":"Microsoft Corporation","originalFileName":"sdbinst.exe","commandLine":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe -m -bg","currentDirectory":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\","user":"NT AUTHORITY\\\\\\\\SYSTEM","logonGuid":"{10906cbf-9df1-6528-e703-000000000000}","logonId":"0x3e7","terminalSessionId":"0","integrityLevel":"System","hashes":"SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD","parentProcessGuid":"{10906cbf-9e07-6528-8600-000000000e00}","parentProcessId":"5952","parentImage":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\svchost.exe","parentCommandLine":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc","parentUser":"NT AUTHORITY\\\\\\\\SYSTEM"}}},"location":"EventChannel"}',
1233 + rule_id: "92058",
1234 + hash_sha256: "SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1235 + manager_name: "ASHWZHMA",
1236 + data_win_eventdata_logonGuid: "{10906cbf-9df1-6528-e703-000000000000}",
1237 + data_win_eventdata_logonId: "0x3e7",
1238 + location: "EventChannel",
1239 + rule_group3: "windows",
1240 + data_win_system_message:
1241 + '"Process Create:\r\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\r\nUtcTime: 2023-11-05 11:34:39.276\r\nProcessGuid: {10906cbf-7dcf-6547-6284-010000000e00}\r\nProcessId: 1800\r\nImage: C:\\Windows\\System32\\sdbinst.exe\r\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\r\nDescription: Application Compatibility Database Installer\r\nProduct: Microsoft® Windows® Operating System\r\nCompany: Microsoft Corporation\r\nOriginalFileName: sdbinst.exe\r\nCommandLine: C:\\Windows\\System32\\sdbinst.exe -m -bg\r\nCurrentDirectory: C:\\Windows\\system32\\\r\nUser: NT AUTHORITY\\SYSTEM\r\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\r\nLogonId: 0x3E7\r\nTerminalSessionId: 0\r\nIntegrityLevel: System\r\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\r\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\r\nParentProcessId: 5952\r\nParentImage: C:\\Windows\\System32\\svchost.exe\r\nParentCommandLine: C:\\Windows\\system32\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\r\nParentUser: NT AUTHORITY\\SYSTEM"',
1242 + msg_timestamp: "2023-11-05T11:34:33.778Z",
1243 + rule_group2: "sysmon_eid1_detections",
1244 + data_win_eventdata_product: "Microsoft® Windows® Operating System",
1245 + rule_group1: "sysmon"
1246 + },
1247 + sort: [1699184079282]
1248 + },
1249 + {
1250 + _index: "wazuh-bkomanh1_1",
1251 + _id: "eab278d1-7bc6-11ee-93bc-86000046278a",
1252 + _score: null,
1253 + _source: {
1254 + data_win_eventdata_description: "Application Compatibility Database Installer",
1255 + source_reserved_ip: true,
1256 + data_win_system_eventRecordID: "832940",
1257 + data_win_eventdata_user: "NT AUTHORITY\\\\SYSTEM",
1258 + agent_id: "068",
1259 + agent_name: "WinDev2308Eval",
1260 + sha256: "5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1261 + gl2_remote_ip: "10.255.255.13",
1262 + data_win_system_eventID: "1",
1263 + gl2_remote_port: 55734,
1264 + agent_labels_customer: "bkomanh1",
1265 + source: "10.255.255.13",
1266 + gl2_source_input: "6459151dea00fd5d3da2df91",
1267 + rule_level: 12,
1268 + data_win_eventdata_originalFileName: "sdbinst.exe",
1269 + data_win_eventdata_company: "Microsoft Corporation",
1270 + data_win_system_task: "1",
1271 + timestamp_utc: "2023-11-05T10:34:39.227Z",
1272 + syslog_type: "wazuh",
1273 + data_win_system_threadID: "4928",
1274 + rule_description: "Application Compatibility Database launched",
1275 + data_win_eventdata_parentUser: "NT AUTHORITY\\\\SYSTEM",
1276 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1277 + id: "1699180474.607950326",
1278 + rule_mitre_tactic: "Privilege Escalation, Persistence",
1279 + gl2_accounted_message_size: 7634,
1280 + data_win_eventdata_integrityLevel: "System",
1281 + data_win_eventdata_utcTime: "2023-11-05 10:34:39.223",
1282 + streams: ["650b315d5e9a2d550c6687ae"],
1283 + rule_mitre_id: "T1546.011",
1284 + gl2_message_id: "01HEFGRY6YC425JFCGQFVGDCSB",
1285 + data_win_system_computer: "WinDev2308Eval",
1286 + data_win_eventdata_currentDirectory: "C:\\\\Windows\\\\system32\\\\",
1287 + agent_ip_reserved_ip: true,
1288 + data_win_eventdata_ruleName: "technique_id=T1546.011,technique_name=Application Shimming",
1289 + data_win_eventdata_hashes:
1290 + "SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD",
1291 + agent_ip: "172.26.161.217",
1292 + data_win_eventdata_image: "C:\\\\Windows\\\\System32\\\\sdbinst.exe",
1293 + data_win_eventdata_parentProcessGuid: "{10906cbf-9e07-6528-8600-000000000e00}",
1294 + true: 1699180474.684764,
1295 + data_win_eventdata_parentProcessId: "5952",
1296 + rule_groups: "sysmon, sysmon_eid1_detections, windows",
1297 + data_win_system_keywords: "0x8000000000000000",
1298 + data_win_system_level: "4",
1299 + data_win_eventdata_fileVersion: "10.0.22621.2361 (WinBuild.160101.0800)",
1300 + data_win_eventdata_parentImage: "C:\\\\Windows\\\\System32\\\\svchost.exe",
1301 + process_id: "10456",
1302 + data_win_system_severityValue: "INFORMATION",
1303 + data_win_eventdata_processGuid: "{10906cbf-6fbf-6547-bc83-010000000e00}",
1304 + rule_mitre_technique: "Application Shimming",
1305 + rule_firedtimes: 1,
1306 + data_win_system_systemTime: "2023-11-05T10:34:39.2278008Z",
1307 + rule_mail: true,
1308 + decoder_name: "windows_eventchannel",
1309 + data_win_eventdata_commandLine: "C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg",
1310 + data_win_system_processID: "3808",
1311 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1312 + syslog_level: "ALERT",
1313 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1314 + data_win_eventdata_processId: "10456",
1315 + data_win_system_version: "5",
1316 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1317 + timestamp: "2023-11-05 10:34:36.638",
1318 + data_win_eventdata_parentCommandLine:
1319 + "C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc",
1320 + data_win_system_opcode: "0",
1321 + gl2_processing_error:
1322 + 'Replaced invalid timestamp value in message <eab278d1-7bc6-11ee-93bc-86000046278a> with current time - Value <2023-11-05T10:34:34.669+0000> caused exception: Invalid format: "2023-11-05T10:34:34.669+0000" is malformed at "T10:34:34.669+0000".',
1323 + data_win_eventdata_terminalSessionId: "0",
1324 + message:
1325 + '{"true":1699180474.684764,"timestamp":"2023-11-05T10:34:34.669+0000","rule":{"level":12,"description":"Application Compatibility Database launched","id":"92058","mitre":{"id":["T1546.011"],"tactic":["Privilege Escalation","Persistence"],"technique":["Application Shimming"]},"firedtimes":1,"mail":true,"groups":["sysmon","sysmon_eid1_detections","windows"]},"agent":{"id":"068","name":"WinDev2308Eval","ip":"172.26.161.217","labels":{"customer":"bkomanh1"}},"manager":{"name":"ASHWZHMA"},"id":"1699180474.607950326","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"1","version":"5","level":"4","task":"1","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T10:34:39.2278008Z","eventRecordID":"832940","processID":"3808","threadID":"4928","channel":"Microsoft-Windows-Sysmon/Operational","computer":"WinDev2308Eval","severityValue":"INFORMATION","message":"\\"Process Create:\\r\\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\\r\\nUtcTime: 2023-11-05 10:34:39.223\\r\\nProcessGuid: {10906cbf-6fbf-6547-bc83-010000000e00}\\r\\nProcessId: 10456\\r\\nImage: C:\\\\Windows\\\\System32\\\\sdbinst.exe\\r\\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\\r\\nDescription: Application Compatibility Database Installer\\r\\nProduct: Microsoft® Windows® Operating System\\r\\nCompany: Microsoft Corporation\\r\\nOriginalFileName: sdbinst.exe\\r\\nCommandLine: C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg\\r\\nCurrentDirectory: C:\\\\Windows\\\\system32\\\\\\r\\nUser: NT AUTHORITY\\\\SYSTEM\\r\\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\\r\\nLogonId: 0x3E7\\r\\nTerminalSessionId: 0\\r\\nIntegrityLevel: System\\r\\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\\r\\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\\r\\nParentProcessId: 5952\\r\\nParentImage: C:\\\\Windows\\\\System32\\\\svchost.exe\\r\\nParentCommandLine: C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\\r\\nParentUser: NT AUTHORITY\\\\SYSTEM\\""},"eventdata":{"ruleName":"technique_id=T1546.011,technique_name=Application Shimming","utcTime":"2023-11-05 10:34:39.223","processGuid":"{10906cbf-6fbf-6547-bc83-010000000e00}","processId":"10456","image":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe","fileVersion":"10.0.22621.2361 (WinBuild.160101.0800)","description":"Application Compatibility Database Installer","product":"Microsoft® Windows® Operating System","company":"Microsoft Corporation","originalFileName":"sdbinst.exe","commandLine":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe -m -bg","currentDirectory":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\","user":"NT AUTHORITY\\\\\\\\SYSTEM","logonGuid":"{10906cbf-9df1-6528-e703-000000000000}","logonId":"0x3e7","terminalSessionId":"0","integrityLevel":"System","hashes":"SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD","parentProcessGuid":"{10906cbf-9e07-6528-8600-000000000e00}","parentProcessId":"5952","parentImage":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\svchost.exe","parentCommandLine":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc","parentUser":"NT AUTHORITY\\\\\\\\SYSTEM"}}},"location":"EventChannel"}',
1326 + rule_id: "92058",
1327 + hash_sha256: "SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1328 + manager_name: "ASHWZHMA",
1329 + data_win_eventdata_logonGuid: "{10906cbf-9df1-6528-e703-000000000000}",
1330 + data_win_eventdata_logonId: "0x3e7",
1331 + location: "EventChannel",
1332 + rule_group3: "windows",
1333 + data_win_system_message:
1334 + '"Process Create:\r\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\r\nUtcTime: 2023-11-05 10:34:39.223\r\nProcessGuid: {10906cbf-6fbf-6547-bc83-010000000e00}\r\nProcessId: 10456\r\nImage: C:\\Windows\\System32\\sdbinst.exe\r\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\r\nDescription: Application Compatibility Database Installer\r\nProduct: Microsoft® Windows® Operating System\r\nCompany: Microsoft Corporation\r\nOriginalFileName: sdbinst.exe\r\nCommandLine: C:\\Windows\\System32\\sdbinst.exe -m -bg\r\nCurrentDirectory: C:\\Windows\\system32\\\r\nUser: NT AUTHORITY\\SYSTEM\r\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\r\nLogonId: 0x3E7\r\nTerminalSessionId: 0\r\nIntegrityLevel: System\r\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\r\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\r\nParentProcessId: 5952\r\nParentImage: C:\\Windows\\System32\\svchost.exe\r\nParentCommandLine: C:\\Windows\\system32\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\r\nParentUser: NT AUTHORITY\\SYSTEM"',
1335 + msg_timestamp: "2023-11-05T10:34:34.669Z",
1336 + rule_group2: "sysmon_eid1_detections",
1337 + data_win_eventdata_product: "Microsoft® Windows® Operating System",
1338 + rule_group1: "sysmon"
1339 + },
1340 + sort: [1699180479227]
1341 + },
1342 + {
1343 + _index: "wazuh-bkomanh1_1",
1344 + _id: "88ee10d0-7bbe-11ee-93bc-86000046278a",
1345 + _score: null,
1346 + _source: {
1347 + data_win_eventdata_description: "Application Compatibility Database Installer",
1348 + source_reserved_ip: true,
1349 + data_win_system_eventRecordID: "832368",
1350 + data_win_eventdata_user: "NT AUTHORITY\\\\SYSTEM",
1351 + agent_id: "068",
1352 + agent_name: "WinDev2308Eval",
1353 + sha256: "5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1354 + gl2_remote_ip: "10.255.255.13",
1355 + data_win_system_eventID: "1",
1356 + gl2_remote_port: 41488,
1357 + agent_labels_customer: "bkomanh1",
1358 + source: "10.255.255.13",
1359 + gl2_source_input: "6459151dea00fd5d3da2df91",
1360 + rule_level: 12,
1361 + data_win_eventdata_originalFileName: "sdbinst.exe",
1362 + data_win_eventdata_company: "Microsoft Corporation",
1363 + data_win_system_task: "1",
1364 + timestamp_utc: "2023-11-05T09:34:39.164Z",
1365 + syslog_type: "wazuh",
1366 + data_win_system_threadID: "4928",
1367 + rule_description: "Application Compatibility Database launched",
1368 + data_win_eventdata_parentUser: "NT AUTHORITY\\\\SYSTEM",
1369 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1370 + id: "1699176874.562050141",
1371 + rule_mitre_tactic: "Privilege Escalation, Persistence",
1372 + gl2_accounted_message_size: 7629,
1373 + data_win_eventdata_integrityLevel: "System",
1374 + data_win_eventdata_utcTime: "2023-11-05 09:34:39.158",
1375 + streams: ["650b315d5e9a2d550c6687ae"],
1376 + rule_mitre_id: "T1546.011",
1377 + gl2_message_id: "01HEFDB2JYZDYQZM86QYF1YYTJ",
1378 + data_win_system_computer: "WinDev2308Eval",
1379 + data_win_eventdata_currentDirectory: "C:\\\\Windows\\\\system32\\\\",
1380 + agent_ip_reserved_ip: true,
1381 + data_win_eventdata_ruleName: "technique_id=T1546.011,technique_name=Application Shimming",
1382 + data_win_eventdata_hashes:
1383 + "SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD",
1384 + agent_ip: "172.26.161.217",
1385 + data_win_eventdata_image: "C:\\\\Windows\\\\System32\\\\sdbinst.exe",
1386 + data_win_eventdata_parentProcessGuid: "{10906cbf-9e07-6528-8600-000000000e00}",
1387 + true: 1699176874.657827,
1388 + data_win_eventdata_parentProcessId: "5952",
1389 + rule_groups: "sysmon, sysmon_eid1_detections, windows",
1390 + data_win_system_keywords: "0x8000000000000000",
1391 + data_win_system_level: "4",
1392 + data_win_eventdata_fileVersion: "10.0.22621.2361 (WinBuild.160101.0800)",
1393 + data_win_eventdata_parentImage: "C:\\\\Windows\\\\System32\\\\svchost.exe",
1394 + process_id: "1368",
1395 + data_win_system_severityValue: "INFORMATION",
1396 + data_win_eventdata_processGuid: "{10906cbf-61af-6547-4683-010000000e00}",
1397 + rule_mitre_technique: "Application Shimming",
1398 + rule_firedtimes: 1,
1399 + data_win_system_systemTime: "2023-11-05T09:34:39.1640751Z",
1400 + rule_mail: true,
1401 + decoder_name: "windows_eventchannel",
1402 + data_win_eventdata_commandLine: "C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg",
1403 + data_win_system_processID: "3808",
1404 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1405 + syslog_level: "ALERT",
1406 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1407 + data_win_eventdata_processId: "1368",
1408 + data_win_system_version: "5",
1409 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1410 + timestamp: "2023-11-05 09:34:36.638",
1411 + data_win_eventdata_parentCommandLine:
1412 + "C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc",
1413 + data_win_system_opcode: "0",
1414 + gl2_processing_error:
1415 + 'Replaced invalid timestamp value in message <88ee10d0-7bbe-11ee-93bc-86000046278a> with current time - Value <2023-11-05T09:34:34.642+0000> caused exception: Invalid format: "2023-11-05T09:34:34.642+0000" is malformed at "T09:34:34.642+0000".',
1416 + data_win_eventdata_terminalSessionId: "0",
1417 + message:
1418 + '{"true":1699176874.657827,"timestamp":"2023-11-05T09:34:34.642+0000","rule":{"level":12,"description":"Application Compatibility Database launched","id":"92058","mitre":{"id":["T1546.011"],"tactic":["Privilege Escalation","Persistence"],"technique":["Application Shimming"]},"firedtimes":1,"mail":true,"groups":["sysmon","sysmon_eid1_detections","windows"]},"agent":{"id":"068","name":"WinDev2308Eval","ip":"172.26.161.217","labels":{"customer":"bkomanh1"}},"manager":{"name":"ASHWZHMA"},"id":"1699176874.562050141","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"1","version":"5","level":"4","task":"1","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T09:34:39.1640751Z","eventRecordID":"832368","processID":"3808","threadID":"4928","channel":"Microsoft-Windows-Sysmon/Operational","computer":"WinDev2308Eval","severityValue":"INFORMATION","message":"\\"Process Create:\\r\\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\\r\\nUtcTime: 2023-11-05 09:34:39.158\\r\\nProcessGuid: {10906cbf-61af-6547-4683-010000000e00}\\r\\nProcessId: 1368\\r\\nImage: C:\\\\Windows\\\\System32\\\\sdbinst.exe\\r\\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\\r\\nDescription: Application Compatibility Database Installer\\r\\nProduct: Microsoft® Windows® Operating System\\r\\nCompany: Microsoft Corporation\\r\\nOriginalFileName: sdbinst.exe\\r\\nCommandLine: C:\\\\Windows\\\\System32\\\\sdbinst.exe -m -bg\\r\\nCurrentDirectory: C:\\\\Windows\\\\system32\\\\\\r\\nUser: NT AUTHORITY\\\\SYSTEM\\r\\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\\r\\nLogonId: 0x3E7\\r\\nTerminalSessionId: 0\\r\\nIntegrityLevel: System\\r\\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\\r\\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\\r\\nParentProcessId: 5952\\r\\nParentImage: C:\\\\Windows\\\\System32\\\\svchost.exe\\r\\nParentCommandLine: C:\\\\Windows\\\\system32\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\\r\\nParentUser: NT AUTHORITY\\\\SYSTEM\\""},"eventdata":{"ruleName":"technique_id=T1546.011,technique_name=Application Shimming","utcTime":"2023-11-05 09:34:39.158","processGuid":"{10906cbf-61af-6547-4683-010000000e00}","processId":"1368","image":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe","fileVersion":"10.0.22621.2361 (WinBuild.160101.0800)","description":"Application Compatibility Database Installer","product":"Microsoft® Windows® Operating System","company":"Microsoft Corporation","originalFileName":"sdbinst.exe","commandLine":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\sdbinst.exe -m -bg","currentDirectory":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\","user":"NT AUTHORITY\\\\\\\\SYSTEM","logonGuid":"{10906cbf-9df1-6528-e703-000000000000}","logonId":"0x3e7","terminalSessionId":"0","integrityLevel":"System","hashes":"SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD","parentProcessGuid":"{10906cbf-9e07-6528-8600-000000000e00}","parentProcessId":"5952","parentImage":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\svchost.exe","parentCommandLine":"C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc","parentUser":"NT AUTHORITY\\\\\\\\SYSTEM"}}},"location":"EventChannel"}',
1419 + rule_id: "92058",
1420 + hash_sha256: "SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77",
1421 + manager_name: "ASHWZHMA",
1422 + data_win_eventdata_logonGuid: "{10906cbf-9df1-6528-e703-000000000000}",
1423 + data_win_eventdata_logonId: "0x3e7",
1424 + location: "EventChannel",
1425 + rule_group3: "windows",
1426 + data_win_system_message:
1427 + '"Process Create:\r\nRuleName: technique_id=T1546.011,technique_name=Application Shimming\r\nUtcTime: 2023-11-05 09:34:39.158\r\nProcessGuid: {10906cbf-61af-6547-4683-010000000e00}\r\nProcessId: 1368\r\nImage: C:\\Windows\\System32\\sdbinst.exe\r\nFileVersion: 10.0.22621.2361 (WinBuild.160101.0800)\r\nDescription: Application Compatibility Database Installer\r\nProduct: Microsoft® Windows® Operating System\r\nCompany: Microsoft Corporation\r\nOriginalFileName: sdbinst.exe\r\nCommandLine: C:\\Windows\\System32\\sdbinst.exe -m -bg\r\nCurrentDirectory: C:\\Windows\\system32\\\r\nUser: NT AUTHORITY\\SYSTEM\r\nLogonGuid: {10906cbf-9df1-6528-e703-000000000000}\r\nLogonId: 0x3E7\r\nTerminalSessionId: 0\r\nIntegrityLevel: System\r\nHashes: SHA1=C0C9554DCEBF89ABC7DA5332037BC2C88A3B0F1E,MD5=72A442005F409F233C223E60A7A0D868,SHA256=5913E1A6AC0D582A8710FFD723E16486A0E7C56C6122820175C4745C30816B77,IMPHASH=999B9DCD61DAB941B1E8D50FE6EF72CD\r\nParentProcessGuid: {10906cbf-9e07-6528-8600-000000000e00}\r\nParentProcessId: 5952\r\nParentImage: C:\\Windows\\System32\\svchost.exe\r\nParentCommandLine: C:\\Windows\\system32\\svchost.exe -k LocalSystemNetworkRestricted -p -s PcaSvc\r\nParentUser: NT AUTHORITY\\SYSTEM"',
1428 + msg_timestamp: "2023-11-05T09:34:34.642Z",
1429 + rule_group2: "sysmon_eid1_detections",
1430 + data_win_eventdata_product: "Microsoft® Windows® Operating System",
1431 + rule_group1: "sysmon"
1432 + },
1433 + sort: [1699176879164]
1434 + }
1435 + ]
1436 + },
1437 + {
1438 + index_name: "wazuh-toafb68l_2",
1439 + total_alerts: 2,
1440 + alerts: [
1441 + {
1442 + _index: "wazuh-toafb68l_2",
1443 + _id: "15d6dd81-7ba4-11ee-93bc-86000046278a",
1444 + _score: null,
1445 + _source: {
1446 + parent_process_id: "214455",
1447 + source_reserved_ip: true,
1448 + agent_id: "077",
1449 + agent_name: "ssdnodes-zabbix",
1450 + gl2_remote_ip: "10.255.255.13",
1451 + gl2_remote_port: 53816,
1452 + data_columns_cwd: "/",
1453 + agent_labels_customer: "toafb68l",
1454 + agent_ip_city_name: "Sydney",
1455 + source: "10.255.255.13",
1456 + gl2_source_input: "6459151dea00fd5d3da2df91",
1457 + rule_level: 12,
1458 + data_calendarTime: "Sun Nov 5 06:25:09 2023 UTC",
1459 + data_counter: "19295",
1460 + data_columns_duration: "2809889",
1461 + timestamp_utc: "2023-11-05T06:25:09.000Z",
1462 + syslog_type: "wazuh",
1463 + process_name: "/bin/dd",
1464 + process_cmd_line: "dd if=/dev/urandom bs=2 count=1",
1465 + data_hostIdentifier: "ssdnodes-zabbix",
1466 + data_columns_probe_error: "0",
1467 + rule_description:
1468 + "Adversaries may use binary padding to add junk data and change the on-disk representation of malware. This rule detect using dd and truncate to add a junk data to file.",
1469 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1470 + id: "1699165511.370373429",
1471 + rule_mitre_tactic: "Defense Evasion",
1472 + process_image: "/bin/dd",
1473 + gl2_accounted_message_size: 2736,
1474 + data_columns_uid: "0",
1475 + streams: ["6518f9c15e9a2d550c8a49f1"],
1476 + rule_mitre_id: "T1027",
1477 + gl2_message_id: "01HEF2GCTSQMHY4KKXANGM0WNP",
1478 + agent_ip: "208.87.135.165",
1479 + data_columns_gid: "0",
1480 + data_columns_syscall: "exec",
1481 + true: 1699165511.829736,
1482 + data_columns_cid: "42132",
1483 + rule_groups: "osquery, bpf_process_events",
1484 + data_columns_exit_code: "0",
1485 + process_id: "214456",
1486 + agent_ip_geolocation: "-33.8715,151.2006",
1487 + rule_mitre_technique: "Obfuscated Files or Information",
1488 + rule_firedtimes: 1,
1489 + rule_mail: true,
1490 + data_name: "bpf_process_events",
1491 + decoder_name: "json",
1492 + agent_ip_country_code: "AU",
1493 + data_columns_ntime: "3024028081495260",
1494 + syslog_level: "ALERT",
1495 + timestamp: "2023-11-05 06:25:16.633",
1496 + data_columns_cmdline: "dd if=/dev/urandom bs=2 count=1",
1497 + data_columns_tid: "214456",
1498 + gl2_processing_error:
1499 + 'Replaced invalid timestamp value in message <15d6dd81-7ba4-11ee-93bc-86000046278a> with current time - Value <2023-11-05T06:25:11.808+0000> caused exception: Invalid format: "2023-11-05T06:25:11.808+0000" is malformed at "T06:25:11.808+0000".',
1500 + data_columns_pid: "214456",
1501 + message:
1502 + '{"true":1699165511.829736,"timestamp":"2023-11-05T06:25:11.808+0000","rule":{"level":12,"description":"Adversaries may use binary padding to add junk data and change the on-disk representation of malware. This rule detect using dd and truncate to add a junk data to file.","id":"200243","mitre":{"id":["T1027"],"tactic":["Defense Evasion"],"technique":["Obfuscated Files or Information"]},"firedtimes":1,"mail":true,"groups":["osquery","bpf_process_events"]},"agent":{"id":"077","name":"ssdnodes-zabbix","ip":"208.87.135.165","labels":{"customer":"toafb68l"}},"manager":{"name":"ASHWZHMA"},"id":"1699165511.370373429","decoder":{"name":"json"},"data":{"action":"added","name":"bpf_process_events","hostIdentifier":"ssdnodes-zabbix","calendarTime":"Sun Nov 5 06:25:09 2023 UTC","unixTime":"1699165509","epoch":"0","counter":"19295","numerics":"false","columns":{"cid":"42132","cmdline":"dd if=/dev/urandom bs=2 count=1","cwd":"/","duration":"2809889","exit_code":"0","gid":"0","ntime":"3024028081495260","parent":"214455","path":"/bin/dd","pid":"214456","probe_error":"0","syscall":"exec","tid":"214456","uid":"0"}},"location":"/var/log/osquery/osqueryd.results.log"}',
1503 + data_numerics: "false",
1504 + rule_id: "200243",
1505 + manager_name: "ASHWZHMA",
1506 + data_columns_path: "/bin/dd",
1507 + data_unixTime: "1699165509",
1508 + data_action: "added",
1509 + data_epoch: "0",
1510 + location: "/var/log/osquery/osqueryd.results.log",
1511 + data_columns_parent: "214455",
1512 + msg_timestamp: "2023-11-05T06:25:11.808Z",
1513 + rule_group2: "bpf_process_events",
1514 + rule_group1: "osquery"
1515 + },
1516 + sort: [1699165509000]
1517 + },
1518 + {
1519 + _index: "wazuh-toafb68l_2",
1520 + _id: "fd5fcbb2-7b83-11ee-93bc-86000046278a",
1521 + _score: null,
1522 + _source: {
1523 + parent_process_id: "98528",
1524 + source_reserved_ip: true,
1525 + agent_id: "077",
1526 + agent_name: "ssdnodes-zabbix",
1527 + gl2_remote_ip: "10.255.255.13",
1528 + gl2_remote_port: 54994,
1529 + data_columns_cwd: "/tmp",
1530 + agent_labels_customer: "toafb68l",
1531 + agent_ip_city_name: "Sydney",
1532 + source: "10.255.255.13",
1533 + gl2_source_input: "6459151dea00fd5d3da2df91",
1534 + rule_level: 12,
1535 + data_calendarTime: "Sun Nov 5 02:35:28 2023 UTC",
1536 + data_counter: "18098",
1537 + data_columns_duration: "134246",
1538 + timestamp_utc: "2023-11-05T02:35:28.000Z",
1539 + syslog_type: "wazuh",
1540 + process_name: "/usr/bin/chmod",
1541 + process_cmd_line: "chmod +r /var/lib/update-notifier/updates-available",
1542 + data_hostIdentifier: "ssdnodes-zabbix",
1543 + data_columns_probe_error: "0",
1544 + rule_description: "Detects file and folder permission changes.",
1545 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1546 + id: "1699151730.117474775",
1547 + rule_mitre_tactic: "Defense Evasion",
1548 + process_image: "/usr/bin/chmod",
1549 + gl2_accounted_message_size: 2594,
1550 + data_columns_uid: "0",
1551 + streams: ["6518f9c15e9a2d550c8a49f1"],
1552 + rule_mitre_id: "T1222",
1553 + gl2_message_id: "01HEENBPZC4ZK59R2RWMZ3AK9K",
1554 + agent_ip: "208.87.135.165",
1555 + data_columns_gid: "0",
1556 + data_columns_syscall: "exec",
1557 + true: 1699151730.737527,
1558 + data_columns_cid: "66927",
1559 + rule_groups: "osquery, bpf_process_events",
1560 + data_columns_exit_code: "0",
1561 + process_id: "98594",
1562 + agent_ip_geolocation: "-33.8715,151.2006",
1563 + rule_mitre_technique: "File and Directory Permissions Modification",
1564 + rule_firedtimes: 1,
1565 + rule_mail: true,
1566 + data_name: "bpf_process_events",
1567 + decoder_name: "json",
1568 + agent_ip_country_code: "AU",
1569 + data_columns_ntime: "3010249590404014",
1570 + syslog_level: "ALERT",
1571 + timestamp: "2023-11-05 02:35:31.692",
1572 + data_columns_cmdline: "chmod +r /var/lib/update-notifier/updates-available",
1573 + data_columns_tid: "98594",
1574 + gl2_processing_error:
1575 + 'Replaced invalid timestamp value in message <fd5fcbb2-7b83-11ee-93bc-86000046278a> with current time - Value <2023-11-05T02:35:30.736+0000> caused exception: Invalid format: "2023-11-05T02:35:30.736+0000" is malformed at "T02:35:30.736+0000".',
1576 + data_columns_pid: "98594",
1577 + message:
1578 + '{"true":1699151730.737527,"timestamp":"2023-11-05T02:35:30.736+0000","rule":{"level":12,"description":"Detects file and folder permission changes.","id":"200259","mitre":{"id":["T1222"],"tactic":["Defense Evasion"],"technique":["File and Directory Permissions Modification"]},"firedtimes":1,"mail":true,"groups":["osquery","bpf_process_events"]},"agent":{"id":"077","name":"ssdnodes-zabbix","ip":"208.87.135.165","labels":{"customer":"toafb68l"}},"manager":{"name":"ASHWZHMA"},"id":"1699151730.117474775","decoder":{"name":"json"},"data":{"action":"added","name":"bpf_process_events","hostIdentifier":"ssdnodes-zabbix","calendarTime":"Sun Nov 5 02:35:28 2023 UTC","unixTime":"1699151728","epoch":"0","counter":"18098","numerics":"false","columns":{"cid":"66927","cmdline":"chmod +r /var/lib/update-notifier/updates-available","cwd":"/tmp","duration":"134246","exit_code":"0","gid":"0","ntime":"3010249590404014","parent":"98528","path":"/usr/bin/chmod","pid":"98594","probe_error":"0","syscall":"exec","tid":"98594","uid":"0"}},"location":"/var/log/osquery/osqueryd.results.log"}',
1579 + data_numerics: "false",
1580 + rule_id: "200259",
1581 + manager_name: "ASHWZHMA",
1582 + data_columns_path: "/usr/bin/chmod",
1583 + data_unixTime: "1699151728",
1584 + data_action: "added",
1585 + data_epoch: "0",
1586 + location: "/var/log/osquery/osqueryd.results.log",
1587 + data_columns_parent: "98528",
1588 + msg_timestamp: "2023-11-05T02:35:30.736Z",
1589 + rule_group2: "bpf_process_events",
1590 + rule_group1: "osquery"
1591 + },
1592 + sort: [1699151728000]
1593 + }
1594 + ]
1595 + },
1596 + {
1597 + index_name: "wazuh-wso4vxhq_8",
1598 + total_alerts: 5,
1599 + alerts: [
1600 + {
1601 + _index: "wazuh-wso4vxhq_8",
1602 + _id: "e1c93161-7bda-11ee-93bc-86000046278a",
1603 + _score: null,
1604 + _source: {
1605 + source_reserved_ip: true,
1606 + data_win_system_eventRecordID: "4882778",
1607 + agent_id: "070",
1608 + agent_name: "web1",
1609 + data_win_eventdata_sourceProcessGUID: "{d9ab9ebb-7e1a-6544-44e0-010000003000}",
1610 + gl2_remote_ip: "10.255.255.13",
1611 + data_win_system_eventID: "10",
1612 + gl2_remote_port: 57078,
1613 + agent_labels_customer: "wso4vxhq",
1614 + agent_ip_city_name: "N/A",
1615 + source: "10.255.255.13",
1616 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
1617 + gl2_source_input: "6459151dea00fd5d3da2df91",
1618 + rule_level: 12,
1619 + data_win_eventdata_sourceUser: "WEB1\\\\Administrator",
1620 + data_win_system_task: "10",
1621 + timestamp_utc: "2023-11-05T12:57:26.938Z",
1622 + syslog_type: "wazuh",
1623 + data_win_system_threadID: "5576",
1624 + rule_description:
1625 + "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
1626 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1627 + id: "1699189047.740504296",
1628 + data_win_eventdata_grantedAccess: "0x40",
1629 + data_win_eventdata_sourceImage:
1630 + "C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe",
1631 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
1632 + gl2_accounted_message_size: 11454,
1633 + data_win_eventdata_utcTime: "2023-11-05 12:57:26.937",
1634 + streams: ["650d3da25e9a2d550c6d6491"],
1635 + rule_mitre_id: "T1055",
1636 + gl2_message_id: "01HEFRYM77XPQT25DBE9HD17DT",
1637 + data_win_system_computer: "web1",
1638 + data_win_eventdata_ruleName: "technique_id=T1036,technique_name=Masquerading",
1639 + agent_ip: "202.43.110.138",
1640 + true: 1699189047.88765,
1641 + rule_groups: "sysmon, sysmon_eid10_detections, windows",
1642 + data_win_system_keywords: "0x8000000000000000",
1643 + data_win_system_level: "4",
1644 + data_win_eventdata_targetProcessGUID: "{d9ab9ebb-62bb-6547-601c-020000003000}",
1645 + data_win_system_severityValue: "INFORMATION",
1646 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
1647 + agent_ip_geolocation: "16.1667,107.8333",
1648 + rule_mitre_technique: "Process Injection",
1649 + rule_firedtimes: 86,
1650 + data_win_system_systemTime: "2023-11-05T12:57:26.938209400Z",
1651 + rule_mail: true,
1652 + decoder_name: "windows_eventchannel",
1653 + agent_ip_country_code: "VN",
1654 + data_win_system_processID: "3468",
1655 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1656 + syslog_level: "ALERT",
1657 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1658 + data_win_system_version: "3",
1659 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1660 + timestamp: "2023-11-05 12:57:31.623",
1661 + data_win_eventdata_callTrace:
1662 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba62|C:\\\\Windows\\\\System32\\\\shcore.dll+b585|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491",
1663 + data_win_system_opcode: "0",
1664 + gl2_processing_error:
1665 + 'Replaced invalid timestamp value in message <e1c93161-7bda-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:57:27.609+0000> caused exception: Invalid format: "2023-11-05T12:57:27.609+0000" is malformed at "T12:57:27.609+0000".',
1666 + data_win_eventdata_sourceProcessId: "1652",
1667 + message:
1668 + '{"true":1699189047.88765,"timestamp":"2023-11-05T12:57:27.609+0000","rule":{"level":12,"description":"Explorer process was accessed by C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe, possible process injection","id":"92910","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":86,"mail":true,"groups":["sysmon","sysmon_eid10_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699189047.740504296","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"10","version":"3","level":"4","task":"10","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:57:26.938209400Z","eventRecordID":"4882778","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"Process accessed:\\r\\nRuleName: technique_id=T1036,technique_name=Masquerading\\r\\nUtcTime: 2023-11-05 12:57:26.937\\r\\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\\r\\nSourceProcessId: 1652\\r\\nSourceThreadId: 1284\\r\\nSourceImage: C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe\\r\\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nGrantedAccess: 0x40\\r\\nCallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba62|C:\\\\Windows\\\\System32\\\\shcore.dll+b585|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491\\r\\nSourceUser: WEB1\\\\Administrator\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1036,technique_name=Masquerading","utcTime":"2023-11-05 12:57:26.937","sourceProcessGUID":"{d9ab9ebb-7e1a-6544-44e0-010000003000}","sourceProcessId":"1652","sourceThreadId":"1284","sourceImage":"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe","targetProcessGUID":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","grantedAccess":"0x40","callTrace":"C:\\\\\\\\Windows\\\\\\\\SYSTEM32\\\\\\\\ntdll.dll+9ff24|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNELBASE.dll+1668e|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+cca8|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+ba62|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b585|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b275|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b209|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b104|C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\explorerframe.dll+12b986|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+157bd8c|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+18051ce|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2e1a17|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2dfdfe|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+130216a|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a566ec|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a5695f|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3e93b|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c40433|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb8e|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3d62541|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb68|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c942dd|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3db94|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a52491","sourceUser":"WEB1\\\\\\\\Administrator","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
1669 + rule_id: "92910",
1670 + manager_name: "ASHWZHMA",
1671 + location: "EventChannel",
1672 + data_win_eventdata_targetProcessId: "4384",
1673 + rule_group3: "windows",
1674 + data_win_system_message:
1675 + '"Process accessed:\r\nRuleName: technique_id=T1036,technique_name=Masquerading\r\nUtcTime: 2023-11-05 12:57:26.937\r\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\r\nSourceProcessId: 1652\r\nSourceThreadId: 1284\r\nSourceImage: C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe\r\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nGrantedAccess: 0x40\r\nCallTrace: C:\\Windows\\SYSTEM32\\ntdll.dll+9ff24|C:\\Windows\\System32\\KERNELBASE.dll+1668e|C:\\Windows\\System32\\shcore.dll+cca8|C:\\Windows\\System32\\shcore.dll+ba62|C:\\Windows\\System32\\shcore.dll+b585|C:\\Windows\\System32\\shcore.dll+b275|C:\\Windows\\System32\\shcore.dll+b209|C:\\Windows\\System32\\shcore.dll+b104|C:\\Windows\\system32\\explorerframe.dll+12b986|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+157bd8c|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+18051ce|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2e1a17|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2dfdfe|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+130216a|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a566ec|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a5695f|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3e93b|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c40433|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb8e|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3d62541|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb68|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c942dd|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3db94|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a52491\r\nSourceUser: WEB1\\Administrator\r\nTargetUser: WEB1\\Administrator"',
1676 + data_win_eventdata_sourceThreadId: "1284",
1677 + msg_timestamp: "2023-11-05T12:57:27.609Z",
1678 + rule_group2: "sysmon_eid10_detections",
1679 + rule_group1: "sysmon"
1680 + },
1681 + sort: [1699189046938]
1682 + },
1683 + {
1684 + _index: "wazuh-wso4vxhq_8",
1685 + _id: "e1c97f73-7bda-11ee-93bc-86000046278a",
1686 + _score: null,
1687 + _source: {
1688 + source_reserved_ip: true,
1689 + data_win_system_eventRecordID: "4882779",
1690 + agent_id: "070",
1691 + agent_name: "web1",
1692 + data_win_eventdata_sourceProcessGUID: "{d9ab9ebb-7e1a-6544-44e0-010000003000}",
1693 + gl2_remote_ip: "10.255.255.13",
1694 + data_win_system_eventID: "10",
1695 + gl2_remote_port: 57078,
1696 + agent_labels_customer: "wso4vxhq",
1697 + agent_ip_city_name: "N/A",
1698 + source: "10.255.255.13",
1699 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
1700 + gl2_source_input: "6459151dea00fd5d3da2df91",
1701 + rule_level: 12,
1702 + data_win_eventdata_sourceUser: "WEB1\\\\Administrator",
1703 + data_win_system_task: "10",
1704 + timestamp_utc: "2023-11-05T12:57:26.938Z",
1705 + syslog_type: "wazuh",
1706 + data_win_system_threadID: "5576",
1707 + rule_description:
1708 + "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
1709 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1710 + id: "1699189047.740509955",
1711 + data_win_eventdata_grantedAccess: "0x40",
1712 + data_win_eventdata_sourceImage:
1713 + "C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe",
1714 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
1715 + gl2_accounted_message_size: 11630,
1716 + data_win_eventdata_utcTime: "2023-11-05 12:57:26.937",
1717 + streams: ["650d3da25e9a2d550c6d6491"],
1718 + rule_mitre_id: "T1055",
1719 + gl2_message_id: "01HEFRYM78C4GA33W2VN2H1WWS",
1720 + data_win_system_computer: "web1",
1721 + data_win_eventdata_ruleName: "technique_id=T1036,technique_name=Masquerading",
1722 + agent_ip: "202.43.110.138",
1723 + true: 1699189048.147682,
1724 + rule_groups: "sysmon, sysmon_eid10_detections, windows",
1725 + data_win_system_keywords: "0x8000000000000000",
1726 + data_win_system_level: "4",
1727 + data_win_eventdata_targetProcessGUID: "{d9ab9ebb-62bb-6547-601c-020000003000}",
1728 + data_win_system_severityValue: "INFORMATION",
1729 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
1730 + agent_ip_geolocation: "16.1667,107.8333",
1731 + rule_mitre_technique: "Process Injection",
1732 + rule_firedtimes: 87,
1733 + data_win_system_systemTime: "2023-11-05T12:57:26.938692200Z",
1734 + rule_mail: true,
1735 + decoder_name: "windows_eventchannel",
1736 + agent_ip_country_code: "VN",
1737 + data_win_system_processID: "3468",
1738 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1739 + syslog_level: "ALERT",
1740 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1741 + data_win_system_version: "3",
1742 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1743 + timestamp: "2023-11-05 12:57:31.624",
1744 + data_win_eventdata_callTrace:
1745 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba77|C:\\\\Windows\\\\System32\\\\shcore.dll+b967|C:\\\\Windows\\\\System32\\\\shcore.dll+b8f1|C:\\\\Windows\\\\System32\\\\shcore.dll+b61a|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b9d0|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9",
1746 + data_win_system_opcode: "0",
1747 + gl2_processing_error:
1748 + 'Replaced invalid timestamp value in message <e1c97f73-7bda-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:57:27.886+0000> caused exception: Invalid format: "2023-11-05T12:57:27.886+0000" is malformed at "T12:57:27.886+0000".',
1749 + data_win_eventdata_sourceProcessId: "1652",
1750 + message:
1751 + '{"true":1699189048.147682,"timestamp":"2023-11-05T12:57:27.886+0000","rule":{"level":12,"description":"Explorer process was accessed by C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe, possible process injection","id":"92910","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":87,"mail":true,"groups":["sysmon","sysmon_eid10_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699189047.740509955","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"10","version":"3","level":"4","task":"10","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:57:26.938692200Z","eventRecordID":"4882779","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"Process accessed:\\r\\nRuleName: technique_id=T1036,technique_name=Masquerading\\r\\nUtcTime: 2023-11-05 12:57:26.937\\r\\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\\r\\nSourceProcessId: 1652\\r\\nSourceThreadId: 1284\\r\\nSourceImage: C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe\\r\\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nGrantedAccess: 0x40\\r\\nCallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+ba77|C:\\\\Windows\\\\System32\\\\shcore.dll+b967|C:\\\\Windows\\\\System32\\\\shcore.dll+b8f1|C:\\\\Windows\\\\System32\\\\shcore.dll+b61a|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b9d0|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9\\r\\nSourceUser: WEB1\\\\Administrator\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1036,technique_name=Masquerading","utcTime":"2023-11-05 12:57:26.937","sourceProcessGUID":"{d9ab9ebb-7e1a-6544-44e0-010000003000}","sourceProcessId":"1652","sourceThreadId":"1284","sourceImage":"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe","targetProcessGUID":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","grantedAccess":"0x40","callTrace":"C:\\\\\\\\Windows\\\\\\\\SYSTEM32\\\\\\\\ntdll.dll+9ff24|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNELBASE.dll+1668e|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+cca8|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+ba77|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b967|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b8f1|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b61a|C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\explorerframe.dll+12b9d0|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+157bd8c|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+18051ce|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2e1a17|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2dfdfe|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+130216a|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a566ec|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a5695f|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3e93b|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c40433|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb8e|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3d62541|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb68|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c942dd|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3db94|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a52491|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a3e4a9","sourceUser":"WEB1\\\\\\\\Administrator","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
1752 + rule_id: "92910",
1753 + manager_name: "ASHWZHMA",
1754 + location: "EventChannel",
1755 + data_win_eventdata_targetProcessId: "4384",
1756 + rule_group3: "windows",
1757 + data_win_system_message:
1758 + '"Process accessed:\r\nRuleName: technique_id=T1036,technique_name=Masquerading\r\nUtcTime: 2023-11-05 12:57:26.937\r\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\r\nSourceProcessId: 1652\r\nSourceThreadId: 1284\r\nSourceImage: C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe\r\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nGrantedAccess: 0x40\r\nCallTrace: C:\\Windows\\SYSTEM32\\ntdll.dll+9ff24|C:\\Windows\\System32\\KERNELBASE.dll+1668e|C:\\Windows\\System32\\shcore.dll+cca8|C:\\Windows\\System32\\shcore.dll+ba77|C:\\Windows\\System32\\shcore.dll+b967|C:\\Windows\\System32\\shcore.dll+b8f1|C:\\Windows\\System32\\shcore.dll+b61a|C:\\Windows\\system32\\explorerframe.dll+12b9d0|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+157bd8c|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+18051ce|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2e1a17|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2dfdfe|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+130216a|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a566ec|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a5695f|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3e93b|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c40433|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb8e|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3d62541|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb68|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c942dd|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3db94|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a52491|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a3e4a9\r\nSourceUser: WEB1\\Administrator\r\nTargetUser: WEB1\\Administrator"',
1759 + data_win_eventdata_sourceThreadId: "1284",
1760 + msg_timestamp: "2023-11-05T12:57:27.886Z",
1761 + rule_group2: "sysmon_eid10_detections",
1762 + rule_group1: "sysmon"
1763 + },
1764 + sort: [1699189046938]
1765 + },
1766 + {
1767 + _index: "wazuh-wso4vxhq_8",
1768 + _id: "e1c93160-7bda-11ee-93bc-86000046278a",
1769 + _score: null,
1770 + _source: {
1771 + source_reserved_ip: true,
1772 + data_win_system_eventRecordID: "4882777",
1773 + agent_id: "070",
1774 + agent_name: "web1",
1775 + data_win_eventdata_sourceProcessGUID: "{d9ab9ebb-7e1a-6544-44e0-010000003000}",
1776 + gl2_remote_ip: "10.255.255.13",
1777 + data_win_system_eventID: "10",
1778 + gl2_remote_port: 57078,
1779 + agent_labels_customer: "wso4vxhq",
1780 + agent_ip_city_name: "N/A",
1781 + source: "10.255.255.13",
1782 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
1783 + gl2_source_input: "6459151dea00fd5d3da2df91",
1784 + rule_level: 12,
1785 + data_win_eventdata_sourceUser: "WEB1\\\\Administrator",
1786 + data_win_system_task: "10",
1787 + timestamp_utc: "2023-11-05T12:57:26.937Z",
1788 + syslog_type: "wazuh",
1789 + data_win_system_threadID: "5576",
1790 + rule_description:
1791 + "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
1792 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1793 + id: "1699189047.740498545",
1794 + data_win_eventdata_grantedAccess: "0x40",
1795 + data_win_eventdata_sourceImage:
1796 + "C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe",
1797 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
1798 + gl2_accounted_message_size: 11630,
1799 + data_win_eventdata_utcTime: "2023-11-05 12:57:26.937",
1800 + streams: ["650d3da25e9a2d550c6d6491"],
1801 + rule_mitre_id: "T1055",
1802 + gl2_message_id: "01HEFRYM7700MP8Y22100S2SEW",
1803 + data_win_system_computer: "web1",
1804 + data_win_eventdata_ruleName: "technique_id=T1036,technique_name=Masquerading",
1805 + agent_ip: "202.43.110.138",
1806 + true: 1699189047.610539,
1807 + rule_groups: "sysmon, sysmon_eid10_detections, windows",
1808 + data_win_system_keywords: "0x8000000000000000",
1809 + data_win_system_level: "4",
1810 + data_win_eventdata_targetProcessGUID: "{d9ab9ebb-62bb-6547-601c-020000003000}",
1811 + data_win_system_severityValue: "INFORMATION",
1812 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
1813 + agent_ip_geolocation: "16.1667,107.8333",
1814 + rule_mitre_technique: "Process Injection",
1815 + rule_firedtimes: 85,
1816 + data_win_system_systemTime: "2023-11-05T12:57:26.937850100Z",
1817 + rule_mail: true,
1818 + decoder_name: "windows_eventchannel",
1819 + agent_ip_country_code: "VN",
1820 + data_win_system_processID: "3468",
1821 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1822 + syslog_level: "ALERT",
1823 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1824 + data_win_system_version: "3",
1825 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1826 + timestamp: "2023-11-05 12:57:31.623",
1827 + data_win_eventdata_callTrace:
1828 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+b55c|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9",
1829 + data_win_system_opcode: "0",
1830 + gl2_processing_error:
1831 + 'Replaced invalid timestamp value in message <e1c93160-7bda-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:57:27.609+0000> caused exception: Invalid format: "2023-11-05T12:57:27.609+0000" is malformed at "T12:57:27.609+0000".',
1832 + data_win_eventdata_sourceProcessId: "1652",
1833 + message:
1834 + '{"true":1699189047.610539,"timestamp":"2023-11-05T12:57:27.609+0000","rule":{"level":12,"description":"Explorer process was accessed by C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe, possible process injection","id":"92910","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":85,"mail":true,"groups":["sysmon","sysmon_eid10_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699189047.740498545","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"10","version":"3","level":"4","task":"10","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:57:26.937850100Z","eventRecordID":"4882777","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"Process accessed:\\r\\nRuleName: technique_id=T1036,technique_name=Masquerading\\r\\nUtcTime: 2023-11-05 12:57:26.937\\r\\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\\r\\nSourceProcessId: 1652\\r\\nSourceThreadId: 1284\\r\\nSourceImage: C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe\\r\\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nGrantedAccess: 0x40\\r\\nCallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9ff24|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+1668e|C:\\\\Windows\\\\System32\\\\shcore.dll+cca8|C:\\\\Windows\\\\System32\\\\shcore.dll+b55c|C:\\\\Windows\\\\System32\\\\shcore.dll+b275|C:\\\\Windows\\\\System32\\\\shcore.dll+b209|C:\\\\Windows\\\\System32\\\\shcore.dll+b104|C:\\\\Windows\\\\system32\\\\explorerframe.dll+12b986|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+157bd8c|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+18051ce|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2e1a17|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+2dfdfe|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+130216a|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a566ec|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a5695f|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3e93b|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c40433|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb8e|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3d62541|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3fbdb68|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c942dd|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+3c3db94|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a52491|C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe+a3e4a9\\r\\nSourceUser: WEB1\\\\Administrator\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1036,technique_name=Masquerading","utcTime":"2023-11-05 12:57:26.937","sourceProcessGUID":"{d9ab9ebb-7e1a-6544-44e0-010000003000}","sourceProcessId":"1652","sourceThreadId":"1284","sourceImage":"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe","targetProcessGUID":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","grantedAccess":"0x40","callTrace":"C:\\\\\\\\Windows\\\\\\\\SYSTEM32\\\\\\\\ntdll.dll+9ff24|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNELBASE.dll+1668e|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+cca8|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b55c|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b275|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b209|C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\shcore.dll+b104|C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\explorerframe.dll+12b986|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+157bd8c|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+18051ce|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2e1a17|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+2dfdfe|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+130216a|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a566ec|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a5695f|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3e93b|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c40433|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb8e|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3d62541|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3fbdb68|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c942dd|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+3c3db94|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a52491|C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\AppData\\\\\\\\Roaming\\\\\\\\Telegram Desktop\\\\\\\\Telegram.exe+a3e4a9","sourceUser":"WEB1\\\\\\\\Administrator","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
1835 + rule_id: "92910",
1836 + manager_name: "ASHWZHMA",
1837 + location: "EventChannel",
1838 + data_win_eventdata_targetProcessId: "4384",
1839 + rule_group3: "windows",
1840 + data_win_system_message:
1841 + '"Process accessed:\r\nRuleName: technique_id=T1036,technique_name=Masquerading\r\nUtcTime: 2023-11-05 12:57:26.937\r\nSourceProcessGUID: {d9ab9ebb-7e1a-6544-44e0-010000003000}\r\nSourceProcessId: 1652\r\nSourceThreadId: 1284\r\nSourceImage: C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe\r\nTargetProcessGUID: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nGrantedAccess: 0x40\r\nCallTrace: C:\\Windows\\SYSTEM32\\ntdll.dll+9ff24|C:\\Windows\\System32\\KERNELBASE.dll+1668e|C:\\Windows\\System32\\shcore.dll+cca8|C:\\Windows\\System32\\shcore.dll+b55c|C:\\Windows\\System32\\shcore.dll+b275|C:\\Windows\\System32\\shcore.dll+b209|C:\\Windows\\System32\\shcore.dll+b104|C:\\Windows\\system32\\explorerframe.dll+12b986|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+157bd8c|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+18051ce|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2e1a17|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+2dfdfe|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+130216a|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a566ec|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a5695f|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3e93b|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c40433|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb8e|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3d62541|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3fbdb68|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c942dd|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+3c3db94|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a52491|C:\\Users\\Administrator\\AppData\\Roaming\\Telegram Desktop\\Telegram.exe+a3e4a9\r\nSourceUser: WEB1\\Administrator\r\nTargetUser: WEB1\\Administrator"',
1842 + data_win_eventdata_sourceThreadId: "1284",
1843 + msg_timestamp: "2023-11-05T12:57:27.609Z",
1844 + rule_group2: "sysmon_eid10_detections",
1845 + rule_group1: "sysmon"
1846 + },
1847 + sort: [1699189046937]
1848 + },
1849 + {
1850 + _index: "wazuh-wso4vxhq_8",
1851 + _id: "85668701-7bda-11ee-93bc-86000046278a",
1852 + _score: null,
1853 + _source: {
1854 + data_win_eventdata_newThreadId: "10040",
1855 + source_reserved_ip: true,
1856 + data_win_system_eventRecordID: "4882503",
1857 + agent_id: "070",
1858 + agent_name: "web1",
1859 + gl2_remote_ip: "10.255.255.13",
1860 + data_win_system_eventID: "8",
1861 + gl2_remote_port: 48272,
1862 + agent_labels_customer: "wso4vxhq",
1863 + agent_ip_city_name: "N/A",
1864 + source: "10.255.255.13",
1865 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\explorer.exe",
1866 + gl2_source_input: "6459151dea00fd5d3da2df91",
1867 + rule_level: 12,
1868 + data_win_eventdata_sourceUser: "NT AUTHORITY\\\\SYSTEM",
1869 + data_win_system_task: "8",
1870 + timestamp_utc: "2023-11-05T12:54:51.363Z",
1871 + syslog_type: "wazuh",
1872 + data_win_system_threadID: "5576",
1873 + rule_description:
1874 + "Possible code injection on explorer.exe by C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe",
1875 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1876 + id: "1699188892.737055402",
1877 + data_win_eventdata_sourceImage: "C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe",
1878 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
1879 + data_win_eventdata_targetProcessGuid: "{d9ab9ebb-62bb-6547-601c-020000003000}",
1880 + gl2_accounted_message_size: 5134,
1881 + data_win_eventdata_utcTime: "2023-11-05 12:54:51.363",
1882 + streams: ["650d3da25e9a2d550c6d6491"],
1883 + rule_mitre_id: "T1055",
1884 + gl2_message_id: "01HEFRSWVHDK18DFBYAWFFH0Q5",
1885 + data_win_system_computer: "web1",
1886 + data_win_eventdata_ruleName: "technique_id=T1055,technique_name=Process Injection",
1887 + agent_ip: "202.43.110.138",
1888 + data_win_eventdata_startAddress: "0x00007FFDE5CCE720",
1889 + true: 1699188892.065792,
1890 + rule_groups: "sysmon, sysmon_eid8_detections, windows",
1891 + data_win_system_keywords: "0x8000000000000000",
1892 + data_win_system_level: "4",
1893 + data_win_system_severityValue: "INFORMATION",
1894 + data_win_eventdata_targetUser: "WEB1\\\\Administrator",
1895 + agent_ip_geolocation: "16.1667,107.8333",
1896 + rule_mitre_technique: "Process Injection",
1897 + rule_firedtimes: 2,
1898 + data_win_system_systemTime: "2023-11-05T12:54:51.363981300Z",
1899 + rule_mail: true,
1900 + decoder_name: "windows_eventchannel",
1901 + agent_ip_country_code: "VN",
1902 + data_win_system_processID: "3468",
1903 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1904 + syslog_level: "ALERT",
1905 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1906 + data_win_system_version: "2",
1907 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1908 + timestamp: "2023-11-05 12:54:56.625",
1909 + data_win_system_opcode: "0",
1910 + gl2_processing_error:
1911 + 'Replaced invalid timestamp value in message <85668701-7bda-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:54:52.014+0000> caused exception: Invalid format: "2023-11-05T12:54:52.014+0000" is malformed at "T12:54:52.014+0000".',
1912 + data_win_eventdata_sourceProcessId: "3368",
1913 + data_win_eventdata_startModule: "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL",
1914 + message:
1915 + '{"true":1699188892.065792,"timestamp":"2023-11-05T12:54:52.014+0000","rule":{"level":12,"description":"Possible code injection on explorer.exe by C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe","id":"92400","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":2,"mail":true,"groups":["sysmon","sysmon_eid8_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699188892.737055402","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"8","version":"2","level":"4","task":"8","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:54:51.363981300Z","eventRecordID":"4882503","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"CreateRemoteThread detected:\\r\\nRuleName: technique_id=T1055,technique_name=Process Injection\\r\\nUtcTime: 2023-11-05 12:54:51.363\\r\\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\\r\\nSourceProcessId: 3368\\r\\nSourceImage: C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe\\r\\nTargetProcessGuid: {d9ab9ebb-62bb-6547-601c-020000003000}\\r\\nTargetProcessId: 4384\\r\\nTargetImage: C:\\\\Windows\\\\explorer.exe\\r\\nNewThreadId: 10040\\r\\nStartAddress: 0x00007FFDE5CCE720\\r\\nStartModule: C:\\\\Windows\\\\System32\\\\KERNEL32.DLL\\r\\nStartFunction: GetCommandLineW\\r\\nSourceUser: NT AUTHORITY\\\\SYSTEM\\r\\nTargetUser: WEB1\\\\Administrator\\""},"eventdata":{"ruleName":"technique_id=T1055,technique_name=Process Injection","utcTime":"2023-11-05 12:54:51.363","sourceProcessGuid":"{d9ab9ebb-48da-652f-4900-000000003000}","sourceProcessId":"3368","sourceImage":"C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe","targetProcessGuid":"{d9ab9ebb-62bb-6547-601c-020000003000}","targetProcessId":"4384","targetImage":"C:\\\\\\\\Windows\\\\\\\\explorer.exe","newThreadId":"10040","startAddress":"0x00007FFDE5CCE720","startModule":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNEL32.DLL","startFunction":"GetCommandLineW","sourceUser":"NT AUTHORITY\\\\\\\\SYSTEM","targetUser":"WEB1\\\\\\\\Administrator"}}},"location":"EventChannel"}',
1916 + rule_id: "92400",
1917 + manager_name: "ASHWZHMA",
1918 + data_win_eventdata_sourceProcessGuid: "{d9ab9ebb-48da-652f-4900-000000003000}",
1919 + location: "EventChannel",
1920 + data_win_eventdata_targetProcessId: "4384",
1921 + data_win_eventdata_startFunction: "GetCommandLineW",
1922 + rule_group3: "windows",
1923 + data_win_system_message:
1924 + '"CreateRemoteThread detected:\r\nRuleName: technique_id=T1055,technique_name=Process Injection\r\nUtcTime: 2023-11-05 12:54:51.363\r\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\r\nSourceProcessId: 3368\r\nSourceImage: C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe\r\nTargetProcessGuid: {d9ab9ebb-62bb-6547-601c-020000003000}\r\nTargetProcessId: 4384\r\nTargetImage: C:\\Windows\\explorer.exe\r\nNewThreadId: 10040\r\nStartAddress: 0x00007FFDE5CCE720\r\nStartModule: C:\\Windows\\System32\\KERNEL32.DLL\r\nStartFunction: GetCommandLineW\r\nSourceUser: NT AUTHORITY\\SYSTEM\r\nTargetUser: WEB1\\Administrator"',
1925 + msg_timestamp: "2023-11-05T12:54:52.014Z",
1926 + rule_group2: "sysmon_eid8_detections",
1927 + rule_group1: "sysmon"
1928 + },
1929 + sort: [1699188891363]
1930 + },
1931 + {
1932 + _index: "wazuh-wso4vxhq_8",
1933 + _id: "797bfd81-7bda-11ee-93bc-86000046278a",
1934 + _score: null,
1935 + _source: {
1936 + data_win_eventdata_newThreadId: "13164",
1937 + source_reserved_ip: true,
1938 + data_win_system_eventRecordID: "4882339",
1939 + agent_id: "070",
1940 + agent_name: "web1",
1941 + gl2_remote_ip: "10.255.255.13",
1942 + data_win_system_eventID: "8",
1943 + gl2_remote_port: 44978,
1944 + agent_labels_customer: "wso4vxhq",
1945 + agent_ip_city_name: "N/A",
1946 + source: "10.255.255.13",
1947 + data_win_eventdata_targetImage: "C:\\\\Windows\\\\System32\\\\lsass.exe",
1948 + gl2_source_input: "6459151dea00fd5d3da2df91",
1949 + rule_level: 12,
1950 + data_win_eventdata_sourceUser: "NT AUTHORITY\\\\SYSTEM",
1951 + data_win_system_task: "8",
1952 + timestamp_utc: "2023-11-05T12:54:32.314Z",
1953 + syslog_type: "wazuh",
1954 + data_win_system_threadID: "5576",
1955 + rule_description:
1956 + "Local Security Authority Subsystem Service (LSASS) process was accessed by C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe, possible code injection for credential dumping",
1957 + gl2_source_node: "809d9894-1865-4ac7-8204-3226c347cb38",
1958 + id: "1699188873.736407303",
1959 + data_win_eventdata_sourceImage: "C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe",
1960 + rule_mitre_tactic: "Defense Evasion, Privilege Escalation",
1961 + data_win_eventdata_targetProcessGuid: "{d9ab9ebb-48d7-652f-0c00-000000003000}",
1962 + gl2_accounted_message_size: 5323,
1963 + data_win_eventdata_utcTime: "2023-11-05 12:54:32.313",
1964 + streams: ["650d3da25e9a2d550c6d6491"],
1965 + rule_mitre_id: "T1055",
1966 + gl2_message_id: "01HEFRS9ASFK4K72X8JPRDYJS6",
1967 + data_win_system_computer: "web1",
1968 + data_win_eventdata_ruleName: "technique_id=T1055,technique_name=Process Injection",
1969 + agent_ip: "202.43.110.138",
1970 + data_win_eventdata_startAddress: "0x00007FFDE5CCE720",
1971 + true: 1699188873.447718,
1972 + rule_groups: "sysmon, sysmon_eid8_detections, windows",
1973 + data_win_system_keywords: "0x8000000000000000",
1974 + data_win_system_level: "4",
1975 + data_win_system_severityValue: "INFORMATION",
1976 + data_win_eventdata_targetUser: "NT AUTHORITY\\\\SYSTEM",
1977 + agent_ip_geolocation: "16.1667,107.8333",
1978 + rule_mitre_technique: "Process Injection",
1979 + rule_firedtimes: 2,
1980 + data_win_system_systemTime: "2023-11-05T12:54:32.314179900Z",
1981 + rule_mail: true,
1982 + decoder_name: "windows_eventchannel",
1983 + agent_ip_country_code: "VN",
1984 + data_win_system_processID: "3468",
1985 + data_win_system_channel: "Microsoft-Windows-Sysmon/Operational",
1986 + syslog_level: "ALERT",
1987 + data_win_system_providerName: "Microsoft-Windows-Sysmon",
1988 + data_win_system_version: "2",
1989 + data_win_system_providerGuid: "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
1990 + timestamp: "2023-11-05 12:54:36.633",
1991 + data_win_system_opcode: "0",
1992 + gl2_processing_error:
1993 + 'Replaced invalid timestamp value in message <797bfd81-7bda-11ee-93bc-86000046278a> with current time - Value <2023-11-05T12:54:33.443+0000> caused exception: Invalid format: "2023-11-05T12:54:33.443+0000" is malformed at "T12:54:33.443+0000".',
1994 + data_win_eventdata_sourceProcessId: "3368",
1995 + data_win_eventdata_startModule: "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL",
1996 + message:
1997 + '{"true":1699188873.447718,"timestamp":"2023-11-05T12:54:33.443+0000","rule":{"level":12,"description":"Local Security Authority Subsystem Service (LSASS) process was accessed by C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe, possible code injection for credential dumping","id":"92403","mitre":{"id":["T1055"],"tactic":["Defense Evasion","Privilege Escalation"],"technique":["Process Injection"]},"firedtimes":2,"mail":true,"groups":["sysmon","sysmon_eid8_detections","windows"]},"agent":{"id":"070","name":"web1","ip":"202.43.110.138","labels":{"customer":"wso4vxhq"}},"manager":{"name":"ASHWZHMA"},"id":"1699188873.736407303","decoder":{"name":"windows_eventchannel"},"data":{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","providerGuid":"{5770385f-c22a-43e0-bf4c-06f5698ffbd9}","eventID":"8","version":"2","level":"4","task":"8","opcode":"0","keywords":"0x8000000000000000","systemTime":"2023-11-05T12:54:32.314179900Z","eventRecordID":"4882339","processID":"3468","threadID":"5576","channel":"Microsoft-Windows-Sysmon/Operational","computer":"web1","severityValue":"INFORMATION","message":"\\"CreateRemoteThread detected:\\r\\nRuleName: technique_id=T1055,technique_name=Process Injection\\r\\nUtcTime: 2023-11-05 12:54:32.313\\r\\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\\r\\nSourceProcessId: 3368\\r\\nSourceImage: C:\\\\Program Files\\\\VMware\\\\VMware Tools\\\\vmtoolsd.exe\\r\\nTargetProcessGuid: {d9ab9ebb-48d7-652f-0c00-000000003000}\\r\\nTargetProcessId: 740\\r\\nTargetImage: C:\\\\Windows\\\\System32\\\\lsass.exe\\r\\nNewThreadId: 13164\\r\\nStartAddress: 0x00007FFDE5CCE720\\r\\nStartModule: C:\\\\Windows\\\\System32\\\\KERNEL32.DLL\\r\\nStartFunction: GetCommandLineW\\r\\nSourceUser: NT AUTHORITY\\\\SYSTEM\\r\\nTargetUser: NT AUTHORITY\\\\SYSTEM\\""},"eventdata":{"ruleName":"technique_id=T1055,technique_name=Process Injection","utcTime":"2023-11-05 12:54:32.313","sourceProcessGuid":"{d9ab9ebb-48da-652f-4900-000000003000}","sourceProcessId":"3368","sourceImage":"C:\\\\\\\\Program Files\\\\\\\\VMware\\\\\\\\VMware Tools\\\\\\\\vmtoolsd.exe","targetProcessGuid":"{d9ab9ebb-48d7-652f-0c00-000000003000}","targetProcessId":"740","targetImage":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\lsass.exe","newThreadId":"13164","startAddress":"0x00007FFDE5CCE720","startModule":"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\KERNEL32.DLL","startFunction":"GetCommandLineW","sourceUser":"NT AUTHORITY\\\\\\\\SYSTEM","targetUser":"NT AUTHORITY\\\\\\\\SYSTEM"}}},"location":"EventChannel"}',
1998 + rule_id: "92403",
1999 + manager_name: "ASHWZHMA",
2000 + data_win_eventdata_sourceProcessGuid: "{d9ab9ebb-48da-652f-4900-000000003000}",
2001 + location: "EventChannel",
2002 + data_win_eventdata_targetProcessId: "740",
2003 + data_win_eventdata_startFunction: "GetCommandLineW",
2004 + rule_group3: "windows",
2005 + data_win_system_message:
2006 + '"CreateRemoteThread detected:\r\nRuleName: technique_id=T1055,technique_name=Process Injection\r\nUtcTime: 2023-11-05 12:54:32.313\r\nSourceProcessGuid: {d9ab9ebb-48da-652f-4900-000000003000}\r\nSourceProcessId: 3368\r\nSourceImage: C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe\r\nTargetProcessGuid: {d9ab9ebb-48d7-652f-0c00-000000003000}\r\nTargetProcessId: 740\r\nTargetImage: C:\\Windows\\System32\\lsass.exe\r\nNewThreadId: 13164\r\nStartAddress: 0x00007FFDE5CCE720\r\nStartModule: C:\\Windows\\System32\\KERNEL32.DLL\r\nStartFunction: GetCommandLineW\r\nSourceUser: NT AUTHORITY\\SYSTEM\r\nTargetUser: NT AUTHORITY\\SYSTEM"',
2007 + msg_timestamp: "2023-11-05T12:54:33.443Z",
2008 + rule_group2: "sysmon_eid8_detections",
2009 + rule_group1: "sysmon"
2010 + },
2011 + sort: [1699188872314]
2012 + }
2013 + ]
2014 + }
2015 +]
2016 +
2017 +const alerts_by_host = [
2018 + {
2019 + agent_name: "ip-178-216-201-141",
2020 + number_of_alerts: 4
2021 + },
2022 + {
2023 + agent_name: "WinDev2308Eval",
2024 + number_of_alerts: 10
2025 + },
2026 + {
2027 + agent_name: "ANSYDWDC01",
2028 + number_of_alerts: 20
2029 + },
2030 + {
2031 + agent_name: "web1",
2032 + number_of_alerts: 10
2033 + },
2034 + {
2035 + agent_name: "ssdnodes-zabbix",
2036 + number_of_alerts: 4
2037 + }
2038 +]
2039 +
2040 +const alerts_by_rule = [
2041 + {
2042 + rule: "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection",
2043 + number_of_alerts: 6
2044 + },
2045 + {
2046 + rule: "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Local\\\\Programs\\\\Messenger\\\\Messenger.exe, possible process injection",
2047 + number_of_alerts: 4
2048 + },
2049 + {
2050 + rule: "Adversaries may use binary padding to add junk data and change the on-disk representation of malware. This rule detect using dd and truncate to add a junk data to file.",
2051 + number_of_alerts: 1
2052 + },
2053 + {
2054 + rule: "Detects file and folder permission changes.",
2055 + number_of_alerts: 7
2056 + },
2057 + {
2058 + rule: "Process Explorer Driver Creation By Non-Sysinternals Binary",
2059 + number_of_alerts: 20
2060 + },
2061 + {
2062 + rule: "Application Compatibility Database launched",
2063 + number_of_alerts: 10
2064 + }
2065 +]
2066 +
2067 +const alerts_by_rule_per_host = [
2068 + {
2069 + agent_name: "ANSYDWDC01",
2070 + number_of_alerts: 20,
2071 + rule: "Process Explorer Driver Creation By Non-Sysinternals Binary"
2072 + },
2073 + {
2074 + agent_name: "WinDev2308Eval",
2075 + number_of_alerts: 10,
2076 + rule: "Application Compatibility Database launched"
2077 + },
2078 + {
2079 + agent_name: "ip-178-216-201-141",
2080 + number_of_alerts: 4,
2081 + rule: "Detects file and folder permission changes."
2082 + },
2083 + {
2084 + agent_name: "web1",
2085 + number_of_alerts: 9,
2086 + rule: "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Roaming\\\\Telegram Desktop\\\\Telegram.exe, possible process injection"
2087 + },
2088 + {
2089 + agent_name: "web1",
2090 + number_of_alerts: 1,
2091 + rule: "Explorer process was accessed by C:\\\\Users\\\\Administrator\\\\AppData\\\\Local\\\\Programs\\\\Messenger\\\\Messenger.exe, possible process injection"
2092 + },
2093 + {
2094 + agent_name: "ssdnodes-zabbix",
2095 + number_of_alerts: 1,
2096 + rule: "Adversaries may use binary padding to add junk data and change the on-disk representation of malware. This rule detect using dd and truncate to add a junk data to file."
2097 + },
2098 + {
2099 + agent_name: "ssdnodes-zabbix",
2100 + number_of_alerts: 3,
2101 + rule: "Detects file and folder permission changes."
2102 + }
2103 +]
2104 +
2105 +export { alerts_summary, alerts_by_host, alerts_by_rule, alerts_by_rule_per_host }
src/components/apps/Mailbox/Email.vue
+2 -2
@@ -32,10 +32,10 @@
32 <div class="attachments flex" v-if="email.attachments.length">
33 <Icon :size="16" :name="AttachmentIcon"></Icon>
34 </div>
35 - <div class="date opacity-70">
35 + <div class="date text-secondary-color">
36 {{ email.dateText }}
37 </div>
38 - <div class="actions opacity-70 flex items-start gap-3">
38 + <div class="actions text-secondary-color flex items-start gap-3">
39 <n-button text>
40 <Icon :size="20" :name="TrashIcon"></Icon>
41 </n-button>
src/components/apps/Mailbox/EmailView.vue
+1 -1
@@ -67,7 +67,7 @@
67 </n-button>
68 </div>
69 <div class="nav-btns flex items-center gap-2">
70 - <span class="opacity-70">1 - 30 of 635</span>
70 + <span class="text-secondary-color">1 - 30 of 635</span>
71 <n-button text size="small">
72 <Icon :size="24" :name="ChevronLeftIcon" />
73 </n-button>
src/components/cards/ecommerce/CardEcommerce1.vue
+1 -1
@@ -5,7 +5,7 @@
5 </template>
6 <template #header>
7 <span>Super bike</span>
8 - <span class="ml-3 opacity-70">$644,00</span>
8 + <span class="ml-3 text-secondary-color">$644,00</span>
9 </template>
10 <template #header-extra>
11 <Icon class="mr-3" :size="20" color="#FF0156" :name="HeartIcon"></Icon>
src/components/cards/ecommerce/CardEcommerce2.vue
+1 -1
@@ -8,7 +8,7 @@
8 <div class="card-header flex justify-between items-center">
9 <div>
10 <span>Premium Headphones</span>
11 - <span class="ml-3 opacity-70">$399,00</span>
11 + <span class="ml-3 text-secondary-color">$399,00</span>
12 </div>
13 <div class="flex items-center">
14 <Icon class="mr-3" :size="20" :name="HeartIcon"></Icon>
src/components/cards/ecommerce/CardEcommerce4.vue
+1 -1
@@ -40,7 +40,7 @@
40 <strong>$299</strong>
41 <span>/year</span>
42 <br />
43 - <small class="opacity-60">$24,90/month</small>
43 + <small class="text-secondary-color">$24,90/month</small>
44 </div>
45 </div>
46 <n-button type="primary">
src/components/common/LayoutSettings.vue
+3 -3
@@ -72,7 +72,7 @@
72 <div class="ls-section ls-nav-selection">
73 <div class="ls-label">
74 Navbar
75 - <span v-if="isMobileView" class="opacity-60">(desktop only)</span>
75 + <span v-if="isMobileView" class="text-secondary-color">(desktop only)</span>
76 </div>
77 <div class="flex items-center gap-2">
78 <div class="basis-1/2">
@@ -102,14 +102,14 @@
102 <div class="flex justify-between items-center">
103 <div class="switch-label">
104 View boxed
105 - <span v-if="isMobileView" class="opacity-60">(desktop only)</span>
105 + <span v-if="isMobileView" class="text-secondary-color">(desktop only)</span>
106 </div>
107 <n-switch v-model:value="boxed" :disabled="isMobileView" size="small" />
108 </div>
109 <div class="flex justify-between items-center">
110 <div class="switch-label">
111 Toolbar boxed
112 - <span v-if="isMobileView" class="opacity-60">(desktop only)</span>
112 + <span v-if="isMobileView" class="text-secondary-color">(desktop only)</span>
113 </div>
114 <n-switch
115 v-model:value="toolbarBoxed"
src/components/graylog/Alerts/Item.vue
+33 -3
@@ -1,5 +1,5 @@
1 <template>
2 - <div class="item flex flex-col mb-2 gap-2 px-5 py-3">
2 + <div class="item flex flex-col gap-2 px-5 py-3">
3 <div class="header-box flex justify-between">
4 <div class="id">
5 <n-popover overlap placement="bottom-start">
@@ -12,7 +12,12 @@
12 <div class="flex flex-col gap-1">
13 <div class="box">
14 event_definition_id:
15 - <code>{{ alertsEvent.event.event_definition_id }}</code>
15 + <code
16 + class="cursor-pointer text-primary-color"
17 + @click="gotoEventsPage(alertsEvent.event.event_definition_id)"
18 + >
19 + {{ alertsEvent.event.event_definition_id }}
20 + </code>
21 </div>
22 <div class="box">
23 event_definition_type:
@@ -24,7 +29,12 @@
29 </div>
30 <div class="box">
31 index_name:
27 - <code>{{ alertsEvent.index_name }}</code>
32 + <code
33 + class="cursor-pointer text-primary-color"
34 + @click="gotoIndicesPage(alertsEvent.index_name)"
35 + >
36 + {{ alertsEvent.index_name }}
37 + </code>
38 </div>
39 <div class="box">
40 index_type:
@@ -79,16 +89,31 @@ import { NPopover } from "naive-ui"
89 import { useSettingsStore } from "@/stores/settings"
90 import dayjs from "@/utils/dayjs"
91 import Icon from "@/components/common/Icon.vue"
92 +import { useRouter } from "vue-router"
93
94 const { alertsEvent } = defineProps<{ alertsEvent: AlertsEventElement }>()
95
96 +const emit = defineEmits<{
97 + (e: "clickEvent", value: string): void
98 +}>()
99 +
100 const InfoIcon = "carbon:information"
101 const TimeIcon = "carbon:time"
102 +
103 +const router = useRouter()
104 const dFormats = useSettingsStore().dateFormat
105
106 function formatDate(timestamp: string): string {
107 return dayjs(timestamp).format(dFormats.datetimesec)
108 }
109 +
110 +function gotoIndicesPage(index: string) {
111 + router.push(`/indices?index_name=${index}`).catch(() => {})
112 +}
113 +
114 +function gotoEventsPage(event_definition_id: string) {
115 + emit("clickEvent", event_definition_id)
116 +}
117 </script>
118
119 <style lang="scss" scoped>
@@ -115,6 +140,11 @@ function formatDate(timestamp: string): string {
140 color: var(--primary-color);
141 }
142 }
143 +
144 + .actionable {
145 + cursor: pointer;
146 + color: var(--primary-color);
147 + }
148 }
149 .main-box {
150 .content {
src/components/graylog/Alerts/List.vue
+12 -2
@@ -5,7 +5,7 @@
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-color border-radius">
8 - <n-button size="small">
8 + <n-button size="small" class="!cursor-help">
9 <template #icon>
10 <Icon :name="InfoIcon"></Icon>
11 </template>
@@ -45,7 +45,7 @@
45 </div>
46 </template>
47 <div class="mb-2">
48 - <div class="opacity-50 text-sm my-1">Time range:</div>
48 + <div class="text-secondary-color text-sm my-1">Time range:</div>
49 <n-select size="small" v-model:value="timerange" :options="timeOptions" class="!w-32 mb-1" />
50 </div>
51 </n-popover>
@@ -56,6 +56,8 @@
56 v-for="alertsEvent of alertsEvents"
57 :key="alertsEvent.event.id"
58 :alertsEvent="alertsEvent"
59 + @click-event="gotoEventsPage($event)"
60 + class="mb-2"
61 />
62 </template>
63 <template v-else>
@@ -84,6 +86,10 @@ import dayjs from "@/utils/dayjs"
86 import Icon from "@/components/common/Icon.vue"
87 import type { AlertsQuery, AlertsEventElement } from "@/types/graylog/alerts.d"
88
89 +const emit = defineEmits<{
90 + (e: "clickEvent", value: string): void
91 +}>()
92 +
93 const message = useMessage()
94 const loading = ref(false)
95 const alertsEvents = ref<AlertsEventElement[]>([])
@@ -140,6 +146,10 @@ const timeOptions = [
146 }
147 ]
148
149 +function gotoEventsPage(event_definition_id: string) {
150 + emit("clickEvent", event_definition_id)
151 +}
152 +
153 function getData(page: number, pageSize: number, timerange: number) {
154 loading.value = true
155
src/components/graylog/Events/Item.vue
+12 -5
@@ -1,5 +1,5 @@
1 <template>
2 - <div class="item flex flex-col mb-2 gap-2 px-5 py-3">
2 + <div class="item flex flex-col gap-2 px-5 py-3" :class="{ highlight }" :id="'event-' + event.id">
3 <div class="header-box flex justify-between">
4 <div class="flex items-center gap-3">
5 <n-tooltip trigger="hover">
@@ -49,6 +49,7 @@
49 :value="event?.config?.query"
50 type="textarea"
51 readonly
52 + placeholder="Empty"
53 :autosize="{
54 minRows: 3,
55 maxRows: 10
@@ -71,14 +72,15 @@
72 </template>
73
74 <script setup lang="ts">
74 -import { ref } from "vue"
75 +import { ref, toRefs } from "vue"
76 import type { EventDefinition } from "@/types/graylog/event-definition.d"
77 import Icon from "@/components/common/Icon.vue"
78 import { SimpleJsonViewer } from "vue-sjv"
79 import "@/assets/scss/vuesjv-override.scss"
80 import { NModal, NTabs, NTabPane, NInput, NTooltip } from "naive-ui"
81
81 -const { event } = defineProps<{ event: EventDefinition }>()
82 +const props = defineProps<{ event: EventDefinition; highlight: boolean | null | undefined }>()
83 +const { event, highlight } = toRefs(props)
84
85 const InfoIcon = "carbon:information"
86
@@ -102,8 +104,8 @@ const showDetails = ref(false)
104 height: 20px;
105 border-radius: 99999px;
106 text-align: center;
105 - line-height: 20px;
106 - font-size: 12px;
107 + line-height: 19px;
108 + font-size: 11px;
109 }
110 .id {
111 word-break: break-word;
@@ -137,6 +139,11 @@ const showDetails = ref(false)
139 }
140 }
141
142 + &.highlight {
143 + background-color: var(--primary-005-color);
144 + box-shadow: 0px 0px 0px 1px inset var(--primary-030-color);
145 + }
146 +
147 &:hover {
148 box-shadow: 0px 0px 0px 1px inset var(--primary-color);
149 }
src/components/graylog/Events/List.vue
+41 -3
@@ -5,7 +5,7 @@
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-color border-radius">
8 - <n-button size="small">
8 + <n-button size="small" class="!cursor-help">
9 <template #icon>
10 <Icon :name="InfoIcon"></Icon>
11 </template>
@@ -31,7 +31,13 @@
31 </div>
32 <div class="list my-3">
33 <template v-if="itemsPaginated.length">
34 - <EventItem v-for="event of itemsPaginated" :key="event.id" :event="event" />
34 + <EventItem
35 + v-for="event of itemsPaginated"
36 + :key="event.id"
37 + :event="event"
38 + :highlight="event.id === highlight"
39 + class="mb-2"
40 + />
41 </template>
42 <template v-else>
43 <n-empty description="No items found" v-if="!loading" />
@@ -41,7 +47,7 @@
47 </template>
48
49 <script setup lang="ts">
44 -import { ref, onBeforeMount, computed } from "vue"
50 +import { ref, onBeforeMount, computed, toRefs, nextTick, watch } from "vue"
51 import { useMessage, NSpin, NPopover, NButton, NSelect, NEmpty } from "naive-ui"
52 import EventItem from "./Item.vue"
53 import Api from "@/api"
@@ -49,6 +55,9 @@ import Icon from "@/components/common/Icon.vue"
55 import type { EventDefinition } from "@/types/graylog/event-definition.d"
56 import type { SelectMixedOption } from "naive-ui/es/select/src/interface"
57
58 +const props = defineProps<{ highlight: string | null | undefined }>()
59 +const { highlight } = toRefs(props)
60 +
61 const InfoIcon = "carbon:information"
62
63 const message = useMessage()
@@ -73,6 +82,17 @@ const itemsPaginated = computed(() => {
82 })
83 })
84
85 +function scrollToEvent(id: string) {
86 + const element = document.getElementById(`event-${id}`)
87 + const scrollContent = document.querySelector("#main > .n-scrollbar > .n-scrollbar-container") as HTMLElement
88 +
89 + if (element && scrollContent) {
90 + const wrap: HTMLElement = scrollContent
91 + const middle = element.offsetTop - wrap.offsetHeight / 2
92 + scrollContent?.scrollTo({ top: middle, behavior: "smooth" })
93 + }
94 +}
95 +
96 function getData() {
97 loading.value = true
98
@@ -82,6 +102,14 @@ function getData() {
102 if (res.data.success) {
103 events.value = res.data.event_definitions || []
104 total.value = events.value.length || 0
105 +
106 + nextTick(() => {
107 + setTimeout(() => {
108 + if (highlight.value) {
109 + scrollToEvent(highlight.value)
110 + }
111 + }, 300)
112 + })
113 } else {
114 message.warning(res.data?.message || "An error occurred. Please try again later.")
115 }
@@ -94,6 +122,16 @@ function getData() {
122 })
123 }
124
125 +watch(highlight, val => {
126 + if (val) {
127 + nextTick(() => {
128 + setTimeout(() => {
129 + scrollToEvent(val)
130 + })
131 + })
132 + }
133 +})
134 +
135 onBeforeMount(() => {
136 getData()
137 })
src/components/graylog/Inputs/Item.vue
+1 -1
@@ -1,5 +1,5 @@
1 <template>
2 - <div class="item flex flex-col mb-2 gap-2 px-5 py-3">
2 + <div class="item flex flex-col gap-2 px-5 py-3">
3 <div class="header-box flex justify-between">
4 <div class="info flex items-center gap-2">
5 <div class="user flex items-center gap-2">
src/components/graylog/Inputs/List.vue
+2 -1
@@ -5,7 +5,7 @@
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-color border-radius">
8 - <n-button size="small">
8 + <n-button size="small" class="!cursor-help">
9 <template #icon>
10 <Icon :name="InfoIcon"></Icon>
11 </template>
@@ -41,6 +41,7 @@
41 :key="input.id"
42 :input="input"
43 @updated="getData('running')"
44 + class="mb-2"
45 />
46 </template>
47 <template v-else>
src/components/graylog/Messages/Item.vue
+1 -1
@@ -1,5 +1,5 @@
1 <template>
2 - <div class="item flex flex-col mb-2 gap-2 px-5 py-3">
2 + <div class="item flex flex-col gap-2 px-5 py-3">
3 <div class="header-box flex justify-between">
4 <div class="caller">{{ message.caller }}</div>
5 <div class="time">{{ formatDate(message.timestamp) }}</div>
src/components/graylog/Messages/List.vue
+2 -2
@@ -5,7 +5,7 @@
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-color border-radius">
8 - <n-button size="small">
8 + <n-button size="small" class="!cursor-help">
9 <template #icon>
10 <Icon :name="InfoIcon"></Icon>
11 </template>
@@ -24,7 +24,7 @@
24 </div>
25 <div class="list my-3">
26 <template v-if="messages.length">
27 - <MessageItem v-for="msg of messages" :key="msg.id" :message="msg" />
27 + <MessageItem v-for="msg of messages" :key="msg.id" :message="msg" class="mb-2" />
28 </template>
29 <template v-else>
30 <n-empty description="No items found" v-if="!loading" />
src/components/graylog/Pipelines/PipeDetails.vue
+1 -2
@@ -29,7 +29,7 @@
29 <Icon :name="RulesIcon" :size="18"></Icon>
30 </template>
31 Rules
32 - <span class="font-mono ml-2 opacity-60">{{ stage.rules.length }}</span>
32 + <span class="font-mono ml-2 text-secondary-color">{{ stage.rules.length }}</span>
33 </n-button>
34 </template>
35
@@ -60,7 +60,6 @@ const emit = defineEmits<{
60 const props = defineProps<{ pipeline: PipelineFull }>()
61 const { pipeline } = toRefs(props)
62
63 -const TimeIcon = "carbon:time"
63 const RulesIcon = "ic:outline-swipe-right-alt"
64
65 const dFormats = useSettingsStore().dateFormat
src/components/graylog/Pipelines/PipeInfo.vue
+1
@@ -26,6 +26,7 @@
26 :value="pipeline?.source"
27 type="textarea"
28 readonly
29 + placeholder="Empty"
30 :autosize="{
31 minRows: 3,
32 maxRows: 10
src/components/graylog/Pipelines/Rule.vue
+1
@@ -69,6 +69,7 @@
69 :value="rule.source"
70 type="textarea"
71 readonly
72 + placeholder="Empty"
73 :autosize="{
74 minRows: 3,
75 maxRows: 10
src/components/graylog/Streams/Item.vue
+1 -1
@@ -1,5 +1,5 @@
1 <template>
2 - <div class="item flex flex-col mb-2 gap-2 px-5 py-3" :class="{ default: stream.is_default }">
2 + <div class="item flex flex-col gap-2 px-5 py-3" :class="{ default: stream.is_default }">
3 <div class="header-box flex justify-between">
4 <div class="info flex items-center gap-2">
5 <div class="user flex items-center gap-2">
src/components/graylog/Streams/List.vue
+4 -4
@@ -5,7 +5,7 @@
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-color border-radius">
8 - <n-button size="small">
8 + <n-button size="small" class="!cursor-help">
9 <template #icon>
10 <Icon :name="InfoIcon"></Icon>
11 </template>
@@ -45,7 +45,7 @@
45 </template>
46 <div class="py-1">
47 <div class="px-3">
48 - <div class="opacity-50 text-sm mb-1">Enabled:</div>
48 + <div class="text-secondary-color text-sm mb-1">Enabled:</div>
49 <n-select
50 size="small"
51 v-model:value="enabledFilter"
@@ -57,7 +57,7 @@
57 </div>
58 <n-divider class="!my-3" />
59 <div class="px-3">
60 - <div class="opacity-50 text-sm mb-1">Editable:</div>
60 + <div class="text-secondary-color text-sm mb-1">Editable:</div>
61 <n-select
62 size="small"
63 v-model:value="editableFilter"
@@ -72,7 +72,7 @@
72 </div>
73 <div class="list my-3">
74 <template v-if="itemsPaginated.length">
75 - <StreamItem v-for="stream of itemsPaginated" :key="stream.id" :stream="stream" />
75 + <StreamItem v-for="stream of itemsPaginated" :key="stream.id" :stream="stream" class="mb-2" />
76 </template>
77 <template v-else>
78 <n-empty description="No items found" v-if="!loading" />
src/components/indices/NodeAllocation.vue
+1 -1
@@ -3,7 +3,7 @@
3 <template #header>
4 <div class="flex align-center justify-between">
5 <span>Nodes Allocation</span>
6 - <small class="opacity-50">{{ indicesAllocation.length }}</small>
6 + <span class="text-secondary-color font-mono">{{ indicesAllocation.length }}</span>
7 </div>
8 </template>
9 <n-spin :show="loading">
src/components/indices/UnhealthyIndices.vue
+1 -1
@@ -3,7 +3,7 @@
3 <template #header>
4 <div class="flex align-center justify-between">
5 <span>Unhealthy Indices</span>
6 - <small class="opacity-50">{{ unhealthyIndices.length }}</small>
6 + <span class="text-secondary-color font-mono">{{ unhealthyIndices.length }}</span>
7 </div>
8 </template>
9 <n-spin :show="loading">
src/layouts/VerticalNav/SidebarHeader.vue
+1 -1
@@ -104,7 +104,7 @@ const isLight = computed(() => useThemeStore().isThemeLight)
104 width: 100%;
105 .anim-wrap {
106 img {
107 - transform: translateX(22px);
107 + transform: translateX(18px);
108 }
109 }
110 }
src/layouts/common/FooterEL.vue
+7 -1
@@ -7,7 +7,13 @@
7 <BrainIcon />
8 </Icon>
9 By
10 - <a href="https://www.socfortress.co/" target="_blank" alt="D*VERSE" rel="noopener noreferrer" class="mx-1">
10 + <a
11 + href="https://www.socfortress.co/"
12 + target="_blank"
13 + alt="D*VERSE"
14 + rel="noopener noreferrer"
15 + class="mx-1"
16 + >
17 SOCFortress
18 </a>
19 All rights Reserved © Copyright {{ year }}
src/layouts/common/Navbar/items.tsx
+14
@@ -112,6 +112,20 @@ export default function getItems(mode: "vertical" | "horizontal", collapsed: boo
112 }
113 ]
114 },
115 + {
116 + label: () =>
117 + h(
118 + RouterLink,
119 + {
120 + to: {
121 + name: "Alerts"
122 + }
123 + },
124 + { default: () => "Alerts" }
125 + ),
126 + key: "Alerts",
127 + icon: renderIcon(BlankIcon)
128 + },
129 {
130 type: "divider"
131 },
src/router/index.ts
+8
@@ -64,6 +64,14 @@ const router = createRouter({
64 }
65 ]
66 },
67 + {
68 + path: "/alerts",
69 + name: "Alerts",
70 + component: () => import("@/views/socfortress/Alerts.vue"),
71 + meta: { title: "Alerts", auth: true, roles: UserRole.All }
72 + },
73 +
74 + // DEMO PAGES ==========================================================
75
76 {
77 path: "/dashboard",
src/types/alerts.d.ts new
+282
@@ -0,0 +1,282 @@
1 +export interface AlertsByHost {
2 + agent_name: string
3 + number_of_alerts: number
4 +}
5 +
6 +export interface AlertsByRule {
7 + rule: string
8 + number_of_alerts: number
9 +}
10 +
11 +export interface AlertsByRulePerHost {
12 + agent_name: string
13 + number_of_alerts: number
14 + rule: string
15 +}
16 +
17 +export interface AlertsSummary {
18 + index_name: string
19 + total_alerts: number
20 + alerts: Alert[]
21 +}
22 +
23 +type IPAddress = `${number}.${number}.${number}.${number}`
24 +type Timestamp = number
25 +type Latitude = number
26 +type Longitude = number
27 +type Location = `${Latitude},${Longitude}`
28 +
29 +export interface Alert {
30 + _index: string
31 + _id: string
32 + _score: null
33 + _source: AlertSource
34 + sort: Timestamp[]
35 +}
36 +
37 +export interface AlertSource {
38 + agent_id: string
39 + agent_ip_city_name?: string
40 + agent_ip_country_code?: string
41 + agent_ip_geolocation?: AlertSourceAgentIPGeolocation
42 + agent_ip_reserved_ip?: boolean
43 + agent_ip: AlertSourceAgentIP
44 + agent_labels_customer: string
45 + agent_name: string
46 + alert_url?: string
47 + ask_socfortress_message?: string
48 + data_action?: string
49 + data_authors?: string
50 + data_calendarTime?: string
51 + data_columns_cid?: string
52 + data_columns_cmdline?: string
53 + data_columns_cwd?: string
54 + data_columns_duration?: string
55 + data_columns_exit_code?: string
56 + data_columns_gid?: string
57 + data_columns_ntime?: string
58 + data_columns_parent?: string
59 + data_columns_path?: string
60 + data_columns_pid?: string
61 + data_columns_probe_error?: string
62 + data_columns_syscall?: string
63 + data_columns_tid?: string
64 + data_columns_uid?: string
65 + data_counter?: string
66 + data_document?: string
67 + data_epoch?: string
68 + data_event_CreationUtcTime?: string
69 + data_event_Image?: string
70 + data_event_ProcessGuid?: string
71 + data_event_ProcessId?: string
72 + data_event_RuleName?: string
73 + data_event_TargetFilename?: string
74 + data_event_User?: string
75 + data_event_UtcTime?: string
76 + data_falsepositives?: string
77 + data_group?: AlertSourceDataGroup
78 + data_hostIdentifier?: string
79 + data_id?: string
80 + data_kind?: AlertSourceDataKind
81 + data_level?: AlertSourceDataLevelEnum
82 + data_logsource_category?: AlertSourceDataLogsourceCategory
83 + data_logsource_product?: AlertSourceDataLogsourceProduct
84 + data_name?: string
85 + data_numerics?: string
86 + data_path?: string
87 + data_references?: string
88 + data_source?: AlertSourceDataLogsourceProduct
89 + data_status?: AlertSourceDataStatus
90 + data_system_Channel?: string
91 + data_system_Computer?: string
92 + data_system_Correlation?: string | null
93 + data_system_EventID?: string
94 + data_system_EventRecordID?: string
95 + data_system_Execution_attributes_ProcessID?: string
96 + data_system_Execution_attributes_ThreadID?: string
97 + data_system_Keywords?: string
98 + data_system_Level?: string
99 + data_system_Opcode?: string
100 + data_system_Provider_attributes_Guid?: string
101 + data_system_Provider_attributes_Name?: string
102 + data_system_Security_attributes_UserID?: string
103 + data_system_Task?: string
104 + data_system_TimeCreated_attributes_SystemTime?: string
105 + data_system_Version?: string
106 + data_tags?: string
107 + data_timestamp?: string
108 + data_unixTime?: string
109 + data_vulnerability_assigner?: string
110 + data_vulnerability_cve_version?: string
111 + data_vulnerability_cve?: string
112 + data_vulnerability_cvss_cvss3_base_score?: string
113 + data_vulnerability_cvss_cvss3_vector_access_complexity?: string
114 + data_vulnerability_cvss_cvss3_vector_attack_vector?: string
115 + data_vulnerability_cvss_cvss3_vector_availability?: AlertSourceDataLevelEnum
116 + data_vulnerability_cvss_cvss3_vector_confidentiality_impact?: AlertSourceDataLevelEnum
117 + data_vulnerability_cvss_cvss3_vector_integrity_impact?: AlertSourceDataLevelEnum
118 + data_vulnerability_cvss_cvss3_vector_privileges_required?: string
119 + data_vulnerability_cvss_cvss3_vector_scope?: string
120 + data_vulnerability_cvss_cvss3_vector_user_interaction?: string
121 + data_vulnerability_cwe_reference?: string
122 + data_vulnerability_package_architecture?: string
123 + data_vulnerability_package_condition?: string
124 + data_vulnerability_package_name?: string
125 + data_vulnerability_package_version?: string
126 + data_vulnerability_published?: string
127 + data_vulnerability_rationale?: string
128 + data_vulnerability_references?: string
129 + data_vulnerability_severity?: string
130 + data_vulnerability_status?: string
131 + data_vulnerability_title?: string
132 + data_vulnerability_type?: string
133 + data_vulnerability_updated?: string
134 + data_win_eventdata_callTrace?: string
135 + data_win_eventdata_commandLine?: string
136 + data_win_eventdata_company?: string
137 + data_win_eventdata_currentDirectory?: string
138 + data_win_eventdata_description?: string
139 + data_win_eventdata_fileVersion?: string
140 + data_win_eventdata_grantedAccess?: string
141 + data_win_eventdata_hashes?: string
142 + data_win_eventdata_image?: string
143 + data_win_eventdata_integrityLevel?: AlertSourceDataWinEventdataIntegrityLevel
144 + data_win_eventdata_logonGuid?: string
145 + data_win_eventdata_logonId?: string
146 + data_win_eventdata_originalFileName?: string
147 + data_win_eventdata_parentCommandLine?: string
148 + data_win_eventdata_parentImage?: string
149 + data_win_eventdata_parentProcessGuid?: string
150 + data_win_eventdata_parentProcessId?: string
151 + data_win_eventdata_parentUser?: string
152 + data_win_eventdata_processGuid?: string
153 + data_win_eventdata_processId?: string
154 + data_win_eventdata_product?: string
155 + data_win_eventdata_ruleName?: string
156 + data_win_eventdata_sourceImage?: string
157 + data_win_eventdata_sourceProcessGUID?: string
158 + data_win_eventdata_sourceProcessId?: string
159 + data_win_eventdata_sourceThreadId?: string
160 + data_win_eventdata_sourceUser?: string
161 + data_win_eventdata_targetImage?: string
162 + data_win_eventdata_targetProcessGUID?: string
163 + data_win_eventdata_targetProcessId?: string
164 + data_win_eventdata_targetUser?: string
165 + data_win_eventdata_terminalSessionId?: string
166 + data_win_eventdata_user?: string
167 + data_win_eventdata_utcTime?: string
168 + data_win_system_channel?: string
169 + data_win_system_computer?: string
170 + data_win_system_eventID?: string
171 + data_win_system_eventRecordID?: string
172 + data_win_system_keywords?: string
173 + data_win_system_level?: string
174 + data_win_system_message?: string
175 + data_win_system_opcode?: string
176 + data_win_system_processID?: string
177 + data_win_system_providerGuid?: string
178 + data_win_system_providerName?: string
179 + data_win_system_severityValue?: AlertSourceDataWinSystemSeverityValue
180 + data_win_system_systemTime?: string
181 + data_win_system_task?: string
182 + data_win_system_threadID?: string
183 + data_win_system_version?: string
184 + decoder_name: AlertSourceDecoderName
185 + epss_cve?: string
186 + epss_date?: string
187 + epss_epss?: string
188 + epss_percentile?: string
189 + gl2_accounted_message_size: number
190 + gl2_message_id: string
191 + gl2_processing_error: string
192 + gl2_remote_ip: AlertSourceIP
193 + gl2_remote_port: number
194 + gl2_source_input: string
195 + gl2_source_node: string
196 + hash_sha256?: string
197 + id: string
198 + location: string
199 + manager_name: string
200 + message: string
201 + msg_timestamp?: string
202 + parent_process_id?: string
203 + process_cmd_line?: string
204 + process_id?: string
205 + process_image?: string
206 + process_name?: string
207 + rule_description: string
208 + rule_firedtimes: number
209 + rule_gdpr?: string
210 + rule_group1: string
211 + rule_group2?: string
212 + rule_group3?: AlertSourceDataLogsourceProduct
213 + rule_groups: string
214 + rule_id: string
215 + rule_level: number
216 + rule_mail: boolean
217 + rule_mitre_id?: string
218 + rule_mitre_tactic?: string
219 + rule_mitre_technique?: string
220 + rule_pci_dss?: string
221 + rule_tsc?: string
222 + sha256?: string
223 + sigma_name_encoded?: string
224 + source_reserved_ip: boolean
225 + source: AlertSourceIP
226 + streams: string[]
227 + syslog_level: AlertSourceSyslogLevel
228 + syslog_type: AlertSourceSyslogType
229 + timestamp_utc: string
230 + timestamp: string
231 + true: number
232 +}
233 +
234 +export type AlertSourceAgentIP = IPAddress
235 +export type AlertSourceIP = IPAddress
236 +export type AlertSourceAgentIPGeolocation = Location
237 +
238 +export enum AlertSourceDataGroup {
239 + Sigma = "Sigma"
240 +}
241 +
242 +export enum AlertSourceDataKind {
243 + Individual = "individual"
244 +}
245 +
246 +export enum AlertSourceDataLevelEnum {
247 + High = "high"
248 +}
249 +
250 +export enum AlertSourceDataLogsourceCategory {
251 + FileEvent = "file_event"
252 +}
253 +
254 +export enum AlertSourceDataLogsourceProduct {
255 + Sigma = "sigma",
256 + Windows = "windows"
257 +}
258 +
259 +export enum AlertSourceDataStatus {
260 + Experimental = "experimental"
261 +}
262 +
263 +export enum AlertSourceDataWinEventdataIntegrityLevel {
264 + System = "System"
265 +}
266 +
267 +export enum AlertSourceDataWinSystemSeverityValue {
268 + Information = "INFORMATION"
269 +}
270 +
271 +export enum AlertSourceDecoderName {
272 + JSON = "json",
273 + WindowsEventchannel = "windows_eventchannel"
274 +}
275 +
276 +export enum AlertSourceSyslogLevel {
277 + Alert = "ALERT"
278 +}
279 +
280 +export enum AlertSourceSyslogType {
281 + Wazuh = "wazuh"
282 +}
src/views/socfortress/AgentOverview.vue
+7 -4
@@ -35,7 +35,7 @@
35 </h1>
36 <span class="online-badge" v-if="isOnline">ONLINE</span>
37 </div>
38 - <div class="label opacity-60 mt-2">Agent #{{ agent?.agent_id }}</div>
38 + <div class="label text-secondary-color mt-2">Agent #{{ agent?.agent_id }}</div>
39 </n-spin>
40 <n-card class="p-2" content-style="padding:0">
41 <n-spin :show="loadingAgent">
@@ -45,13 +45,15 @@
45 <OverviewSection v-if="agent" :agent="agent" />
46 </div>
47 </n-tab-pane>
48 - <n-tab-pane name="Vulnerabilities" tab="Vulnerabilities" display-directive="show">
48 + <n-tab-pane name="Vulnerabilities" tab="Vulnerabilities" display-directive="show:lazy">
49 <div class="section">
50 <VulnerabilitiesSection v-if="agent" :agent="agent" />
51 </div>
52 </n-tab-pane>
53 - <n-tab-pane name="Alerts" tab="Alerts" display-directive="show">
54 - <div class="section">...yet to be implemented...</div>
53 + <n-tab-pane name="Alerts" tab="Alerts" display-directive="show:lazy">
54 + <div class="section">
55 + <AlertsList v-if="agent" :agent-hostname="agent.hostname" />
56 + </div>
57 </n-tab-pane>
58 </n-tabs>
59 </n-spin>
@@ -67,6 +69,7 @@ import { type Agent } from "@/types/agents.d"
69 import { handleDeleteAgent, isAgentOnline, toggleAgentCritical } from "@/components/agents/utils"
70 import { useRouter } from "vue-router"
71 import VulnerabilitiesSection from "@/components/agents/VulnerabilitiesSection.vue"
72 +import AlertsList from "@/components/alerts/AlertsList.vue"
73 import OverviewSection from "@/components/agents/OverviewSection.vue"
74 import { useMessage, NSpin, NTooltip, NButton, NTabs, NTabPane, NCard, useDialog } from "naive-ui"
75 import Icon from "@/components/common/Icon.vue"
src/views/socfortress/Agents.vue
+21
@@ -168,6 +168,27 @@ onBeforeMount(() => {
168
169 .agents-list {
170 width: 100%;
171 +
172 + .agent-card {
173 + opacity: 0;
174 + animation: agent-card-fade 0.3s forwards;
175 +
176 + @for $i from 0 through 20 {
177 + &:nth-child(#{$i}) {
178 + animation-delay: $i * 0.05s;
179 + }
180 + }
181 +
182 + @keyframes agent-card-fade {
183 + from {
184 + opacity: 0;
185 + transform: translateY(10px);
186 + }
187 + to {
188 + opacity: 1;
189 + }
190 + }
191 + }
192 }
193 }
194 @container (max-width: 770px) {
src/views/socfortress/Alerts.vue new
+11
@@ -0,0 +1,11 @@
1 +<template>
2 + <div class="page">
3 + <AlertsList />
4 + </div>
5 +</template>
6 +
7 +<script setup>
8 +import AlertsList from "@/components/alerts/AlertsList.vue"
9 +</script>
10 +
11 +<style lang="scss" scoped></style>
src/views/socfortress/Indices.vue
+27 -11
@@ -19,16 +19,16 @@
19 </div>
20 </div>
21
22 - <div class="section">
23 - <div class="columns flex column-1200">
22 + <n-card class="section overflow-hidden" content-style="padding:0">
23 + <div class="columns flex column-1200 !gap-0">
24 <div class="col basis-2/5">
25 - <NodeAllocation class="stretchy" />
25 + <NodeAllocation class="stretchy" style="border-radius: 0" :bordered="false" />
26 </div>
27 <div class="col basis-3/5 overflow-hidden">
28 - <TopIndices :indices="indices" />
28 + <TopIndices :indices="indices" style="border-radius: 0" :bordered="false" />
29 </div>
30 </div>
31 - </div>
31 + </n-card>
32 </div>
33 </template>
34
@@ -42,18 +42,26 @@ import ClusterHealth from "@/components/indices/ClusterHealth.vue"
42 import Details from "@/components/indices/Details.vue"
43 import UnhealthyIndices from "@/components/indices/UnhealthyIndices.vue"
44 import TopIndices from "@/components/indices/TopIndices.vue"
45 -import { useMessage } from "naive-ui"
45 +import { useMessage, NCard } from "naive-ui"
46 +import { useRoute } from "vue-router"
47
48 const message = useMessage()
49 +const route = useRoute()
50 const indices = ref<IndexStats[] | null>(null)
51 const loadingIndex = ref(false)
52 const currentIndex = ref<IndexStats | null>(null)
53 +const requestedIndex = ref<string | null>(null)
54
52 -function setIndex(index: IndexStats) {
53 - currentIndex.value = index
55 +function setIndex(index: IndexStats | string) {
56 + if (typeof index === "string") {
57 + const indexStats = indices.value?.find(o => o.index === index) || null
58 + indexStats && (currentIndex.value = indexStats)
59 + } else {
60 + currentIndex.value = index
61 + }
62 }
63
56 -function getIndices() {
64 +function getIndices(cb?: () => void) {
65 loadingIndex.value = true
66
67 Api.indices
@@ -61,6 +69,8 @@ function getIndices() {
69 .then(res => {
70 if (res.data.success) {
71 indices.value = res.data.indices_stats
72 +
73 + if (cb) cb()
74 } else {
75 message.error(res.data?.message || "An error occurred. Please try again later.")
76 }
@@ -72,7 +82,7 @@ function getIndices() {
82 "Wazuh-Indexer returned Unauthorized. Please check your connector credentials."
83 )
84 } else if (err.response?.status === 404) {
75 - message.error(err.response?.data?.message || "No alerts were found.")
85 + message.error(err.response?.data?.message || "No indices were found.")
86 } else {
87 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
88 }
@@ -83,7 +93,13 @@ function getIndices() {
93 }
94
95 onBeforeMount(() => {
86 - getIndices()
96 + if (route.query?.index_name) {
97 + requestedIndex.value = route.query.index_name.toString()
98 + }
99 +
100 + getIndices(() => {
101 + requestedIndex.value && setIndex(requestedIndex.value)
102 + })
103 })
104 </script>
105
src/views/socfortress/graylog/Management.vue
+18 -5
@@ -1,14 +1,14 @@
1 <template>
2 <div class="page">
3 - <n-tabs type="line" animated>
3 + <n-tabs type="line" animated v-model:value="activeTab">
4 <n-tab-pane name="messages" tab="Messages" display-directive="show:lazy">
5 <Messages />
6 </n-tab-pane>
7 <n-tab-pane name="alerts" tab="Alerts" display-directive="show:lazy">
8 - <Alerts />
8 + <Alerts @click-event="gotoEventsPage($event)" />
9 </n-tab-pane>
10 <n-tab-pane name="events" tab="Events" display-directive="show:lazy">
11 - <Events />
11 + <Events :highlight="highlightEvent" />
12 </n-tab-pane>
13 <n-tab-pane name="streams" tab="Streams" display-directive="show:lazy">
14 <Streams />
@@ -18,7 +18,13 @@
18 </template>
19 </n-tabs>
20
21 - <n-drawer v-model:show="showInputDrawer" :width="700" style="max-width: 90vw" :trap-focus="false">
21 + <n-drawer
22 + v-model:show="showInputDrawer"
23 + :width="700"
24 + style="max-width: 90vw"
25 + :trap-focus="false"
26 + display-directive="show"
27 + >
28 <n-drawer-content title="Inputs" closable body-content-style="padding:0">
29 <Inputs />
30 </n-drawer-content>
@@ -26,7 +32,7 @@
32 </div>
33 </template>
34
29 -<script setup>
35 +<script setup lang="ts">
36 import { ref } from "vue"
37 import { NTabs, NTabPane, NButton, NDrawer, NDrawerContent } from "naive-ui"
38 import Messages from "@/components/graylog/Messages/List.vue"
@@ -35,7 +41,14 @@ import Events from "@/components/graylog/Events/List.vue"
41 import Streams from "@/components/graylog/Streams/List.vue"
42 import Inputs from "@/components/graylog/Inputs/List.vue"
43
44 +const activeTab = ref<string | undefined>(undefined)
45 +const highlightEvent = ref<string | undefined>(undefined)
46 const showInputDrawer = ref(false)
47 +
48 +function gotoEventsPage(event_definition_id: string) {
49 + activeTab.value = "events"
50 + highlightEvent.value = event_definition_id
51 +}
52 </script>
53
54 <style lang="scss" scoped></style>
src/views/socfortress/graylog/Pipelines.vue
+2 -3
@@ -52,7 +52,7 @@
52 <n-drawer-content closable body-content-style="padding:0">
53 <template #header>
54 <span>Rules list</span>
55 - <span class="font-mono ml-2 opacity-60" v-if="rulesTotal !== null">{{ rulesTotal }}</span>
55 + <span class="font-mono ml-2 text-secondary-color" v-if="rulesTotal !== null">{{ rulesTotal }}</span>
56 </template>
57 <RulesList @loaded="rulesTotal = $event.total" :highlight="highlightRule" />
58 </n-drawer-content>
@@ -62,7 +62,7 @@
62
63 <script setup lang="ts">
64 import { useMessage, NCollapse, NCollapseItem, NSpin, NButton, NModal, NCard, NDrawer, NDrawerContent } from "naive-ui"
65 -import { onBeforeMount, ref } from "vue"
65 +import { onBeforeMount, ref, watch } from "vue"
66 import type { PipelineFull } from "@/types/graylog/pipelines.d"
67 import Api from "@/api"
68 import Icon from "@/components/common/Icon.vue"
@@ -70,7 +70,6 @@ import PipeDetails from "@/components/graylog/Pipelines/PipeDetails.vue"
70 import PipeInfo from "@/components/graylog/Pipelines/PipeInfo.vue"
71 import PipeTitle from "@/components/graylog/Pipelines/PipeTitle.vue"
72 import RulesList from "@/components/graylog/Pipelines/RulesList.vue"
73 -import { watch } from "vue"
73
74 const RulesIcon = "ic:outline-swipe-right-alt"
75 const InfoIcon = "carbon:information"