@cryptotaxi247 / CoPilot / commits / a8faa660

Precommit fixes (#120)

* Remove unused import statement * more precommit fixes * precommit fixes * added asset tags to alert escalation * env variables update * update integration details * scheduler restructure * precommit eslint

taylor_socfortress committed Jan 28, 2024 at 17:38 UTC a8faa66098fba3c9e05e568b9b2c7392592508a9
65 files changed +350 -582
.devcontainer/devcontainer.json deleted
-34
@@ -1,34 +0,0 @@
1 -{
2 - "name": "Existing Dockerfile",
3 - "build": {
4 - // Sets the run context to one level up instead of the .devcontainer folder.
5 - "context": "..",
6 - // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
7 - "dockerfile": "../Dockerfile.deb"
8 - },
9 - "features": {
10 - "ghcr.io/devcontainers/features/node:1": {},
11 - "ghcr.io/devcontainers/features/python:1": {},
12 - "ghcr.io/devcontainers-contrib/features/npm-package:1": {},
13 - "ghcr.io/devcontainers-contrib/features/pipenv:2": {},
14 - "ghcr.io/akhildevelops/devcontainer-features/pip:0": {}
15 - }
16 -
17 - // For format details, see https://aka.ms/devcontainer.json. For config options, see the
18 - // README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
19 -
20 - // Features to add to the dev container. More info: https://containers.dev/features.
21 - // "features": {},
22 -
23 - // Use 'forwardPorts' to make a list of ports inside the container available locally.
24 - // "forwardPorts": [],
25 -
26 - // Uncomment the next line to run commands after the container is created.
27 - // "postCreateCommand": "cat /etc/os-release",
28 -
29 - // Configure tool-specific properties.
30 - // "customizations": {},
31 -
32 - // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
33 - // "remoteUser": "devcontainer"
34 -}
.env.example
+4 -4
@@ -40,11 +40,11 @@ INFLUXDB_URL=http://example.com
40 INFLUXDB_API_KEY=dummy
41 INFLUXDB_ORG_AND_BUCKET=dummy,dummy
42
43 -ASK_SOCFORTRESS_URL=https://example.com
44 -ASK_SOCFORTRESS_API_KEY=dummy
43 +ASKSOCFORTRESS_URL=https://example.com
44 +ASKSOCFORTRESS_API_KEY=dummy
45
46 -SOCFORTRESS_THREAT_INTEL_URL=https://example.com/search
47 -SOCFORTRESS_THREAT_INTEL_API_KEY=dummy
46 +SOCFORTRESSTHREATINTEL_URL=https://example.com/search
47 +SOCFORTRESSTHREATINTEL_API_KEY=dummy
48
49 CORTEX_URL=http://example.com
50 CORTEX_API_KEY=dummy
.pre-commit-config.yaml
+14 -12
@@ -1,3 +1,4 @@
1 +exclude: "^backend/app/routers/__init__.py$|^backend/app/db/all_models.py$"
2 repos:
3 - repo: https://github.com/pre-commit/pre-commit-hooks
4 rev: v4.4.0
@@ -12,6 +13,7 @@ repos:
13 - id: detect-private-key
14 - id: requirements-txt-fixer
15 args: ["backend/requirements.txt", "backend/requirements.in"]
16 +
17 - repo: https://github.com/PyCQA/isort
18 rev: 5.12.0
19 hooks:
@@ -48,15 +50,15 @@ repos:
50 hooks:
51 - id: prettier
52
51 - # - repo: https://github.com/pre-commit/mirrors-eslint
52 - # rev: v8.41.0
53 - # hooks:
54 - # - id: eslint
55 - # files: \.([cjt]sx?|[cm]ts|[cm]js|cvue)$ # *.js, *.jsx, *.ts, *.tsx and *.vue
56 - # additional_dependencies:
57 - # - "@rushstack/eslint-patch@1.2.0"
58 - # - eslint@8.41.0
59 - # - "@vue/eslint-config-prettier@7.1.0"
60 - # - "@vue/eslint-config-typescript@11.0.2"
61 - # - eslint-plugin-cypress@2.13.3
62 - # - eslint-plugin-vue@9.11.1
53 + - repo: https://github.com/pre-commit/mirrors-eslint
54 + rev: v8.41.0
55 + hooks:
56 + - id: eslint
57 + files: \.([cjt]sx?|[cm]ts|[cm]js|cvue)$ # *.js, *.jsx, *.ts, *.tsx and *.vue
58 + additional_dependencies:
59 + - "@rushstack/eslint-patch@1.2.0"
60 + - eslint@8.41.0
61 + - "@vue/eslint-config-prettier@7.1.0"
62 + - "@vue/eslint-config-typescript@11.0.2"
63 + - eslint-plugin-cypress@2.13.3
64 + - eslint-plugin-vue@9.11.1
backend/app/agents/dfir_iris/services/cases.py
-28
@@ -1,39 +1,11 @@
1 from typing import List
2
3 -from fastapi import HTTPException
3 from loguru import logger
4
5 from app.agents.dfir_iris.schema.cases import AssetCaseIDResponse
6 from app.connectors.dfir_iris.services.assets import get_case_assets
7 from app.connectors.dfir_iris.services.cases import get_all_cases
8
10 -# async def collect_agent_soc_cases(agent_id: int) -> AssetCaseIDResponse:
11 -# """
12 -# Get all cases for the given agent ID.
13 -
14 -# Args:
15 -# agent_id (int): The ID of the agent to get cases for.
16 -
17 -# Returns:
18 -# CaseResponse: An instance of CaseResponse containing the cases for the given agent ID.
19 -
20 -# Raises:
21 -# HTTPException: If the agent does not exist.
22 -# """
23 -# logger.info(f"Getting cases for agent: {agent_id}")
24 -# cases = await get_all_cases()
25 -# for case in cases.cases:
26 -# logger.info(f"Getting assets for case: {case.case_id}")
27 -# assets = await get_case_assets(case.case_id)
28 -# case_ids = []
29 -# for asset in assets.assets:
30 -# if f"agent_id:{agent_id}" in asset.asset_tags:
31 -# logger.info(f"Found case for agent: {agent_id}")
32 -# case_ids.append(case.case_id)
33 -# logger.info(f"Found cases: {case_ids}")
34 -# cases = AssetCaseID(case_ids=case_ids)
35 -# return AssetCaseIDResponse(case_ids=cases, success=True, message="Successfully retrieved cases for agent")
36 -
9
10 async def collect_agent_soc_cases(agent_id: int) -> AssetCaseIDResponse:
11 """
backend/app/agents/routes/agents.py
+1 -2
@@ -25,7 +25,6 @@ from app.agents.wazuh.services.vulnerabilities import collect_agent_vulnerabilit
25 # App specific imports
26 from app.auth.routes.auth import AuthHandler
27 from app.db.db_session import get_db
28 -from app.db.db_session import get_session
28
29 # App specific imports
30 # from app.db.db_session import session
@@ -137,7 +136,7 @@ async def get_agent(agent_id: str, db: AsyncSession = Depends(get_db)) -> Agents
136 else:
137 raise HTTPException(status_code=404, detail=f"Agent with agent_id {agent_id} not found")
138 except Exception as e:
140 - logger.error(f"Failed to fetch agent: {agent_id}. Does it exist?")
139 + logger.error(f"Failed to fetch agent: {agent_id} with error {e}. Does it exist?")
140 raise HTTPException(status_code=500, detail=f"Failed to fetch agent: {agent_id}. Does it exist?")
141
142
backend/app/agents/wazuh/services/agents.py
+1 -1
@@ -18,7 +18,7 @@ async def collect_wazuh_agents() -> WazuhAgentsList:
18 logger.info("Collecting all agents from Wazuh Manager")
19 agents_collected = await send_get_request(endpoint="/agents", params={"limit": 1000})
20
21 - if agents_collected.get("success") == False:
21 + if agents_collected.get("success") is False:
22 raise HTTPException(
23 status_code=500,
24 detail=agents_collected.get("message", "Unknown error"),
backend/app/agents/wazuh/services/vulnerabilities.py
-3
@@ -34,9 +34,6 @@ async def collect_agent_vulnerabilities(agent_id: str):
34 )
35
36
37 -from typing import List
38 -
39 -
37 def process_agent_vulnerabilities(agent_vulnerabilities: dict) -> List[WazuhAgentVulnerabilities]:
38 """
39 Process agent vulnerabilities and return a list of WazuhAgentVulnerabilities objects.
backend/app/auth/models/users.py
-1
@@ -6,7 +6,6 @@ from enum import Enum
6 from typing import Optional
7
8 import bcrypt
9 -from fastapi import HTTPException
9 from pydantic import BaseModel
10 from pydantic import EmailStr
11 from pydantic import validator
backend/app/auth/routes/auth.py
-1
@@ -8,7 +8,6 @@ from fastapi import status
8 from fastapi.security import OAuth2PasswordRequestForm
9 from loguru import logger
10 from sqlalchemy.ext.asyncio import AsyncSession
11 -from starlette.status import HTTP_401_UNAUTHORIZED
11
12 from app.auth.models.users import PasswordReset
13 from app.auth.models.users import PasswordResetToken
backend/app/auth/services/universal.py
-1
@@ -1,4 +1,3 @@
1 -from fastapi import HTTPException
1 from loguru import logger
2
3 # ! New with Async
backend/app/auth/utils.py
+1 -7
@@ -99,7 +99,7 @@ class AuthHandler:
99 """
100 user = await find_user(username)
101 if not user or not self.verify_password(password, user.password):
102 - logger.info(f"Password is not verified")
102 + logger.info("Password is not verified")
103 return False
104 return user
105
@@ -170,12 +170,6 @@ class AuthHandler:
170 else:
171 authenticate_value = "Bearer"
172
173 - credentials_exception = HTTPException(
174 - status_code=401,
175 - detail="Could not validate credentials",
176 - headers={"WWW-Authenticate": authenticate_value},
177 - )
178 -
173 try:
174 username, token_scopes = self.decode_token(token)
175 if username == "Expired signature":
backend/app/connectors/dfir_iris/routes/alerts.py
+1 -1
@@ -222,7 +222,7 @@ async def purge_alerts_route() -> DeleteAlertResponse:
222 Returns:
223 AlertResponse: The response containing the deleted alerts.
224 """
225 - logger.info(f"Purging all alerts, up to 1000")
225 + logger.info("Purging all alerts, up to 1000")
226 alerts = (await get_alerts(request=FilterAlertsRequest(per_page=1000))).alerts
227 for alert in alerts:
228 await delete_alert(int(alert["alert_id"]))
backend/app/connectors/dfir_iris/services/cases.py
+1 -1
@@ -258,5 +258,5 @@ async def delete_single_case(case_id: SingleCaseBody) -> PurgeCaseResponse:
258 """
259 dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
260 case = Case(session=dfir_iris_client)
261 - result = await fetch_and_parse_data(dfir_iris_client, case.delete_case, case_id)
261 + await fetch_and_parse_data(dfir_iris_client, case.delete_case, case_id)
262 return PurgeCaseResponse(success=True, message="Successfully deleted single case")
backend/app/connectors/dfir_iris/utils/universal.py
+2 -1
@@ -266,9 +266,10 @@ async def check_alert_exists(alert_id: str) -> bool:
266 try:
267 dfir_iris_client = await create_dfir_iris_client("DFIR-IRIS")
268 except Exception as e:
269 + logger.error(f"Failed to create DFIR-IRIS client: {e}")
270 raise HTTPException(
271 status_code=500,
271 - detail=f"Failed to create DFIR-IRIS client. Make sure the DFIR-IRIS connector is configured correctly.",
272 + detail="Failed to create DFIR-IRIS client. Make sure the DFIR-IRIS connector is configured correctly.",
273 )
274 try:
275 logger.info(f"Checking if alert {alert_id} exists")
backend/app/connectors/event_shipper/utils/universal.py
-11
@@ -1,10 +1,8 @@
1 from typing import Optional
2
3 import asyncgelf
4 -from loguru import logger
4
5 from app.connectors.utils import get_connector_info_from_db
7 -from app.db.db_session import AsyncSessionLocal
6 from app.db.db_session import get_db_session
7
8
@@ -14,15 +12,6 @@ class GelfLogger:
12 self.port = port
13 self.compress = compress
14
17 - # async def tcp_handler(self, message):
18 - # handler = asyncgelf.GelfTcp(
19 - # host=self.host,
20 - # port=self.port,
21 - # compress=self.compress,
22 - # )
23 -
24 - # response = await handler.tcp_handler(message)
25 - # return response
15 async def tcp_handler(self, message):
16 if not isinstance(message, dict):
17 message = message.to_dict()
backend/app/connectors/grafana/schema/dashboards.py
-1
@@ -1,6 +1,5 @@
1 from enum import Enum
2 from typing import List
3 -from typing import Union
3
4 from pydantic import BaseModel
5 from pydantic import Field
backend/app/connectors/grafana/schema/organization.py
-2
@@ -1,5 +1,3 @@
1 -from enum import Enum
2 -
1 from pydantic import BaseModel
2
3
backend/app/connectors/influxdb/schema/alerts.py
-1
@@ -1,5 +1,4 @@
1 from datetime import datetime
2 -from typing import Optional
2
3 from pydantic import BaseModel
4
backend/app/connectors/influxdb/utils/universal.py
+1 -1
@@ -85,5 +85,5 @@ async def get_influxdb_organization() -> str:
85 async with get_db_session() as session: # This will correctly enter the context manager
86 attributes = await get_connector_info_from_db("InfluxDB", session)
87 if attributes is None:
88 - raise HTTPException(status_code=500, detail=f"No InfluxDB connector found in the database")
88 + raise HTTPException(status_code=500, detail="No InfluxDB connector found in the database")
89 return attributes["connector_extra_data"].split(",")[0]
backend/app/connectors/services.py
+2 -5
@@ -11,7 +11,6 @@ from loguru import logger
11 from pydantic import BaseModel
12 from sqlalchemy.ext.asyncio import AsyncSession
13 from sqlalchemy.future import select
14 -from sqlmodel import select
14 from werkzeug.utils import secure_filename
15
16 from app.connectors.cortex.utils.universal import verify_cortex_connection
@@ -26,9 +25,6 @@ from app.connectors.sublime.utils.universal import verify_sublime_connection
25 from app.connectors.velociraptor.utils.universal import verify_velociraptor_connection
26 from app.connectors.wazuh_indexer.utils.universal import verify_wazuh_indexer_connection
27 from app.connectors.wazuh_manager.utils.universal import verify_wazuh_manager_connection
29 -
30 -# from app.db.db_session import engine # Import the shared engine
31 -from app.db.db_session import get_session
28 from app.integrations.ask_socfortress.services.ask_socfortress import (
29 verify_ask_socfortress_connector,
30 )
@@ -340,7 +336,8 @@ class ConnectorServices:
336 session (AsyncSession): The async session for interacting with the database.
337
338 Returns:
343 - Union[ConnectorResponse, bool]: Returns a ConnectorResponse object if the file is saved and the connector record is updated successfully. Otherwise, returns False.
339 + Union[ConnectorResponse, bool]: Returns a ConnectorResponse object if the file is saved and the connector record is updated successfully.
340 + Otherwise, returns False.
341 """
342 if file and cls.allowed_file(file.filename):
343 filename = secure_filename(file.filename)
backend/app/connectors/utils.py
-1
@@ -5,7 +5,6 @@ from typing import Optional
5 from loguru import logger
6 from sqlalchemy.ext.asyncio import AsyncSession
7 from sqlalchemy.future import select
8 -from sqlmodel import select
8
9 from app.connectors.models import Connectors
10 from app.connectors.schema import ConnectorResponse
backend/app/connectors/velociraptor/routes/artifacts.py
-1
@@ -32,7 +32,6 @@ velociraptor_artifacts_router = APIRouter()
32
33
34 # Get all valid OS prefixes
35 -from typing import List
35
36
37 def get_valid_os_prefixes() -> List[str]:
backend/app/connectors/velociraptor/routes/flows.py
-14
@@ -1,5 +1,3 @@
1 -from typing import List
2 -
1 from fastapi import APIRouter
2 from fastapi import Depends
3 from fastapi import HTTPException
@@ -9,21 +7,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
7 from sqlalchemy.future import select
8
9 from app.auth.utils import AuthHandler
12 -from app.connectors.velociraptor.schema.artifacts import ArtifactsResponse
13 -from app.connectors.velociraptor.schema.artifacts import CollectArtifactBody
10 from app.connectors.velociraptor.schema.artifacts import CollectArtifactResponse
15 -from app.connectors.velociraptor.schema.artifacts import OSPrefixEnum
16 -from app.connectors.velociraptor.schema.artifacts import OSPrefixModel
17 -from app.connectors.velociraptor.schema.artifacts import QuarantineBody
18 -from app.connectors.velociraptor.schema.artifacts import QuarantineResponse
19 -from app.connectors.velociraptor.schema.artifacts import RunCommandBody
20 -from app.connectors.velociraptor.schema.artifacts import RunCommandResponse
11 from app.connectors.velociraptor.schema.flows import FlowResponse
12 from app.connectors.velociraptor.schema.flows import RetrieveFlowRequest
23 -from app.connectors.velociraptor.services.artifacts import get_artifacts
24 -from app.connectors.velociraptor.services.artifacts import quarantine_host
25 -from app.connectors.velociraptor.services.artifacts import run_artifact_collection
26 -from app.connectors.velociraptor.services.artifacts import run_remote_command
13 from app.connectors.velociraptor.services.flows import get_flow
14 from app.connectors.velociraptor.services.flows import get_flows
15 from app.db.db_session import get_db
backend/app/connectors/velociraptor/services/artifacts.py
+34 -8
@@ -42,13 +42,25 @@ def get_artifact_key(analyzer_body: CollectArtifactBody) -> str:
42 command = getattr(analyzer_body, "command", None)
43
44 if action == "quarantine":
45 - return f'collect_client(client_id="{analyzer_body.velociraptor_id}", artifacts=["{analyzer_body.artifact_name}"], spec=dict(`{analyzer_body.artifact_name}`=dict()))'
45 + return (
46 + f'collect_client(client_id="{analyzer_body.velociraptor_id}", '
47 + f'artifacts=["{analyzer_body.artifact_name}"], '
48 + f"spec=dict(`{analyzer_body.artifact_name}`=dict()))"
49 + )
50 elif action == "remove_quarantine":
47 - return f'collect_client(client_id="{analyzer_body.velociraptor_id}", artifacts=["{analyzer_body.artifact_name}"], spec=dict(`{analyzer_body.artifact_name}`=dict(`RemovePolicy`="Y")))'
51 + return (
52 + f'collect_client(client_id="{analyzer_body.velociraptor_id}", '
53 + f'artifacts=["{analyzer_body.artifact_name}"], '
54 + f'spec=dict(`{analyzer_body.artifact_name}`=dict(`RemovePolicy`="Y")))'
55 + )
56 elif command is not None:
49 - return f"collect_client(client_id='{analyzer_body.velociraptor_id}', urgent=true, artifacts=['{analyzer_body.artifact_name}'], env=dict(Command='{analyzer_body.command}'))"
57 + return (
58 + f"collect_client(client_id='{analyzer_body.velociraptor_id}', "
59 + f"urgent=true, artifacts=['{analyzer_body.artifact_name}'], "
60 + f"env=dict(Command='{analyzer_body.command}'))"
61 + )
62 else:
51 - return f"collect_client(client_id='{analyzer_body.velociraptor_id}', artifacts=['{analyzer_body.artifact_name}'])"
63 + return f"collect_client(client_id='{analyzer_body.velociraptor_id}', " f"artifacts=['{analyzer_body.artifact_name}'])"
64
65
66 async def get_artifacts() -> ArtifactsResponse:
@@ -131,8 +143,12 @@ async def run_remote_command(run_command_body: RunCommandBody) -> RunCommandResp
143 run_command_body.artifact_name = run_command_body.artifact_name.value
144 logger.info(f"Running remote command on {run_command_body}")
145 query = create_query(
134 - 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}')) "
135 - "FROM scope()",
146 + (
147 + f"SELECT collect_client(client_id='{run_command_body.velociraptor_id}', "
148 + f"urgent=true, artifacts=['{run_command_body.artifact_name}'], "
149 + f"env=dict(Command='{run_command_body.command}')) "
150 + "FROM scope()"
151 + ),
152 )
153 flow = velociraptor_service.execute_query(query)
154 logger.info(f"Successfully ran artifact collection on {flow}")
@@ -175,11 +191,21 @@ async def quarantine_host(quarantine_body: QuarantineBody) -> QuarantineResponse
191 quarantine_body.action = quarantine_body.action.value
192 if quarantine_body.action == "quarantine":
193 query = create_query(
178 - f'SELECT collect_client(client_id="{quarantine_body.velociraptor_id}", artifacts=["{quarantine_body.artifact_name}"], spec=dict(`{quarantine_body.artifact_name}`=dict())) FROM scope()',
194 + (
195 + f'SELECT collect_client(client_id="{quarantine_body.velociraptor_id}", '
196 + f'artifacts=["{quarantine_body.artifact_name}"], '
197 + f"spec=dict(`{quarantine_body.artifact_name}`=dict())) "
198 + "FROM scope()"
199 + ),
200 )
201 else:
202 query = create_query(
182 - 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()',
203 + (
204 + f'SELECT collect_client(client_id="{quarantine_body.velociraptor_id}", '
205 + f'artifacts=["{quarantine_body.artifact_name}"], '
206 + f'spec=dict(`{quarantine_body.artifact_name}`=dict(`RemovePolicy`="Y"))) '
207 + "FROM scope()"
208 + ),
209 )
210 flow = velociraptor_service.execute_query(query)
211 logger.info(f"Successfully ran artifact collection on {flow}")
backend/app/connectors/velociraptor/services/flows.py
-7
@@ -1,14 +1,7 @@
1 from fastapi import HTTPException
2 from loguru import logger
3
4 -from app.connectors.velociraptor.schema.artifacts import Artifacts
5 -from app.connectors.velociraptor.schema.artifacts import ArtifactsResponse
6 -from app.connectors.velociraptor.schema.artifacts import CollectArtifactBody
4 from app.connectors.velociraptor.schema.artifacts import CollectArtifactResponse
8 -from app.connectors.velociraptor.schema.artifacts import QuarantineBody
9 -from app.connectors.velociraptor.schema.artifacts import QuarantineResponse
10 -from app.connectors.velociraptor.schema.artifacts import RunCommandBody
11 -from app.connectors.velociraptor.schema.artifacts import RunCommandResponse
5 from app.connectors.velociraptor.schema.flows import FlowClientSession
6 from app.connectors.velociraptor.schema.flows import FlowResponse
7 from app.connectors.velociraptor.schema.flows import RetrieveFlowRequest
backend/app/connectors/wazuh_manager/routes/rules.py
+6 -16
@@ -14,34 +14,24 @@ from app.connectors.wazuh_manager.schema.rules import RuleDisable
14 from app.connectors.wazuh_manager.schema.rules import RuleDisableResponse
15 from app.connectors.wazuh_manager.schema.rules import RuleEnable
16 from app.connectors.wazuh_manager.schema.rules import RuleEnableResponse
17 -from app.connectors.wazuh_manager.schema.rules import RuleExclude
18 -from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
17
18 # from app.connectors.wazuh_manager.schema.rules import RuleExclude
19 # from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
20 from app.connectors.wazuh_manager.services.rules import disable_rule
21 from app.connectors.wazuh_manager.services.rules import enable_rule
24 -from app.connectors.wazuh_manager.services.rules import exclude_rule
22
23 # from app.connectors.wazuh_manager.services.rules import exclude_rule
24 from app.db.db_session import get_db
25
29 -NEW_LEVEL = "1"
30 -wazuh_manager_rules_router = APIRouter()
31 -auth_handler = AuthHandler()
32 -
26 +# from app.connectors.wazuh_manager.schema.rules import RuleExclude
27 +# from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
28
34 -def query_disabled_rule(rule_id: str):
35 - """
36 - Query a disabled rule by its ID.
29 +# from app.connectors.wazuh_manager.services.rules import exclude_rule
30
38 - Args:
39 - rule_id (str): The ID of the rule.
31
41 - Returns:
42 - DisabledRule: The disabled rule object if found, None otherwise.
43 - """
44 - return session.query(DisabledRule).filter(DisabledRule.rule_id == rule_id).first()
32 +NEW_LEVEL = "1"
33 +wazuh_manager_rules_router = APIRouter()
34 +auth_handler = AuthHandler()
35
36
37 @wazuh_manager_rules_router.get(
backend/app/connectors/wazuh_manager/schema/rules.py
-1
@@ -3,7 +3,6 @@ from typing import Optional
3
4 from pydantic import BaseModel
5 from pydantic import Field
6 -from pydantic import validator
6
7
8 class RuleDisable(BaseModel):
backend/app/connectors/wazuh_manager/services/rules.py
+1 -1
@@ -6,7 +6,7 @@ from typing import List
6 from typing import Tuple
7 from typing import Union
8
9 -import pcre2
9 +# import pcre2
10 import xmltodict
11 from fastapi import HTTPException
12 from loguru import logger
backend/app/customer_provisioning/routes/provision.py
-2
@@ -1,5 +1,3 @@
1 -from typing import List
2 -
1 from fastapi import APIRouter
2 from fastapi import Body
3 from fastapi import Depends
backend/app/customer_provisioning/schema/graylog.py
+1 -1
@@ -5,7 +5,7 @@ from pydantic import BaseModel
5 from pydantic import Field
6
7
8 -#! INDEX SETS !#
8 +# ! INDEX SETS ! #
9 class TimeBasedRotationStrategyConfig(BaseModel):
10 type: str
11 rotation_period: Optional[str] = None
backend/app/customer_provisioning/services/grafana.py
+4 -3
@@ -99,7 +99,7 @@ async def create_grafana_folder(organization_id: int, folder_title: str) -> Graf
99 Returns:
100 GrafanaFolderCreationResponse: The response object containing the details of the created folder.
101 """
102 - logger.info(f"Creating Grafana folder")
102 + logger.info("Creating Grafana folder")
103 grafana_client = await create_grafana_client("Grafana")
104 # Switch to the newly created organization
105 grafana_client.user.switch_actual_user_organisation(organization_id)
@@ -132,7 +132,7 @@ async def get_opensearch_version() -> str:
132 return node_info.version
133
134 # If no version is found, raise an exception
135 - raise HTTPException(status_code=500, detail=f"Failed to retrieve OpenSearch version.")
135 + raise HTTPException(status_code=500, detail="Failed to retrieve OpenSearch version.")
136
137
138 ################# ! GRAFANA DECOMISSIONING ! #################
@@ -143,13 +143,14 @@ async def delete_grafana_organization(organization_id: int):
143 Args:
144 organization_id (int): The ID of the organization to delete.
145 """
146 - logger.info(f"Deleting Grafana organization")
146 + logger.info("Deleting Grafana organization")
147 grafana_client = await create_grafana_client("Grafana")
148 try:
149 organization_deleted = grafana_client.organizations.delete_organization(organization_id=organization_id)
150 logger.info(f"Organization deleted: {organization_deleted}")
151 except Exception as e:
152 # Switch the organization to the default and try again
153 + logger.info(f"Failed to delete organization: {e}. Switching to default organization and trying again.")
154 grafana_client.user.switch_actual_user_organisation(1)
155 organization_deleted = grafana_client.organizations.delete_organization(organization_id=organization_id)
156 logger.info(f"Organization deleted: {organization_deleted}")
backend/app/customer_provisioning/services/provision.py
+1 -1
@@ -198,4 +198,4 @@ async def provision_wazuh_worker(request: ProvisionWorkerRequest, session: Async
198 if response.status_code != 200:
199 return ProvisionWorkerResponse(success=False, message=f"Failed to provision Wazuh worker: {response.text}")
200 # Return the response
201 - return ProvisionWorkerResponse(success=True, message=f"Wazuh worker provisioned successfully")
201 + return ProvisionWorkerResponse(success=True, message="Wazuh worker provisioned successfully")
backend/app/db/db_populate.py
+13 -3
@@ -77,7 +77,11 @@ def get_connectors_list():
77 "Wazuh Worker Provisioning",
78 "3",
79 "host_only",
80 - "Connection to Wazuh Worker Provisioning. Make sure you have deployed the Wazuh Worker Provisioning Application provided by SOCFortress: https://github.com/socfortress/Customer-Provisioning-Worker",
80 + (
81 + "Connection to Wazuh Worker Provisioning. Make sure you have "
82 + "deployed the Wazuh Worker Provisioning Application provided by "
83 + "SOCFortress: https://github.com/socfortress/Customer-Provisioning-Worker"
84 + ),
85 ),
86 (
87 "Event Shipper",
@@ -90,7 +94,11 @@ def get_connectors_list():
94 "Alert Creation Provisioning",
95 "3",
96 "host_only",
93 - "Connection to Alert Creation Provisioning. Make sure you have deployed the Alert Creation Provisioning Application provided by SOCFortress: https://github.com/socfortress/Customer-Provisioning-Alert",
97 + (
98 + "Connection to Alert Creation Provisioning. Make sure you have "
99 + "deployed the Alert Creation Provisioning Application provided by "
100 + "SOCFortress: https://github.com/socfortress/Customer-Provisioning-Alert"
101 + ),
102 ),
103 # ... Add more connectors as needed ...
104 ]
@@ -326,5 +334,7 @@ async def add_available_integrations_auth_keys_if_not_exist(session: AsyncSessio
334 new_auth_key = AvailableIntegrationsAuthKeys(**available_integration_auth_keys_data)
335 session.add(new_auth_key)
336 logger.info(
329 - f"Added new available integration auth keys: {available_integration_auth_keys_data['auth_key_name']} for {available_integration_auth_keys_data['integration_name']}",
337 + f"Added new available integration auth keys: "
338 + f"{available_integration_auth_keys_data['auth_key_name']} for "
339 + f"{available_integration_auth_keys_data['integration_name']}",
340 )
backend/app/db/db_session.py
-4
@@ -1,7 +1,4 @@
1 # ! Old Testing without Async
2 -from contextlib import asynccontextmanager
3 -from contextlib import contextmanager
4 -
2 from sqlmodel import Session
3 from sqlmodel import create_engine
4
@@ -18,7 +15,6 @@ from sqlalchemy import create_engine
15 from sqlalchemy.ext.asyncio import AsyncSession
16 from sqlalchemy.ext.asyncio import create_async_engine
17 from sqlalchemy.orm import sessionmaker
21 -from sqlmodel import Session
18
19 from settings import SQLALCHEMY_DATABASE_URI
20
backend/app/integrations/alert_creation/general/services/alert.py
-1
@@ -107,7 +107,6 @@ async def build_asset_payload(agent_data: AgentsResponse, alert_details) -> Iris
107 asset_ip=agent_data.agents[0].ip_address,
108 asset_description=agent_data.agents[0].os,
109 asset_type_id=await get_asset_type_id(agent_data.agents[0].os),
110 - # asset_tags=agent_data.agents[0].agent_id,
110 asset_tags=f"agent_id:{agent_data.agents[0].agent_id}",
111 )
112 return IrisAsset()
backend/app/integrations/alert_creation/office365/routes/alert.py
-1
@@ -34,7 +34,6 @@ from app.integrations.alert_creation.office365.services.threat_intel import (
34 from app.integrations.alert_creation_settings.models.alert_creation_settings import (
35 AlertCreationSettings,
36 )
37 -from app.utils import get_customer_alert_settings_office365
37
38 office365_alerts_router = APIRouter()
39
backend/app/integrations/alert_creation/office365/services/exchange.py
-1
@@ -20,7 +20,6 @@ from app.integrations.alert_creation.office365.schema.exchange import (
20 from app.integrations.utils.alerts import send_to_shuffle
21 from app.integrations.utils.alerts import validate_ioc_type
22 from app.integrations.utils.schema import ShufflePayload
23 -from app.utils import get_customer_alert_settings
23 from app.utils import get_customer_alert_settings_office365
24
25
backend/app/integrations/alert_creation/office365/services/threat_intel.py
-4
@@ -15,9 +15,6 @@ from app.integrations.alert_creation.office365.schema.threat_intel import (
15 )
16 from app.integrations.alert_creation.office365.schema.threat_intel import IrisAsset
17 from app.integrations.alert_creation.office365.schema.threat_intel import IrisIoc
18 -from app.integrations.alert_creation.office365.schema.threat_intel import (
19 - Office365ThreatIntelAlertBase,
20 -)
18 from app.integrations.alert_creation.office365.schema.threat_intel import (
19 Office365ThreatIntelAlertRequest,
20 )
@@ -27,7 +24,6 @@ from app.integrations.alert_creation.office365.schema.threat_intel import (
24 from app.integrations.utils.alerts import send_to_shuffle
25 from app.integrations.utils.alerts import validate_ioc_type
26 from app.integrations.utils.schema import ShufflePayload
30 -from app.utils import get_customer_alert_settings
27 from app.utils import get_customer_alert_settings_office365
28
29
backend/app/integrations/alert_escalation/schema/general_alert.py
+21 -2
@@ -82,14 +82,33 @@ class IrisAsset(BaseModel):
82 asset_ip: str = Field(..., description="IP address of the asset", example="192.168.1.1")
83 asset_description: str = Field(..., description="Description of the asset", example="Windows Server")
84 asset_type_id: int = Field(..., description="Type ID of the asset", example=1)
85 + asset_tags: Optional[str] = Field(
86 + "Agent ID not found. Ensure the agent has been registered with Wazuh Manager and synced to the Agents table.",
87 + description="Tags of the asset",
88 + example="001",
89 + )
90 +
91 + def to_dict(self):
92 + return self.dict(exclude_none=True)
93
94
95 class IrisIoc(BaseModel):
88 - ioc_value: str = Field(..., description="Value of the IoC", example="www.google.com")
89 - ioc_description: str = Field(..., description="Description of the IoC", example="Google")
96 + ioc_value: str = Field(
97 + ...,
98 + description="Value of the IoC",
99 + example="www.google.com",
100 + )
101 + ioc_description: str = Field(
102 + ...,
103 + description="Description of the IoC",
104 + example="Google",
105 + )
106 ioc_tlp_id: int = Field(1, description="TLP ID of the IoC", example=1)
107 ioc_type_id: int = Field(20, description="Type ID of the IoC", example=20)
108
109 + def to_dict(self):
110 + return self.dict(exclude_none=True)
111 +
112
113 class IrisAlertContext(BaseModel):
114 alert_id: str = Field(..., description="ID of the alert", example="123")
backend/app/integrations/alert_escalation/services/general_alert.py
+18
@@ -156,6 +156,7 @@ async def build_asset_payload(agent_data: AgentsResponse, alert_details) -> Iris
156 asset_ip=agent_data.agents[0].ip_address,
157 asset_description=agent_data.agents[0].os,
158 asset_type_id=await get_asset_type_id(agent_data.agents[0].os),
159 + asset_tags=f"agent_id:{agent_data.agents[0].agent_id}",
160 )
161 return IrisAsset()
162
@@ -354,6 +355,23 @@ async def create_alert(alert: CreateAlertRequest, session: AsyncSession) -> Crea
355 )
356 client, alert_client = await initialize_client_and_alert("DFIR-IRIS")
357 result = await fetch_and_validate_data(client, alert_client.add_alert, iris_alert_payload.to_dict())
358 + alert_id = result["data"]["alert_id"]
359 + logger.info(f"Successfully created alert {alert_id} in IRIS.")
360 + # Update the alert with the asset payload
361 + await fetch_and_validate_data(
362 + client,
363 + alert_client.update_alert,
364 + alert_id,
365 + {"assets": [dict(IrisAsset(**iris_alert_payload.assets[0].to_dict()))]},
366 + )
367 + # Updae the alert if the ioc_payload is not None
368 + if ioc_payload:
369 + await fetch_and_validate_data(
370 + client,
371 + alert_client.update_alert,
372 + alert_id,
373 + {"iocs": [dict(IrisIoc(**iris_alert_payload.alert_iocs[0].to_dict()))]},
374 + )
375 es_client = await create_wazuh_indexer_client("Wazuh-Indexer")
376 iris_url = await add_alert_to_document(es_client, alert, result["data"]["alert_id"], session=session)
377 try:
backend/app/integrations/log_shipper_test/routes/event_shipper.py deleted
-29
@@ -1,29 +0,0 @@
1 -import json
2 -
3 -from fastapi import APIRouter
4 -from fastapi import Depends
5 -from fastapi import HTTPException
6 -from loguru import logger
7 -
8 -from app.integrations.utils.event_shipper import event_shipper
9 -from app.integrations.utils.schema import EventShipperPayload
10 -
11 -log_shipper_test_router = APIRouter()
12 -
13 -
14 -@log_shipper_test_router.get("")
15 -async def event_shipper_test_route():
16 - """
17 - Test the log shipper.
18 - """
19 - message = EventShipperPayload(
20 - customer_code="test",
21 - integration="test",
22 - version="1.1",
23 - host="example.org",
24 - )
25 - try:
26 - return await event_shipper(message)
27 - except Exception as e:
28 - logger.error(f"Failed to send test message to log shipper: {e}")
29 - raise HTTPException(status_code=500, detail=f"Failed to send test message to log shipper: {e}")
backend/app/integrations/markdown/mimecast.md
+84 -118
@@ -1,120 +1,86 @@
1 # [Mimecast](https://integrations.mimecast.com/documentation/api-overview/authentication-scripts-server-apps/)
2
3 -When developing a script of server application integration you will:
4 -
5 -- Use a single user that has the Mimecast administrator permissions to perform the actions required by your use case.
6 -- Update the Authentication Cache TTL setting in the service user's effective Authentication Profile to "Never Expire."
7 -
8 -This page provides a step-by-step guide to prepare a user for your integration and get the access key and secret key values required to authorize all requests to the API.
9 -
10 -### Step 1: Create a New User
11 -
12 -1. Login to the Administration Console.
13 -2. Navigate to the Administration | Directories | Internal Directories menu item to display a list of internal domains.
14 -3. Select the internal domain where you would like to create your new user.
15 -4. Select the New Address button from the menu bar.
16 -5. Complete the new address form and select Save and Exit to create the new user.
17 -6. Keep a note of the password set as you will use this to get your Authentication Token in Step 6.
18 -
19 -### Step 2: Add the User to an Administrative Role
20 -
21 -1. While logged into the Administration Console, navigate to the Administration | Account | Roles menu item to display the Roles page.
22 -2. Right-click the Basic Administrator role and select Add users to role.
23 -3. Browse or search to find the new user created in Step 1.
24 -4. Select the tick box to the left of the user.
25 -5. Select the Add selected users button to add the user to the role.
26 -
27 -### Step 3: Create a New Group and Add Your New User
28 -
29 -1. While logged into the Administration Console, navigate to the Administration | Directories | Profile Groups menu item to display the Profile groups page.
30 -2. Create a new group by selecting the plus icon on the parent folder where you would like to create the group. This creates a new group with the Name "New Folder"
31 -3. To rename the group, select the newly created "New Folder" group. Then from the Edit group text box type the name you want to give the folder, for example, Splunk Admin and press the Enter key to apply the change.
32 -4. With the group selected select the Build drop-down button and select Add Email Addresses.
33 -5. Type the name of the new user created in Step 1.
34 -6. Select Save and Exit to add the new user to the group.
35 -
36 -### Step 4: Create a New Authentication Profile
37 -
38 -1. While logged into the Administration Console, navigate to the Administration | Services | Applications menu item to display the Application Settings page.
39 -2. Select the Authentication Profiles button.
40 -3. Select the New Authentication Profile button.
41 -4. Type a Description for the new profile.
42 -5. Set the Authentication TTL setting to Never Expires. This will make sure that when you create your Authentication Token it will not expire and impact the data collection of the app.
43 -6. Leave all other settings as their default.
44 -7. Select Save and Exit to create the profile.
45 -
46 -### Step 5: Create a New Application Setting
47 -
48 -1. While logged into the Administration Console, navigate to the Administration | Services | Applications menu item to display the Application Settings page.
49 -2. Select the New Application Settings button.
50 -3. Type a Description.
51 -4. Use the Group Lookup button to select the Group that you created in Step 3.
52 -5. Use the Authentication Profile Lookup button to select the Authentication Profile created in Step 4.
53 -6. Leave all other settings as their default.
54 -7. Select Save and Exit to create and apply the Application Settings to your new group and user.
55 -
56 -### Step 6: Get Your Authentication Token
57 -
58 -Now that you have a dedicated user who will receive an Authentication Token that will never expire, the final preparation task is to get the Authentication Token for the user.
59 -
60 -#### Get an Authentication Token Using Windows
61 -
62 -NOTE: This process has been tested in Powershell version 4 and 5.
63 -
64 -Copy paste the following script into a Powershell window:
65 -
66 -```powershell
67 -$appId = Read-Host -Prompt 'Input your registered application id'
68 -
69 -$creds = Get-Credential
70 -
71 -$discoverPostBody = @{"data" = ,@{"emailAddress" = $creds.UserName}}
72 -
73 -$discoverPostBodyJson = ConvertTo-Json $discoverPostBody
74 -
75 -$discoverRequestId = [GUID]::NewGuid().guid
76 -
77 -$discoverRequestHeaders = @{"x-mc-app-id" = $appId; "x-mc-req-id" = $discoverRequestId; "Content-Type" = "application/json"}
78 -
79 -$discoveryData = Invoke-RestMethod -Method Post -Headers $discoverRequestHeaders -Body $discoverPostBodyJson -Uri "https://api.mimecast.com/api/login/discover-authentication"
80 -
81 -$baseUrl = $discoveryData.data.region.api
82 -
83 -$keys = @{}
84 -
85 -$uri = $baseUrl + "/api/login/login"
86 -
87 -$requestId = [GUID]::NewGuid()
88 -
89 -$netCred = $creds.GetNetworkCredential()
90 -
91 -$PlainPassword = $netCred.Password
92 -
93 -$credsBytes = [System.Text.Encoding]::ASCII.GetBytes($creds.UserName + ":" + $PlainPassword)
94 -
95 -$creds64 = [System.Convert]::ToBase64String($credsBytes)
96 -
97 -$headers = @{"Authorization" = "Basic-Cloud " + $creds64; "x-mc-app-id" = $appId; "x-mc-req-id" = $requestId; "Content-Type" = "application/json"}
98 -
99 -$postBody = @{"data" = ,@{"username" = $creds.UserName}}
100 -
101 -$postBodyJson = ConvertTo-Json $postBody
102 -
103 -$data = Invoke-RestMethod -Method Post -Headers $headers -Body $postBodyJson -Uri $uri
104 -
105 -"Meta: " + $data.meta
106 -
107 -"Access key: " + $data.data.accessKey
108 -
109 -"Secret key: " + $data.data.secretKey
110 -
111 -"Fail: " + $data.fail.errorss
112 -```
113 -
114 -When prompted, enter the Application ID value received when you registered your application.
115 -
116 -Enter the email address and password of the user created in Step 1: Create a new user into the Windows credentials box that will launch after you have pasted the script into the Powershell window.
117 -
118 -Copy and paste the accessKey and secretKey values printed at the bottom of the Powershell window to use in your application.
119 -
120 -IMPORTANT: be sure to copy and paste these values to a text editor and remove any line breaks caused by your Powershell window size before using the values.
3 +# Requirements
4 +
5 +- A Mimecast plan with a Targeted Threat Protection (TTP) license. For more information, see Mimecast Plans.
6 +- A Mimecast administrator account.
7 +
8 +# Steps
9 +
10 +1. **Create the API application.**
11 +2. **Configure the API service account user.**
12 +3. **Configure 2-step authentication with SMS.**
13 +4. **Create API keys.**
14 +5. **Provide your Mimecast credentials to CoPilot.**
15 +
16 +## Step 1: Create the API Application
17 +
18 +1. Sign in to the Mimecast Administration Console.
19 +2. In the Administration menu, click `Services > API and Platform Integrations`.
20 +3. On the Available Integrations tab, click `Generate Keys`.
21 +4. In the Description field, enter a description for this API application.
22 +5. Click `Next`.
23 +6. Configure these settings:
24 + - Technical Point of Contact — Enter the name of the person who Mimecast should contact if necessary. For example, the active user configuring the API application.
25 + - Email — Enter the corresponding email for the point of contact.
26 +7. Click `Next`.
27 +8. Verify that your information is correct, and then click the Status toggle to the `Enabled` position.
28 +9. Click `Add`.
29 +10. Click the application that you created to open the information panel.
30 +11. Copy the Application ID and Application Key to a safe, encrypted location to provide to CoPilot later.
31 +
32 +## Step 2: Configure the API service account user
33 +
34 +To prevent permission overrides during the configuration process, create a dedicated service account user. For more information, see Managing API Applications.
35 +
36 +1. Sign in to the Mimecast Administration Console.
37 +2. Create a service account user:
38 + - In the Administration menu, click `Directories > Internal Directories`.
39 + - Select the domain the user will be added to.
40 + - Enter the email address for the user.
41 + - Create and confirm a password.
42 + - Click `Save`.
43 +3. Assign the service account user permissions:
44 + - In the Administration menu, click `Account > Roles`.
45 + - Click `Basic Administrator`.
46 + - Click `Add User to Role`.
47 + - Select the email address of the API service user account.
48 +
49 +## Step 3: Configure 2-step authentication with SMS
50 +
51 +You must configure 2-step authentication with SMS to create the API keys. After creating the API keys, you can revert to your previous authentication method.
52 +
53 +1. In a new browser tab, sign in to the Mimecast Administration Console as the service account user created in Configure the API service account user.
54 +2. Register a phone number for the service account that can be used for 2-step authentication with SMS:
55 + - Click the flag icon to select the correct country code.
56 + - Enter the phone number.
57 + - Click `Next`.
58 + - Enter the verification sent to the registered phone number.
59 + - Click `Verify`.
60 +3. Sign out of Mimecast.
61 +4. Return to the previous browser tab.
62 +
63 +## Step 4: Create API keys
64 +
65 +> **Notes:**
66 +>
67 +> - You may need to wait a maximum of 30 minutes after creating the API application before creating the API keys.
68 +
69 +1. In the browser tab that you just returned to, in the Administration menu, click `Services > API and Platform Integrations`.
70 +2. In the Your Application Integrations tab, select the application that you created in Create the API application.
71 +3. In the information pane, click `Create Keys`.
72 +4. In the Email Address field, enter the email address for the service account that you created in Configure the API service account user.
73 +5. Click `Next`.
74 +6. In the Type menu, click `Cloud`.
75 +7. In the Password field, enter the service account password, and then click `Next`.
76 +8. Follow the prompts to verify the service account, and then click `Next`.
77 +9. Click the eye next to Access Key and Secret Key to reveal each value.
78 +10. Copy the Access Key and Secret Key values and save them in a safe, encrypted location to provide to CoPilot later.
79 +11. Click `Finish`.
80 +
81 +## Step 5: Provide your Mimecast credentials to CoPilot
82 +
83 +1. In the CoPilot web app, click `Customers > **Select the Customer's details** > Integrations`.
84 +2. Click `Add Integration`.
85 +3. Select `Mimecast` from the list of integrations.
86 +4. Enter the Application ID, Application Key, Access Key, Secret Key, and Email Address that you saved in the previous steps.
backend/app/integrations/markdown/office365.md
+8 -22
@@ -70,33 +70,19 @@ Admin consent is required for API permission changes.
70
71 ![API permissions](/src/assets/images/office365/4-azure-wazuh-app-configure-permissions-admin-consent.png)
72
73 -### Wazuh configuration
73 +### CoPilot configuration
74
75 -Next, we will see the options we have to configure for the Wazuh integration.
75 +Next, we will see how to deploy this module in CoPilot. To do so, we will need to navigate to the `Customers` section and select the customer we want to deploy the module to. Once there, we will click on the `Integrations` tab and then on the `Add integration` button. We will select the `Office365` module and fill in the required fields.
76
77 -Configure the `office365` module either in the Wazuh manager or the Wazuh agent. To do so, modify the :doc:`ossec.conf </user-manual/reference/ossec-conf/index>` configuration file. Through the following configuration, Wazuh is ready to search for logs created by Office 365 audit-log. In this case, we will only search for the `Audit.SharePoint` type events within an interval of `1m`. Those logs will be only those that were created after the module was started:
77 +![Copilot Configuration](/src/assets/images/office365/copilot_config_customer_details.PNG)
78
79 -```html
80 -<office365>
81 - <enabled>yes</enabled>
82 - <interval>1m</interval>
83 - <curl_max_size>1M</curl_max_size>
84 - <only_future_events>yes</only_future_events>
85 - <api_auth>
86 - <tenant_id>your_tenant_id</tenant_id>
87 - <client_id>your_client_id</client_id>
88 - <client_secret>your_client_secret</client_secret>
89 - <api_type>commercial</api_type>
90 - </api_auth>
91 - <subscriptions>
92 - <subscription>Audit.SharePoint</subscription>
93 - </subscriptions>
94 -</office365>
95 -```
79 +![Copilot Configuration](/src/assets/images/office365/copilot_config_customer_integration.PNG)
80 +
81 +![Copilot Configuration](/src/assets/images/office365/copilot_config_customer_integration_config.PNG)
82
97 -To learn more, check the :ref:`office365-module` module reference.
83 +![Copilot Configuration](/src/assets/images/office365/copilot_config_customer_integration_auth.PNG)
84
99 -Using the configuration mentioned above, we will see an example of monitoring Office 365 activity.
85 +Once deployed, Copilot will automatically add the required configuration to the `Wazuh manager`, deploy the required Index, Stream, and Pipeline to `Graylog` and create the required Dashboards within `Grafana`. `Praeco` will also be configured to send `Exchange` and `Threat Intel` Office365 alerts to `DFIR-IRIS`.
86
87 ### Generate activity on Office 365
88
backend/app/integrations/mimecast/checkpoint/mimecast_00002.checkpoint
+1 -1
@@ -1 +1 @@
1 -eNo9jstSgzAYRt_l38IigYZgZ1wErAxlOo5c7GWHIaUBGjCkxdrx3UUXrr9zznx3GAW_aCErWAJJD2khu40up8hYPNFWEqXXzLPy4MRbRh3_c9M2T-t9uqp2re6K7FZ3TTzE5GQ-3iqq2G7yA5Ws8zQqD3H0vl95L-G0CLdcvn4NJhBasTpunreLR7ChPx5HYWCJbRilOHd9_fvigbiu7zrYs4FrURqRy7OYIYoIpQR7PkLOv2Buw7whG65Cj7JXf7GS8_6i5jCERcYwpowg-P4BPsVKLQ
1 +eNo9jtFOgzAYRt_lv5XEgm0pS7woyEhnjHEwNxZvsHSswAChkw3ju4teeP2dc_J9waDkuVc6hwWUeNwj7USTmVJ-IVgsQ9q93cb0JvGPsuKuwy5PVfmwStdhvqv6ehNfi7oUnSBH8_Gauw3fjcxvHlfJOsr2InpPQ_ocjDjYSv0ydcZXfcMLUS63-B4saA-HQRlY2BYMWp3qtvi94ZE7jyLPZhbIXmVGJfqkZshFhFFsU-Ix518w127ekAWfqh902_zFMinbczOHIdjE3LZdThB8_wA-O0ou
backend/app/integrations/mimecast/routes/mimecast.py
+1 -1
@@ -9,7 +9,6 @@ from sqlalchemy.ext.asyncio import AsyncSession
9
10 from app.auth.utils import AuthHandler
11 from app.db.db_session import get_db
12 -from app.integrations.alert_escalation.services.general_alert import create_alert
12 from app.integrations.mimecast.schema.mimecast import MimecastAuthKeys
13 from app.integrations.mimecast.schema.mimecast import MimecastRequest
14 from app.integrations.mimecast.schema.mimecast import MimecastResponse
@@ -67,6 +66,7 @@ def extract_mimecast_auth_keys(customer_integration: CustomerIntegrations) -> Di
66 detail="No auth keys found for Mimecast integration. Please create auth keys for Mimecast integration.",
67 )
68 except Exception as e:
69 + logger.error(f"Error extracting auth keys for Mimecast integration: {e}")
70 raise HTTPException(
71 status_code=404,
72 detail="No auth keys found for Mimecast integration. Please create auth keys for Mimecast integration.",
backend/app/integrations/mimecast/routes/provision.py
-9
@@ -1,17 +1,8 @@
1 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
2
8 -from app.auth.utils import AuthHandler
9 -from app.db.db_session import get_db
3 from app.integrations.mimecast.schema.mimecast import MimecastScheduledResponse
4 from app.schedulers.models.scheduler import CreateSchedulerRequest
12 -from app.schedulers.models.scheduler import JobMetadata
5 from app.schedulers.scheduler import add_scheduler_jobs
14 -from app.schedulers.services.invoke_mimecast import invoke_mimecast_integration
6
7 integration_mimecast_scheduler_router = APIRouter()
8
backend/app/integrations/mimecast/schema/mimecast.py
-5
@@ -1,14 +1,9 @@
1 from enum import Enum
2 -from typing import Any
3 -from typing import Dict
2 from typing import List
5 -from typing import Optional
3
4 from pydantic import BaseModel
8 -from pydantic import Extra
5 from pydantic import Field
6 from pydantic import HttpUrl
11 -from pydantic import root_validator
7
8
9 class PipelineRuleTitles(Enum):
backend/app/integrations/mimecast/services/mimecast.py
+1 -14
@@ -8,30 +8,17 @@ import os
8 import shutil
9 import time
10 import uuid
11 -from typing import Dict
11 from zipfile import ZipFile
12
13 import aiofiles
14 import requests
16 -from fastapi import APIRouter
17 -from fastapi import Depends
15 from fastapi import HTTPException
19 -from fastapi import Security
16 from loguru import logger
21 -from sqlalchemy.ext.asyncio import AsyncSession
17
23 -from app.auth.utils import AuthHandler
24 -from app.db.db_session import get_db
25 -from app.integrations.alert_escalation.services.general_alert import create_alert
18 from app.integrations.mimecast.schema.mimecast import MimecastAPIEndpointResponse
19 from app.integrations.mimecast.schema.mimecast import MimecastAuthKeys
20 from app.integrations.mimecast.schema.mimecast import MimecastRequest
21 from app.integrations.mimecast.schema.mimecast import MimecastResponse
30 -from app.integrations.routes import find_customer_integration
31 -from app.integrations.routes import get_customer_integrations_by_customer_code
32 -from app.integrations.schema import CustomerIntegrations
33 -from app.integrations.schema import CustomerIntegrationsResponse
34 -from app.integrations.utils.collection import send_get_request
22 from app.integrations.utils.collection import send_post_request
23 from app.integrations.utils.event_shipper import event_shipper
24 from app.integrations.utils.schema import EventShipperPayload
@@ -119,7 +106,7 @@ async def get_base_url(mimecast_auth_keys: MimecastAuthKeys) -> MimecastAPIEndpo
106 headers=headers,
107 data=post_body,
108 )
122 - if response["success"] == True:
109 + if response["success"] is True:
110 logger.info(f"Successfully retrieved base URL for Mimecast integration. Response: {response}")
111 return MimecastAPIEndpointResponse(**response)
112 else:
backend/app/integrations/office365/routes/provision.py
-2
@@ -4,12 +4,10 @@ from fastapi import APIRouter
4 from fastapi import Depends
5 from fastapi import HTTPException
6 from fastapi import Security
7 -from loguru import logger
7 from sqlalchemy.ext.asyncio import AsyncSession
8
9 from app.auth.utils import AuthHandler
10 from app.db.db_session import get_db
12 -from app.integrations.alert_escalation.services.general_alert import create_alert
11 from app.integrations.office365.schema.provision import ProvisionOffice365AuthKeys
12 from app.integrations.office365.schema.provision import ProvisionOffice365Request
13 from app.integrations.office365.schema.provision import ProvisionOffice365Response
backend/app/integrations/office365/schema/provision.py
-3
@@ -1,11 +1,8 @@
1 from enum import Enum
2 from typing import Any
3 from typing import Dict
4 -from typing import List
5 -from typing import Optional
4
5 from pydantic import BaseModel
8 -from pydantic import Extra
6 from pydantic import Field
7 from pydantic import root_validator
8
backend/app/integrations/office365/services/provision.py
+10 -3
@@ -27,7 +27,6 @@ from app.connectors.graylog.services.pipelines import get_pipeline_id
27 from app.connectors.graylog.services.pipelines import get_pipeline_rules
28 from app.connectors.graylog.services.pipelines import get_pipelines
29 from app.connectors.graylog.utils.universal import send_post_request
30 -from app.connectors.wazuh_indexer.utils.universal import create_wazuh_indexer_client
30 from app.connectors.wazuh_manager.utils.universal import send_get_request
31 from app.connectors.wazuh_manager.utils.universal import send_put_request
32 from app.customer_provisioning.schema.grafana import GrafanaDatasource
@@ -41,7 +40,6 @@ from app.customer_provisioning.services.grafana import create_grafana_folder
40 from app.customer_provisioning.services.grafana import get_opensearch_version
41 from app.customers.routes.customers import get_customer
42 from app.customers.routes.customers import get_customer_meta
44 -from app.integrations.alert_escalation.services.general_alert import create_alert
43 from app.integrations.models.customer_integration_settings import CustomerIntegrations
44 from app.integrations.office365.schema.provision import PipelineRuleTitles
45 from app.integrations.office365.schema.provision import PipelineTitles
@@ -551,7 +549,16 @@ async def create_office365_pipeline(pipeline_title: str) -> None:
549 Creates the 'OFFICE365 PROCESSING PIPELINE' pipeline.
550 """
551 pipeline_description = "OFFICE365 PROCESSING PIPELINE"
554 - pipeline_source = 'pipeline "OFFICE365 PROCESSING PIPELINE"\nstage 0 match either\nrule "WAZUH CREATE FIELD SYSLOG LEVEL - ALERT"\nrule "WAZUH CREATE FIELD SYSLOG LEVEL - INFO"\nrule "WAZUH CREATE FIELD SYSLOG LEVEL - NOTICE"\nrule "WAZUH CREATE FIELD SYSLOG LEVEL - WARNING"\nrule "Office365 Timestamp - UTC"\nend'
552 + pipeline_source = (
553 + 'pipeline "OFFICE365 PROCESSING PIPELINE"\n'
554 + "stage 0 match either\n"
555 + 'rule "WAZUH CREATE FIELD SYSLOG LEVEL - ALERT"\n'
556 + 'rule "WAZUH CREATE FIELD SYSLOG LEVEL - INFO"\n'
557 + 'rule "WAZUH CREATE FIELD SYSLOG LEVEL - NOTICE"\n'
558 + 'rule "WAZUH CREATE FIELD SYSLOG LEVEL - WARNING"\n'
559 + 'rule "Office365 Timestamp - UTC"\n'
560 + "end"
561 + )
562 await create_pipeline_graylog(CreatePipeline(title=pipeline_title, description=pipeline_description, source=pipeline_source))
563
564
backend/app/integrations/routes.py
-1
@@ -6,7 +6,6 @@ from fastapi import Depends
6 from fastapi import HTTPException
7 from fastapi import Security
8 from loguru import logger
9 -from pydantic import ValidationError
9 from sqlalchemy import delete
10 from sqlalchemy import update
11 from sqlalchemy.exc import NoResultFound
backend/app/integrations/schema.py
-5
@@ -1,14 +1,9 @@
1 -from typing import Dict
1 from typing import List
2 from typing import Optional
4 -from typing import Type
5 -from typing import Union
3
4 from pydantic import BaseModel
5 from pydantic import Field
6
10 -from app.integrations.models.customer_integration_settings import AvailableIntegrations
11 -
7
8 class AuthKey(BaseModel):
9 auth_key_name: str
backend/app/integrations/utils/collection.py
-3
@@ -1,7 +1,4 @@
1 import asyncio
2 -import json
3 -import time
4 -import traceback
2 from typing import Any
3 from typing import Dict
4 from typing import Optional
backend/app/routers/log_shipper_test.py deleted
-11
@@ -1,11 +0,0 @@
1 -from fastapi import APIRouter
2 -
3 -from app.integrations.log_shipper_test.routes.event_shipper import (
4 - log_shipper_test_router,
5 -)
6 -
7 -# Instantiate the APIRouter
8 -router = APIRouter()
9 -
10 -# Include the Log Shipper Test related routes
11 -router.include_router(log_shipper_test_router, prefix="/log_shipper_test", tags=["Log Shipper Test"])
backend/app/schedulers/routes/scheduler.py
+116 -82
@@ -1,14 +1,10 @@
1 from fastapi import APIRouter
2 from fastapi import Depends
3 -from fastapi import HTTPException
4 -from fastapi import Security
3 from loguru import logger
4 from sqlalchemy.ext.asyncio import AsyncSession
5 from sqlalchemy.future import select
8 -from sqlmodel import select
6
7 from app.db.db_session import get_db
11 -from app.schedulers.models.scheduler import CreateSchedulerRequest
8 from app.schedulers.models.scheduler import JobMetadata
9 from app.schedulers.scheduler import init_scheduler
10 from app.schedulers.schema.scheduler import JobsResponse
@@ -16,26 +12,69 @@ from app.schedulers.schema.scheduler import JobsResponse
12 scheduler_router = APIRouter()
13
14
19 -@scheduler_router.get(
20 - "",
21 - response_model=JobsResponse,
22 - description="Get all jobs",
23 -)
15 +def get_scheduler():
16 + # Singleton pattern or reference to existing instance
17 + return init_scheduler()
18 +
19 +
20 +async def find_job_by_id(scheduler, job_id):
21 + """
22 + Find a job in the scheduler by its ID.
23 +
24 + Args:
25 + scheduler (Scheduler): The scheduler object.
26 + job_id (str): The ID of the job to find.
27 +
28 + Returns:
29 + Job: The job object if found, None otherwise.
30 + """
31 + for job in scheduler.get_jobs():
32 + if job.id == job_id:
33 + return job
34 + return None
35 +
36 +
37 +async def manage_job_metadata(session, job_id, action, **kwargs):
38 + """
39 + Manage job metadata based on the specified action.
40 +
41 + Args:
42 + session (Session): The database session.
43 + job_id (int): The ID of the job.
44 + action (str): The action to perform on the job metadata. Possible values are "update" and "delete".
45 + **kwargs: Additional keyword arguments representing the fields to update and their new values.
46 +
47 + Returns:
48 + JobMetadata: The updated or deleted job metadata.
49 + """
50 + job_metadata = await session.execute(select(JobMetadata).filter_by(job_id=job_id))
51 + job_metadata = job_metadata.scalars().first()
52 +
53 + if action == "update":
54 + for key, value in kwargs.items():
55 + setattr(job_metadata, key, value)
56 + await session.commit()
57 + elif action == "delete":
58 + await session.delete(job_metadata)
59 + await session.commit()
60 +
61 + return job_metadata
62 +
63 +
64 +@scheduler_router.get("", response_model=JobsResponse, description="Get all jobs")
65 async def get_all_jobs(session: AsyncSession = Depends(get_db)) -> JobsResponse:
66 """
26 - Provisions Office365 integration for a customer.
67 + Retrieve all jobs from the scheduler.
68
69 Args:
29 - provision_office365_request (ProvisionOffice365Request): The request object containing the necessary information for provisioning.
30 - session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
70 + session (AsyncSession): The database session.
71
72 Returns:
33 - ProvisionOffice365Response: The response object containing the result of the provisioning.
73 + JobsResponse: The response containing the list of jobs.
74 +
75 """
35 - scheduler = init_scheduler()
76 + scheduler = get_scheduler()
77 jobs = scheduler.get_jobs()
37 - test = scheduler.print_jobs()
38 - logger.info(f"test: {test}")
78 apscheduler_jobs = []
79 for job in jobs:
80 job_metadata = await session.execute(select(JobMetadata).filter_by(job_id=job.id))
@@ -44,108 +83,103 @@ async def get_all_jobs(session: AsyncSession = Depends(get_db)) -> JobsResponse:
83 {"id": job.id, "name": job.name, "time_interval": job_metadata.time_interval, "enabled": job_metadata.enabled},
84 )
85 logger.info(f"apscheduler_jobs: {apscheduler_jobs}")
47 - return JobsResponse(jobs=[{"id": job.id, "name": job.name} for job in jobs], success=True, message="Jobs successfully retrieved.")
86 + return JobsResponse(jobs=apscheduler_jobs, success=True, message="Jobs successfully retrieved.")
87
88
50 -@scheduler_router.post(
51 - "/start/{job_id}",
52 - description="Start a job",
53 -)
89 +@scheduler_router.post("/start/{job_id}", description="Start a job")
90 async def start_job(job_id: str):
91 """
56 - Provisions Office365 integration for a customer.
92 + Start a job by resuming its execution.
93
94 Args:
59 - provision_office365_request (ProvisionOffice365Request): The request object containing the necessary information for provisioning.
60 - session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
95 + job_id (str): The ID of the job to start.
96
97 Returns:
63 - ProvisionOffice365Response: The response object containing the result of the provisioning.
98 + dict: A dictionary containing the success status and a message.
99 + - If the job is found and successfully started, the success status is True and the message is "Job started successfully".
100 + - If the job is not found, the success status is False and the message is "Job not found".
101 """
65 - scheduler = init_scheduler()
66 - jobs = scheduler.get_jobs()
67 - logger.info(f"jobs: {jobs}")
68 - for job in jobs:
69 - if job.id == job_id:
70 - job.resume()
71 - return {"success": True, "message": "Job started successfully"}
102 + scheduler = get_scheduler()
103 + job = await find_job_by_id(scheduler, job_id)
104 + if job:
105 + job.resume()
106 + logger.info(f"Job {job_id} started successfully")
107 + return {"success": True, "message": "Job started successfully"}
108 + logger.error(f"Job {job_id} not found for starting")
109 return {"success": False, "message": "Job not found"}
110
111
75 -@scheduler_router.post(
76 - "/pause/{job_id}",
77 - description="Pause a job",
78 -)
112 +@scheduler_router.post("/pause/{job_id}", description="Pause a job")
113 async def pause_job(job_id: str):
114 """
81 - Provisions Office365 integration for a customer.
115 + Pause a job.
116
117 Args:
84 - provision_office365_request (ProvisionOffice365Request): The request object containing the necessary information for provisioning.
85 - session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
118 + job_id (str): The ID of the job to be paused.
119
120 Returns:
88 - ProvisionOffice365Response: The response object containing the result of the provisioning.
121 + dict: A dictionary containing the success status and a message.
122 + - If the job is paused successfully, the success status is True and the message is "Job paused successfully".
123 + - If the job is not found, the success status is False and the message is "Job not found".
124 """
90 - scheduler = init_scheduler()
91 - jobs = scheduler.get_jobs()
92 - logger.info(f"jobs: {jobs}")
93 - for job in jobs:
94 - if job.id == job_id:
95 - job.pause()
96 - return {"success": True, "message": "Job paused successfully"}
125 + scheduler = get_scheduler()
126 + job = await find_job_by_id(scheduler, job_id)
127 + if job:
128 + job.pause()
129 + logger.info(f"Job {job_id} paused successfully")
130 + return {"success": True, "message": "Job paused successfully"}
131 + logger.error(f"Job {job_id} not found for pausing")
132 return {"success": False, "message": "Job not found"}
133
134
100 -@scheduler_router.put(
101 - "/update/{job_id}",
102 - description="Update a job",
103 -)
135 +@scheduler_router.put("/update/{job_id}", description="Update a job")
136 async def update_job(job_id: str, time_interval: int, session: AsyncSession = Depends(get_db)):
137 """
106 - Provisions Office365 integration for a customer.
138 + Update a job with the specified job_id and time_interval.
139
108 - Args:
109 - provision_office365_request (ProvisionOffice365Request): The request object containing the necessary information for provisioning.
110 - session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
140 + Parameters:
141 + - job_id (str): The ID of the job to be updated.
142 + - time_interval (int): The new time interval for the job in minutes.
143 + - session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
144
145 Returns:
113 - ProvisionOffice365Response: The response object containing the result of the provisioning.
146 + - dict: A dictionary containing the success status and a message.
147 +
148 + Example:
149 + {
150 + "success": True,
151 + "message": "Job updated successfully"
152 + }
153 """
115 - scheduler = init_scheduler()
116 - jobs = scheduler.get_jobs()
117 - logger.info(f"jobs: {jobs}")
118 - for job in jobs:
119 - if job.id == job_id:
120 - job.reschedule(trigger="interval", minutes=time_interval)
121 - job_metadata = await session.execute(select(JobMetadata).filter_by(job_id=job_id))
122 - job_metadata = job_metadata.scalars().first()
123 - job_metadata.time_interval = time_interval
124 - await session.commit()
125 - return {"success": True, "message": "Job updated successfully"}
154 + scheduler = get_scheduler()
155 + job = await find_job_by_id(scheduler, job_id)
156 + if job:
157 + job.reschedule(trigger="interval", minutes=time_interval)
158 + await manage_job_metadata(session, job_id, "update", time_interval=time_interval)
159 + logger.info(f"Job {job_id} updated successfully")
160 + return {"success": True, "message": "Job updated successfully"}
161 + logger.error(f"Job {job_id} not found for updating")
162 return {"success": False, "message": "Job not found"}
163
164
129 -@scheduler_router.delete(
130 - "/{job_id}",
131 - description="Delete a job",
132 -)
133 -async def delete_job(job_id: str):
165 +@scheduler_router.delete("/{job_id}", description="Delete a job")
166 +async def delete_job(job_id: str, session: AsyncSession = Depends(get_db)):
167 """
135 - Provisions Office365 integration for a customer.
168 + Delete a job.
169
170 Args:
138 - provision_office365_request (ProvisionOffice365Request): The request object containing the necessary information for provisioning.
171 + job_id (str): The ID of the job to be deleted.
172 session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
173
174 Returns:
142 - ProvisionOffice365Response: The response object containing the result of the provisioning.
175 + dict: A dictionary containing the success status and a message.
176 """
144 - scheduler = init_scheduler()
145 - jobs = scheduler.get_jobs()
146 - logger.info(f"jobs: {jobs}")
147 - for job in jobs:
148 - if job.id == job_id:
149 - job.remove()
150 - return {"success": True, "message": "Job deleted successfully"}
177 + scheduler = get_scheduler()
178 + job = await find_job_by_id(scheduler, job_id)
179 + if job:
180 + scheduler.remove_job(job_id)
181 + await manage_job_metadata(session, job_id, "delete")
182 + logger.info(f"Job {job_id} deleted successfully")
183 + return {"success": True, "message": "Job deleted successfully"}
184 + logger.error(f"Job {job_id} not found for deletion")
185 return {"success": False, "message": "Job not found"}
backend/app/schedulers/scheduler.py
-76
@@ -9,82 +9,6 @@ from app.schedulers.models.scheduler import JobMetadata
9 from app.schedulers.services.agent_sync import agent_sync
10 from app.schedulers.services.invoke_mimecast import invoke_mimecast_integration
11
12 -# def init_scheduler():
13 -# """
14 -# Initializes and configures the scheduler.
15 -
16 -# Returns:
17 -# scheduler (AsyncIOScheduler): The initialized scheduler object.
18 -# """
19 -# scheduler = AsyncIOScheduler()
20 -# jobstores = {"default": SQLAlchemyJobStore(engine=sync_engine)}
21 -# scheduler.configure(jobstores=jobstores)
22 -
23 -# # Use SyncSessionLocal to create a synchronous session
24 -# with SyncSessionLocal() as session:
25 -# # Synchronous ORM operations
26 -# job_metadata = session.query(JobMetadata).filter_by(job_id="agent_sync").one_or_none()
27 -# #invoke_mimecast_job_metadata = session.query(JobMetadata).filter_by(job_id="invoke_mimecast_integration").one_or_none()
28 -# if not job_metadata:
29 -# job_metadata = JobMetadata(job_id="agent_sync", last_success=None, time_interval=60, enabled=True)
30 -# #invoke_mimecast_job_metadata = JobMetadata(job_id="invoke_mimecast_integration", last_success=None, time_interval=60, enabled=True)
31 -# session.add(job_metadata)
32 -# #session.add(invoke_mimecast_job_metadata)
33 -# else:
34 -# job_metadata.time_interval = 1
35 -# job_metadata.enabled = True
36 -# session.commit()
37 -
38 -# scheduler.add_job(agent_sync, "interval", minutes=60, id="agent_sync", replace_existing=True)
39 -# #scheduler.add_job(invoke_mimecast_integration, "interval", minutes=1, id="invoke_mimecast_integration", replace_existing=True)
40 -# return scheduler
41 -
42 -
43 -# async def add_scheduler_jobs(create_scheduler_request: CreateSchedulerRequest):
44 -# """
45 -# Adds a job to the scheduler.
46 -
47 -# Args:
48 -# create_scheduler_request (CreateSchedulerRequest): The request object containing the job details.
49 -# """
50 -# scheduler = init_scheduler()
51 -# logger.info(f"create_scheduler_request: {create_scheduler_request}")
52 -
53 -# # Assuming 'get_function_by_name' fetches the actual function based on a string name.
54 -# job_function = get_function_by_name(create_scheduler_request.function_name)
55 -
56 -# scheduler.add_job(
57 -# job_function,
58 -# "interval",
59 -# minutes=create_scheduler_request.time_interval,
60 -# id=create_scheduler_request.job_id,
61 -# replace_existing=True,
62 -# )
63 -# if not scheduler.running:
64 -# scheduler.start()
65 -
66 -# def init_scheduler():
67 -# """
68 -# Initializes and returns an AsyncIO scheduler.
69 -# """
70 -# return AsyncIOScheduler()
71 -
72 -# def get_function_by_name(function_name: str):
73 -# """
74 -# Returns a function object based on its name.
75 -
76 -# Args:
77 -# function_name (str): The name of the function to retrieve.
78 -
79 -# Returns:
80 -# Callable: The function object.
81 -# """
82 -# # Example implementation
83 -# if function_name == "invoke_mimecast_integration":
84 -# return invoke_mimecast_integration
85 -# else:
86 -# raise ValueError(f"Function {function_name} not found")
87 -
12
13 def init_scheduler():
14 """
backend/app/schedulers/schema/scheduler.py
+2
@@ -6,6 +6,8 @@ from pydantic import BaseModel
6 class Job(BaseModel):
7 id: str
8 name: str
9 + enabled: bool
10 + time_interval: int
11
12
13 class JobsResponse(BaseModel):
backend/app/schedulers/services/invoke_mimecast.py
-4
@@ -1,12 +1,9 @@
1 -import os
1 from datetime import datetime
2
4 -import requests
3 from dotenv import load_dotenv
4 from loguru import logger
5 from sqlalchemy import select
6
9 -from app.db.db_session import AsyncSession
7 from app.db.db_session import get_db_session
8 from app.db.db_session import get_sync_db_session
9 from app.integrations.mimecast.routes.mimecast import invoke_mimecast_route
@@ -14,7 +11,6 @@ from app.integrations.mimecast.schema.mimecast import MimecastRequest
11 from app.integrations.mimecast.schema.mimecast import MimecastResponse
12 from app.integrations.models.customer_integration_settings import CustomerIntegrations
13 from app.schedulers.models.scheduler import JobMetadata
17 -from app.schedulers.utils.universal import scheduler_login
14
15 load_dotenv()
16
backend/app/threat_intel/services/socfortress.py
-1
@@ -1,6 +1,5 @@
1 from typing import Any
2 from typing import Dict
3 -from typing import Optional
3
4 import httpx
5 from fastapi import HTTPException
backend/copilot.py
-2
@@ -36,7 +36,6 @@ from app.routers import graylog
36 from app.routers import healthcheck
37 from app.routers import influxdb
38 from app.routers import integrations
39 -from app.routers import log_shipper_test
39 from app.routers import logs
40 from app.routers import mimecast
41 from app.routers import office365
@@ -102,7 +101,6 @@ app.include_router(threat_intel.router)
101 app.include_router(ask_socfortress.router)
102 app.include_router(alert_creation.router)
103 app.include_router(alert_creation_settings.router)
105 -app.include_router(log_shipper_test.router)
104 app.include_router(integrations.router)
105 app.include_router(office365.router)
106 app.include_router(mimecast.router)
src/assets/images/office365/copilot_config_customer_details.PNG
Binary files /dev/null and b/src/assets/images/office365/copilot_config_customer_details.PNG differ
src/assets/images/office365/copilot_config_customer_integration.PNG
Binary files /dev/null and b/src/assets/images/office365/copilot_config_customer_integration.PNG differ
src/assets/images/office365/copilot_config_customer_integration_auth.PNG
Binary files /dev/null and b/src/assets/images/office365/copilot_config_customer_integration_auth.PNG differ
src/assets/images/office365/copilot_config_customer_integration_config.PNG
Binary files /dev/null and b/src/assets/images/office365/copilot_config_customer_integration_config.PNG differ