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

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
+
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
+
80
+
81
+
82
97
-To learn more, check the :ref:`office365-module` module reference.
83
+
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