@cryptotaxi247 / CoPilot / commits / b7d7d121

precommit-fixes (#855)

taylor_socfortress committed May 8, 2026 at 10:26 UTC b7d7d121592ac4fc6105503090e9362e94496020
78 files changed +868 -668
backend/app/active_response/schema/active_response.py
+18 -11
@@ -1,12 +1,16 @@
1 from enum import Enum
2 -from typing import Literal, Any
2 +from typing import Any
3 from typing import Dict
4 from typing import List
5 +from typing import Literal
6 from typing import Optional
7
8 from fastapi import HTTPException
8 -from pydantic import field_validator, model_validator, ConfigDict, BaseModel
9 +from pydantic import BaseModel
10 +from pydantic import ConfigDict
11 from pydantic import Field
12 +from pydantic import field_validator
13 +from pydantic import model_validator
14
15
16 class ActiveResponsesSupported(Enum):
@@ -121,16 +125,19 @@ class InvokeActiveResponseRequest(BaseModel):
125 raise HTTPException(status_code=400, detail="Invalid command for alert")
126
127 return values
124 - model_config = ConfigDict(json_schema_extra={
125 - "example": {
126 - "endpoint": "/active-response",
127 - "arguments": [],
128 - "command": "windows_firewall",
129 - "custom": True,
130 - "alert": {"action": "block", "ip": "1.1.1.1"},
131 - "params": {"wait_for_complete": True, "agents_list": ["032"]},
128 +
129 + model_config = ConfigDict(
130 + json_schema_extra={
131 + "example": {
132 + "endpoint": "/active-response",
133 + "arguments": [],
134 + "command": "windows_firewall",
135 + "custom": True,
136 + "alert": {"action": "block", "ip": "1.1.1.1"},
137 + "params": {"wait_for_complete": True, "agents_list": ["032"]},
138 + },
139 },
133 - })
140 + )
141
142
143 class InvokeActiveResponseResponse(BaseModel):
backend/app/active_response/schema/graylog.py
+80 -75
@@ -4,7 +4,8 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import ConfigDict, BaseModel
7 +from pydantic import BaseModel
8 +from pydantic import ConfigDict
9 from pydantic import Field
10
11
@@ -69,46 +70,48 @@ class GraylogEventNotification(BaseModel):
70 job_trigger_id: str
71 event: GraylogEvent
72 backlog: List[Any] = Field(default_factory=list)
72 - model_config = ConfigDict(json_schema_extra={
73 - "example": {
74 - "event_definition_id": "67c78b93cf26aa2045bdc2ea",
75 - "event_definition_type": "aggregation-v1",
76 - "event_definition_title": "ACTIVE RESPONSE WEBSERVER THREAT INTEL",
77 - "event_definition_description": "",
78 - "job_definition_id": "67c78befcf26aa2045bdc4e9",
79 - "job_trigger_id": "67c78c0bcf26aa2045bdc5b5",
80 - "event": {
81 - "id": "01JNHQP35THD48VH8601F6EMHE",
82 - "event_definition_type": "aggregation-v1",
73 + model_config = ConfigDict(
74 + json_schema_extra={
75 + "example": {
76 "event_definition_id": "67c78b93cf26aa2045bdc2ea",
84 - "origin_context": "urn:graylog:message:es:graylog-01_3:766b70b4-f94f-11ef-be9a-005056b6f13d",
85 - "timestamp": "2025-03-04T23:21:55.840Z",
86 - "timestamp_processing": "2025-03-04T23:26:03.450Z",
87 - "timerange_start": None,
88 - "timerange_end": None,
89 - "streams": [],
90 - "source_streams": ["679945aa7c4dd06afcef5feb", "679945a47c4dd06afcef5ef3"],
91 - "message": "ACTIVE RESPONSE WEBSERVER THREAT INTEL",
92 - "source": "soc-grlog01",
93 - "key_tuple": [],
94 - "key": "",
95 - "priority": 2,
96 - "scores": {},
97 - "associated_assets": [],
98 - "alert": True,
99 - "fields": {"COMMAND": "domain_sinkhole", "AGENT_ID": "032", "ACTION": "sinkhole", "VALUE": "example.com"},
100 - "group_by_fields": {},
101 - "replay_info": {
102 - "timerange_start": "2025-03-04T23:21:03.360Z",
103 - "timerange_end": "2025-03-04T23:26:03.360Z",
104 - "query": "_exists_:threat_intel_score",
105 - "streams": ["679945aa7c4dd06afcef5feb", "679945a47c4dd06afcef5ef3"],
106 - "filters": [],
77 + "event_definition_type": "aggregation-v1",
78 + "event_definition_title": "ACTIVE RESPONSE WEBSERVER THREAT INTEL",
79 + "event_definition_description": "",
80 + "job_definition_id": "67c78befcf26aa2045bdc4e9",
81 + "job_trigger_id": "67c78c0bcf26aa2045bdc5b5",
82 + "event": {
83 + "id": "01JNHQP35THD48VH8601F6EMHE",
84 + "event_definition_type": "aggregation-v1",
85 + "event_definition_id": "67c78b93cf26aa2045bdc2ea",
86 + "origin_context": "urn:graylog:message:es:graylog-01_3:766b70b4-f94f-11ef-be9a-005056b6f13d",
87 + "timestamp": "2025-03-04T23:21:55.840Z",
88 + "timestamp_processing": "2025-03-04T23:26:03.450Z",
89 + "timerange_start": None,
90 + "timerange_end": None,
91 + "streams": [],
92 + "source_streams": ["679945aa7c4dd06afcef5feb", "679945a47c4dd06afcef5ef3"],
93 + "message": "ACTIVE RESPONSE WEBSERVER THREAT INTEL",
94 + "source": "soc-grlog01",
95 + "key_tuple": [],
96 + "key": "",
97 + "priority": 2,
98 + "scores": {},
99 + "associated_assets": [],
100 + "alert": True,
101 + "fields": {"COMMAND": "domain_sinkhole", "AGENT_ID": "032", "ACTION": "sinkhole", "VALUE": "example.com"},
102 + "group_by_fields": {},
103 + "replay_info": {
104 + "timerange_start": "2025-03-04T23:21:03.360Z",
105 + "timerange_end": "2025-03-04T23:26:03.360Z",
106 + "query": "_exists_:threat_intel_score",
107 + "streams": ["679945aa7c4dd06afcef5feb", "679945a47c4dd06afcef5ef3"],
108 + "filters": [],
109 + },
110 },
111 + "backlog": [],
112 },
109 - "backlog": [],
113 },
111 - })
114 + )
115
116
117 class GraylogThresholdEvent(BaseModel):
@@ -144,47 +147,49 @@ class GraylogThresholdEventNotification(BaseModel):
147 job_trigger_id: str
148 event: GraylogThresholdEvent
149 backlog: List[Any] = Field(default_factory=list)
147 - model_config = ConfigDict(json_schema_extra={
148 - "example": {
149 - "event_definition_id": "67b6687184088513bdc6cd1b",
150 - "event_definition_type": "aggregation-v1",
151 - "event_definition_title": "DELL SWITCHES - MULTIPLE AUTH FAILURES",
152 - "event_definition_description": "DELL SWITCHES - MULTIPLE AUTH FAILURES",
153 - "job_definition_id": "67dde9bc84088513bde5ce29",
154 - "job_trigger_id": "67ddeabf84088513bde5d283",
155 - "event": {
156 - "id": "01JPXDSZ8AECWZ88HR9JQPYHJP",
157 - "event_definition_type": "aggregation-v1",
150 + model_config = ConfigDict(
151 + json_schema_extra={
152 + "example": {
153 "event_definition_id": "67b6687184088513bdc6cd1b",
159 - "origin_context": None,
160 - "timestamp": "2025-03-21T22:39:54.219Z",
161 - "timestamp_processing": "2025-03-21T22:39:59.754Z",
162 - "timerange_start": "2024-08-25T14:39:54.219Z",
163 - "timerange_end": "2025-03-21T22:39:54.219Z",
164 - "streams": [],
165 - "source_streams": ["67abcb0a84088513bdc09e32"],
166 - "message": "DELL SWITCHES - MULTIPLE AUTH FAILURES: 10.0.64.233 - count()=14.0",
167 - "source": "soc-grlog02",
168 - "key_tuple": [],
169 - "key": "",
170 - "priority": 2,
171 - "scores": {},
172 - "associated_assets": [],
173 - "alert": True,
174 - "fields": {
175 - "CUSTOMER_CODE": "6bdd96a0-06a5-11f0-a499-005056b6c109",
176 - "SOURCE": "DELLSWITCH",
177 - "ALERT_DESCRIPTION": "THIS IS A TEST",
178 - },
179 - "group_by_fields": {"source": "10.0.64.233"},
180 - "replay_info": {
154 + "event_definition_type": "aggregation-v1",
155 + "event_definition_title": "DELL SWITCHES - MULTIPLE AUTH FAILURES",
156 + "event_definition_description": "DELL SWITCHES - MULTIPLE AUTH FAILURES",
157 + "job_definition_id": "67dde9bc84088513bde5ce29",
158 + "job_trigger_id": "67ddeabf84088513bde5d283",
159 + "event": {
160 + "id": "01JPXDSZ8AECWZ88HR9JQPYHJP",
161 + "event_definition_type": "aggregation-v1",
162 + "event_definition_id": "67b6687184088513bdc6cd1b",
163 + "origin_context": None,
164 + "timestamp": "2025-03-21T22:39:54.219Z",
165 + "timestamp_processing": "2025-03-21T22:39:59.754Z",
166 "timerange_start": "2024-08-25T14:39:54.219Z",
167 "timerange_end": "2025-03-21T22:39:54.219Z",
183 - "query": '"An invalid user tried to login"',
184 - "streams": ["67abcb0a84088513bdc09e32"],
185 - "filters": [],
168 + "streams": [],
169 + "source_streams": ["67abcb0a84088513bdc09e32"],
170 + "message": "DELL SWITCHES - MULTIPLE AUTH FAILURES: 10.0.64.233 - count()=14.0",
171 + "source": "soc-grlog02",
172 + "key_tuple": [],
173 + "key": "",
174 + "priority": 2,
175 + "scores": {},
176 + "associated_assets": [],
177 + "alert": True,
178 + "fields": {
179 + "CUSTOMER_CODE": "6bdd96a0-06a5-11f0-a499-005056b6c109",
180 + "SOURCE": "DELLSWITCH",
181 + "ALERT_DESCRIPTION": "THIS IS A TEST",
182 + },
183 + "group_by_fields": {"source": "10.0.64.233"},
184 + "replay_info": {
185 + "timerange_start": "2024-08-25T14:39:54.219Z",
186 + "timerange_end": "2025-03-21T22:39:54.219Z",
187 + "query": '"An invalid user tried to login"',
188 + "streams": ["67abcb0a84088513bdc09e32"],
189 + "filters": [],
190 + },
191 },
192 + "backlog": [],
193 },
188 - "backlog": [],
194 },
190 - })
195 + )
backend/app/agents/sca/schema/sca.py
+2 -1
@@ -4,7 +4,8 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import ConfigDict, BaseModel
7 +from pydantic import BaseModel
8 +from pydantic import ConfigDict
9 from pydantic import Field
10
11
backend/app/agents/velociraptor/schema/agents.py
+3 -1
@@ -2,7 +2,8 @@ from datetime import datetime
2 from typing import List
3 from typing import Optional
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7 from pydantic import Field
8
9
@@ -16,6 +17,7 @@ class VelociraptorAgent(BaseModel):
17 def client_last_seen_as_datetime(self):
18 dt = datetime.strptime(self.client_last_seen, "%Y-%m-%dT%H:%M:%S%z")
19 return dt.replace(tzinfo=None)
20 +
21 model_config = ConfigDict(populate_by_name=True)
22
23
backend/app/agents/vulnerabilities/schema/vulnerabilities.py
+2 -1
@@ -4,7 +4,8 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import ConfigDict, BaseModel
7 +from pydantic import BaseModel
8 +from pydantic import ConfigDict
9 from pydantic import Field
10
11
backend/app/agents/wazuh/schema/agents.py
+3 -1
@@ -3,7 +3,8 @@ from enum import Enum
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9
10
@@ -29,6 +30,7 @@ class WazuhAgent(BaseModel):
30 def agent_last_seen_as_datetime(self):
31 dt = datetime.strptime(self.agent_last_seen, "%Y-%m-%dT%H:%M:%S%z")
32 return dt.replace(tzinfo=None)
33 +
34 model_config = ConfigDict(populate_by_name=True)
35
36
backend/app/agents/wazuh/syscollector/schema/packages.py
+2 -1
@@ -3,7 +3,8 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9
10
backend/app/ai_analyst/schema/ai_analyst.py
+2 -1
@@ -4,8 +4,9 @@ from enum import Enum
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import field_validator, BaseModel
7 +from pydantic import BaseModel
8 from pydantic import Field
9 +from pydantic import field_validator
10
11 # --- Enums ---
12
backend/app/auth/models/users.py
+2 -1
@@ -7,8 +7,9 @@ from typing import List
7 from typing import Optional
8
9 import bcrypt
10 -from pydantic import field_validator, BaseModel
10 +from pydantic import BaseModel
11 from pydantic import EmailStr
12 +from pydantic import field_validator
13 from sqlmodel import Field
14 from sqlmodel import Relationship
15 from sqlmodel import SQLModel
backend/app/auth/services/totp.py
+1 -1
@@ -45,7 +45,7 @@ except (ValueError, TypeError) as e:
45 raise RuntimeError(
46 "TOTP_ENCRYPTION_KEY is malformed. Expected a 32-byte url-safe base64 key "
47 "(typically 44 characters ending with '='). Generate a valid one with:\n"
48 - " python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"\n"
48 + ' python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"\n'
49 f"Then paste the value into your .env without surrounding quotes or trailing whitespace. "
50 f"(underlying error: {e})",
51 ) from e
backend/app/connectors/grafana/schema/reporting.py
+48 -45
@@ -2,8 +2,9 @@ from typing import List
2 from typing import Optional
3
4 from fastapi import HTTPException
5 -from pydantic import field_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import field_validator
8
9
10 class GrafanaOrganizations(BaseModel):
@@ -223,50 +224,52 @@ class GenerateReportRequest(BaseModel):
224 rows: List[RequestRow] = Field(
225 ...,
226 description="Rows in the report",
226 - examples=[[
227 - {
228 - "id": 1710437961108,
229 - "panels": [
230 - {
231 - "panel_id": 5,
232 - "org_id": 1,
233 - "dashboard_title": "HUNTRESS - _SUMMARY",
234 - "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
235 - "panel_width": 500,
236 - "panel_height": 300,
237 - },
238 - {
239 - "panel_id": 3,
240 - "org_id": 1,
241 - "dashboard_title": "HUNTRESS - _SUMMARY",
242 - "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
243 - "panel_width": 500,
244 - "panel_height": 300,
245 - },
246 - ],
247 - },
248 - {
249 - "id": 1710437961109,
250 - "panels": [
251 - {
252 - "panel_id": 5,
253 - "org_id": 1,
254 - "dashboard_title": "HUNTRESS - _SUMMARY",
255 - "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
256 - "panel_width": 500,
257 - "panel_height": 300,
258 - },
259 - {
260 - "panel_id": 3,
261 - "org_id": 1,
262 - "dashboard_title": "HUNTRESS - _SUMMARY",
263 - "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
264 - "panel_width": 500,
265 - "panel_height": 300,
266 - },
267 - ],
268 - },
269 - ]],
227 + examples=[
228 + [
229 + {
230 + "id": 1710437961108,
231 + "panels": [
232 + {
233 + "panel_id": 5,
234 + "org_id": 1,
235 + "dashboard_title": "HUNTRESS - _SUMMARY",
236 + "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
237 + "panel_width": 500,
238 + "panel_height": 300,
239 + },
240 + {
241 + "panel_id": 3,
242 + "org_id": 1,
243 + "dashboard_title": "HUNTRESS - _SUMMARY",
244 + "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
245 + "panel_width": 500,
246 + "panel_height": 300,
247 + },
248 + ],
249 + },
250 + {
251 + "id": 1710437961109,
252 + "panels": [
253 + {
254 + "panel_id": 5,
255 + "org_id": 1,
256 + "dashboard_title": "HUNTRESS - _SUMMARY",
257 + "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
258 + "panel_width": 500,
259 + "panel_height": 300,
260 + },
261 + {
262 + "panel_id": 3,
263 + "org_id": 1,
264 + "dashboard_title": "HUNTRESS - _SUMMARY",
265 + "dashboard_uid": "ab9bab2c-5d86-43e7-bac2-c1d68fc91342",
266 + "panel_width": 500,
267 + "panel_height": 300,
268 + },
269 + ],
270 + },
271 + ],
272 + ],
273 )
274
275
backend/app/connectors/graylog/schema/content_packs.py
+2 -1
@@ -3,7 +3,8 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9
10
backend/app/connectors/graylog/schema/monitoring.py
+14 -7
@@ -32,25 +32,32 @@ class GraylogThroughputMetrics(BaseModel):
32
33 class GraylogThroughputMetricsCollection(BaseModel):
34 graylog2_buffers_input_usage: Optional[str] = Field(
35 - None, alias="org.graylog2.buffers.input.usage",
35 + None,
36 + alias="org.graylog2.buffers.input.usage",
37 )
38 graylog2_buffers_output_usage: Optional[str] = Field(
38 - None, alias="org.graylog2.buffers.output.usage",
39 + None,
40 + alias="org.graylog2.buffers.output.usage",
41 )
42 graylog2_buffers_process_usage: Optional[str] = Field(
41 - None, alias="org.graylog2.buffers.process.usage",
43 + None,
44 + alias="org.graylog2.buffers.process.usage",
45 )
46 graylog2_throughput_input_1_sec_rate: Optional[str] = Field(
44 - None, alias="org.graylog2.throughput.input.1-sec-rate",
47 + None,
48 + alias="org.graylog2.throughput.input.1-sec-rate",
49 )
50 graylog2_throughput_output_1_sec_rate: Optional[str] = Field(
47 - None, alias="org.graylog2.throughput.output.1-sec-rate",
51 + None,
52 + alias="org.graylog2.throughput.output.1-sec-rate",
53 )
54 graylog2_throughput_output: Optional[str] = Field(
50 - None, alias="org.graylog2.throughput.output",
55 + None,
56 + alias="org.graylog2.throughput.output",
57 )
58 graylog2_throughput_input: Optional[str] = Field(
53 - None, alias="org.graylog2.throughput.input",
59 + None,
60 + alias="org.graylog2.throughput.input",
61 )
62
63
backend/app/connectors/influxdb/schema/alerts.py
+34 -27
@@ -2,7 +2,8 @@ from datetime import datetime
2 from enum import Enum
3 from typing import Optional
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7 from pydantic import Field
8
9
@@ -65,26 +66,29 @@ class InfluxDBAlertResponse(BaseModel):
66 filtered_count: int
67 active_alerts_count: int = 0
68 cleared_alerts_count: int = 0
68 - model_config = ConfigDict(from_attributes=True, json_schema_extra={
69 - "example": {
70 - "success": True,
71 - "message": "Successfully retrieved alerts",
72 - "alerts": [
73 - {
74 - "time": "2025-12-01T10:30:00Z",
75 - "check_name": "CPU CHECK",
76 - "sensor_type": "CPU",
77 - "severity": "warning",
78 - "message": "CPU usage high",
79 - "status": "active",
80 - },
81 - ],
82 - "total_count": 150,
83 - "filtered_count": 25,
84 - "active_alerts_count": 5,
85 - "cleared_alerts_count": 20,
69 + model_config = ConfigDict(
70 + from_attributes=True,
71 + json_schema_extra={
72 + "example": {
73 + "success": True,
74 + "message": "Successfully retrieved alerts",
75 + "alerts": [
76 + {
77 + "time": "2025-12-01T10:30:00Z",
78 + "check_name": "CPU CHECK",
79 + "sensor_type": "CPU",
80 + "severity": "warning",
81 + "message": "CPU usage high",
82 + "status": "active",
83 + },
84 + ],
85 + "total_count": 150,
86 + "filtered_count": 25,
87 + "active_alerts_count": 5,
88 + "cleared_alerts_count": 20,
89 + },
90 },
87 - })
91 + )
92
93
94 class InfluxDBAlertsResponse(BaseModel):
@@ -102,11 +106,14 @@ class InfluxDBCheckNamesResponse(BaseModel):
106 message: str
107 check_names: list[str]
108 total_count: int
105 - model_config = ConfigDict(from_attributes=True, json_schema_extra={
106 - "example": {
107 - "success": True,
108 - "message": "Successfully retrieved check names",
109 - "check_names": ["CPU CHECK", "Host Offline", "Memory Usage", "Disk Space"],
110 - "total_count": 4,
109 + model_config = ConfigDict(
110 + from_attributes=True,
111 + json_schema_extra={
112 + "example": {
113 + "success": True,
114 + "message": "Successfully retrieved check names",
115 + "check_names": ["CPU CHECK", "Host Offline", "Memory Usage", "Disk Space"],
116 + "total_count": 4,
117 + },
118 },
112 - })
119 + )
backend/app/connectors/schema.py
+2 -1
@@ -2,7 +2,8 @@ from datetime import datetime
2 from typing import List
3 from typing import Optional
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7
8
9 class ConnectorHistoryResponse(BaseModel):
backend/app/connectors/shuffle/schema/integrations.py
+7 -5
@@ -16,11 +16,13 @@ class IntegrationRequest(BaseModel):
16 fields: Optional[List[Dict[str, Any]]] = Field(
17 [],
18 description="The fields of the application",
19 - examples=[[
20 - {"key": "title", "value": "This is the title"},
21 - {"key": "description", "value": "This is the description"},
22 - {"key": "source", "value": "Shuffle"},
23 - ]],
19 + examples=[
20 + [
21 + {"key": "title", "value": "This is the title"},
22 + {"key": "description", "value": "This is the description"},
23 + {"key": "source", "value": "Shuffle"},
24 + ],
25 + ],
26 )
27 skip_workflow: Optional[bool] = Field(
28 False,
backend/app/connectors/sublime/schema/alerts.py
+2 -1
@@ -2,7 +2,8 @@ import datetime
2 from typing import List
3 from typing import Optional
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7 from pydantic import Field
8
9
backend/app/connectors/velociraptor/schema/artifacts.py
+28 -22
@@ -6,8 +6,10 @@ from typing import Optional
6 from typing import Union
7
8 from fastapi import HTTPException
9 -from pydantic import field_validator, ConfigDict, BaseModel
9 +from pydantic import BaseModel
10 +from pydantic import ConfigDict
11 from pydantic import Field
12 +from pydantic import field_validator
13
14
15 class ArtifactParameter(BaseModel):
@@ -46,19 +48,21 @@ class ArtifactParametersResponse(BaseModel):
48 parameter_prefix: str = Field(..., description="The prefix used for filtering")
49 matching_parameters: List[ArtifactParameter] = Field(default_factory=list, description="List of parameters that match the prefix")
50 total_matches: int = Field(..., description="Total number of matching parameters")
49 - model_config = ConfigDict(json_schema_extra={
50 - "example": {
51 - "success": True,
52 - "message": "Found 2 parameters matching prefix 'T1552.001'",
53 - "artifact_name": "Windows.AttackSimulation.AtomicRedTeam",
54 - "parameter_prefix": "T1552.001",
55 - "matching_parameters": [
56 - {"name": "T1552.001 - 3", "description": "Credentials In Files - Extracting passwords with findstr", "type": "bool"},
57 - {"name": "T1552.001 - 4", "description": "Credentials In Files - Access unattend.xml", "type": "bool"},
58 - ],
59 - "total_matches": 2,
51 + model_config = ConfigDict(
52 + json_schema_extra={
53 + "example": {
54 + "success": True,
55 + "message": "Found 2 parameters matching prefix 'T1552.001'",
56 + "artifact_name": "Windows.AttackSimulation.AtomicRedTeam",
57 + "parameter_prefix": "T1552.001",
58 + "matching_parameters": [
59 + {"name": "T1552.001 - 3", "description": "Credentials In Files - Extracting passwords with findstr", "type": "bool"},
60 + {"name": "T1552.001 - 4", "description": "Credentials In Files - Access unattend.xml", "type": "bool"},
61 + ],
62 + "total_matches": 2,
63 + },
64 },
61 - })
65 + )
66
67
68 class OSPrefixEnum(Enum):
@@ -139,16 +143,18 @@ class CollectArtifactBody(BaseBody):
143 False,
144 description="If true, only store the collected data in the datastore without sending it back immediately",
145 )
142 - model_config = ConfigDict(json_schema_extra={
143 - "example": {
144 - "hostname": "WIN-HFOU106TD7K",
145 - "velociraptor_id": "C.475df76785008b04",
146 - "velociraptor_org": "root",
147 - "artifact_name": "Windows.AttackSimulation.AtomicRedTeam",
148 - "parameters": {"env": [{"key": "InstallART", "value": "N"}, {"key": "T1552.001 - 3", "value": "Y"}]},
149 - "data_store_only": False,
146 + model_config = ConfigDict(
147 + json_schema_extra={
148 + "example": {
149 + "hostname": "WIN-HFOU106TD7K",
150 + "velociraptor_id": "C.475df76785008b04",
151 + "velociraptor_org": "root",
152 + "artifact_name": "Windows.AttackSimulation.AtomicRedTeam",
153 + "parameters": {"env": [{"key": "InstallART", "value": "N"}, {"key": "T1552.001 - 3", "value": "Y"}]},
154 + "data_store_only": False,
155 + },
156 },
151 - })
157 + )
158
159
160 class InvokeCopilotActionBody(BaseModel):
backend/app/connectors/velociraptor/schema/flows.py
+2 -1
@@ -3,8 +3,9 @@ from typing import Optional
3
4 from fastapi import HTTPException
5 from loguru import logger
6 -from pydantic import model_validator, BaseModel
6 +from pydantic import BaseModel
7 from pydantic import Field
8 +from pydantic import model_validator
9
10
11 class FlowSpecParameter(BaseModel):
backend/app/connectors/wazuh_indexer/schema/alerts.py
+2 -1
@@ -4,8 +4,9 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import field_validator, BaseModel
7 +from pydantic import BaseModel
8 from pydantic import Field
9 +from pydantic import field_validator
10
11
12 class Alert(BaseModel):
backend/app/connectors/wazuh_manager/schema/groups.py
+8 -5
@@ -2,7 +2,8 @@ from typing import List
2 from typing import Optional
3 from typing import Union
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7 from pydantic import Field
8
9
@@ -59,9 +60,10 @@ class WazuhGroupConfigurationUpdateRequest(BaseModel):
60 """Request model for updating group configuration."""
61
62 configuration: str = Field(..., description="Full valid XML configuration content")
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "configuration": """<agent_config>
63 + model_config = ConfigDict(
64 + json_schema_extra={
65 + "example": {
66 + "configuration": """<agent_config>
67 <labels>
68 <label key="customer">example</label>
69 </labels>
@@ -71,8 +73,9 @@ class WazuhGroupConfigurationUpdateRequest(BaseModel):
73 <events_per_second>1000</events_per_second>
74 </client_buffer>
75 </agent_config>""",
76 + },
77 },
75 - })
78 + )
79
80
81 class WazuhGroupConfigurationUpdateResponse(BaseModel):
backend/app/connectors/wazuh_manager/schema/mitre.py
+2 -1
@@ -3,7 +3,8 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9
10
backend/app/connectors/wazuh_manager/schema/rules.py
+3 -1
@@ -3,8 +3,10 @@ from typing import Optional
3 from typing import Union
4
5 from fastapi import HTTPException
6 -from pydantic import field_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import field_validator
10
11
12 class RuleDisable(BaseModel):
backend/app/customer_portal/schema/settings.py
+7 -2
@@ -3,8 +3,10 @@ import re
3 from typing import Optional
4
5 from fastapi import HTTPException
6 -from pydantic import field_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import field_validator
10
11
12 class UpdatePortalSettingsRequest(BaseModel):
@@ -52,7 +54,10 @@ class UpdatePortalSettingsRequest(BaseModel):
54 if v not in allowed_types:
55 raise HTTPException(status_code=400, detail=f"Invalid MIME type. Allowed types are: {', '.join(allowed_types)}")
56 return v
55 - model_config = ConfigDict(json_schema_extra={"example": {"title": "My Custom Portal", "logo_base64": "iVBORw0KGgoAAAANS...", "logo_mime_type": "image/png"}})
57 +
58 + model_config = ConfigDict(
59 + json_schema_extra={"example": {"title": "My Custom Portal", "logo_base64": "iVBORw0KGgoAAAANS...", "logo_mime_type": "image/png"}},
60 + )
61
62
63 class PortalSettingsData(BaseModel):
backend/app/customer_provisioning/schema/graylog.py
+71 -64
@@ -1,7 +1,8 @@
1 from typing import List
2 from typing import Optional
3
4 -from pydantic import ConfigDict, BaseModel
4 +from pydantic import BaseModel
5 +from pydantic import ConfigDict
6 from pydantic import Field
7
8
@@ -32,31 +33,33 @@ class TimeBasedIndexSet(BaseModel):
33 index_optimization_disabled: bool
34 writable: bool
35 field_type_refresh_interval: int
35 - model_config = ConfigDict(json_schema_extra={
36 - "example": {
37 - "title": "Wazuh - Example Company",
38 - "description": "Wazuh - Example Company",
39 - "index_prefix": "wazuh-examplecode",
40 - "rotation_strategy_class": "org.graylog2.indexer.rotation.strategies.SizeBasedRotationStrategy",
41 - "rotation_strategy": {
42 - "type": "org.graylog2.indexer.rotation.strategies.SizeBasedRotationStrategyConfig",
43 - "max_size": 2684354560,
44 - },
45 - "retention_strategy_class": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategy",
46 - "retention_strategy": {
47 - "type": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategyConfig",
48 - "max_number_of_indices": 20,
36 + model_config = ConfigDict(
37 + json_schema_extra={
38 + "example": {
39 + "title": "Wazuh - Example Company",
40 + "description": "Wazuh - Example Company",
41 + "index_prefix": "wazuh-examplecode",
42 + "rotation_strategy_class": "org.graylog2.indexer.rotation.strategies.SizeBasedRotationStrategy",
43 + "rotation_strategy": {
44 + "type": "org.graylog2.indexer.rotation.strategies.SizeBasedRotationStrategyConfig",
45 + "max_size": 2684354560,
46 + },
47 + "retention_strategy_class": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategy",
48 + "retention_strategy": {
49 + "type": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategyConfig",
50 + "max_number_of_indices": 20,
51 + },
52 + "creation_date": "2021-01-01T00:00:00.000Z",
53 + "index_analyzer": "standard",
54 + "shards": 1,
55 + "replicas": 0,
56 + "index_optimization_max_num_segments": 1,
57 + "index_optimization_disabled": False,
58 + "writable": True,
59 + "field_type_refresh_interval": 5000,
60 },
50 - "creation_date": "2021-01-01T00:00:00.000Z",
51 - "index_analyzer": "standard",
52 - "shards": 1,
53 - "replicas": 0,
54 - "index_optimization_max_num_segments": 1,
55 - "index_optimization_disabled": False,
56 - "writable": True,
57 - "field_type_refresh_interval": 5000,
61 },
59 - })
62 + )
63
64
65 class RotationStrategyConfig(BaseModel):
@@ -124,24 +127,26 @@ class WazuhEventStream(BaseModel):
127 None,
128 description="Associated content pack, if any",
129 )
127 - model_config = ConfigDict(json_schema_extra={
128 - "example": {
129 - "title": "WAZUH EVENTS CUSTOMERS - Example Company",
130 - "description": "WAZUH EVENTS CUSTOMERS - Example Company",
131 - "index_set_id": "12345",
132 - "rules": [
133 - {
134 - "field": "agent_labels_customer",
135 - "type": 1,
136 - "inverted": False,
137 - "value": "ExampleCode",
138 - },
139 - ],
140 - "matching_type": "AND",
141 - "remove_matches_from_default_stream": True,
142 - "content_pack": None,
130 + model_config = ConfigDict(
131 + json_schema_extra={
132 + "example": {
133 + "title": "WAZUH EVENTS CUSTOMERS - Example Company",
134 + "description": "WAZUH EVENTS CUSTOMERS - Example Company",
135 + "index_set_id": "12345",
136 + "rules": [
137 + {
138 + "field": "agent_labels_customer",
139 + "type": 1,
140 + "inverted": False,
141 + "value": "ExampleCode",
142 + },
143 + ],
144 + "matching_type": "AND",
145 + "remove_matches_from_default_stream": True,
146 + "content_pack": None,
147 + },
148 },
144 - })
149 + )
150
151
152 class Office365EventStream(BaseModel):
@@ -158,30 +163,32 @@ class Office365EventStream(BaseModel):
163 None,
164 description="Associated content pack, if any",
165 )
161 - model_config = ConfigDict(json_schema_extra={
162 - "example": {
163 - "title": "Office365 EVENTS - Example Company",
164 - "description": "Office365 EVENTS - Example Company",
165 - "index_set_id": "12345",
166 - "rules": [
167 - {
168 - "field": "agent_labels_customer",
169 - "type": 1,
170 - "inverted": False,
171 - "value": "ExampleCode",
172 - },
173 - {
174 - "field": "agent_labels_integration",
175 - "type": 1,
176 - "inverted": False,
177 - "value": "Office365",
178 - },
179 - ],
180 - "matching_type": "AND",
181 - "remove_matches_from_default_stream": True,
182 - "content_pack": None,
166 + model_config = ConfigDict(
167 + json_schema_extra={
168 + "example": {
169 + "title": "Office365 EVENTS - Example Company",
170 + "description": "Office365 EVENTS - Example Company",
171 + "index_set_id": "12345",
172 + "rules": [
173 + {
174 + "field": "agent_labels_customer",
175 + "type": 1,
176 + "inverted": False,
177 + "value": "ExampleCode",
178 + },
179 + {
180 + "field": "agent_labels_integration",
181 + "type": 1,
182 + "inverted": False,
183 + "value": "Office365",
184 + },
185 + ],
186 + "matching_type": "AND",
187 + "remove_matches_from_default_stream": True,
188 + "content_pack": None,
189 + },
190 },
184 - })
191 + )
192
193
194 class StreamData(BaseModel):
backend/app/customer_provisioning/schema/provision.py
+38 -33
@@ -3,8 +3,9 @@ from enum import Enum
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import field_validator, BaseModel
6 +from pydantic import BaseModel
7 from pydantic import Field
8 +from pydantic import field_validator
9
10 from app.connectors.grafana.schema.dashboards import DashboardProvisionRequest
11 from app.db.universal_models import CustomersMeta
@@ -59,11 +60,13 @@ class ProvisionNewCustomer(BaseModel):
60 dashboards_to_include: DashboardProvisionRequest = Field(
61 ...,
62 description="Dashboards to include in the customer's Grafana instance",
62 - examples=[{
63 - "dashboards": [
64 - "WAZUH_SUMMARY",
65 - ],
66 - }],
63 + examples=[
64 + {
65 + "dashboards": [
66 + "WAZUH_SUMMARY",
67 + ],
68 + },
69 + ],
70 )
71 wazuh_auth_password: Optional[str] = Field("n/a", description="Password for the Wazuh API user")
72 wazuh_registration_port: Optional[str] = Field(
@@ -224,33 +227,35 @@ class ProvisionDashboardRequest(BaseModel):
227 dashboards_to_include: DashboardProvisionRequest = Field(
228 ...,
229 description="Dashboards to include in the customer's Grafana instance",
227 - examples=[{
228 - "dashboards": [
229 - "WAZUH_SUMMARY",
230 - "EDR_WINDOWS_EVENT_LOGS",
231 - "EDR_WAZUH_INVENOTRY",
232 - "EDR_USERS_AND_GROUPS",
233 - "EDR_SYSTEM_VULNERABILITIES",
234 - "EDR_SYSTEM_SECURITY_AUDIT",
235 - "EDR_SYSTEM_PROCESSES",
236 - "EDR_PROCESS_INJECTION",
237 - "EDR_OPEN_AUDIT",
238 - "EDR_NETWORK_SCAN",
239 - "EDR_NETWORK_CONNECTIONS",
240 - "EDR_MITRE",
241 - "EDR_FIM",
242 - "EDR_DOCKER_MONITORING",
243 - "EDR_DNS_REQUESTS",
244 - "EDR_DLL_SIDE_LOADING",
245 - "EDR_COMPLIANCE",
246 - "EDR_AV_MALWARE_IOC",
247 - "EDR_AGENT_INVENTORY",
248 - "EDR_AD_INVENOTRY",
249 - ],
250 - "organizationId": 1,
251 - "folderId": 1,
252 - "datasourceUid": "wazuh",
253 - }],
230 + examples=[
231 + {
232 + "dashboards": [
233 + "WAZUH_SUMMARY",
234 + "EDR_WINDOWS_EVENT_LOGS",
235 + "EDR_WAZUH_INVENOTRY",
236 + "EDR_USERS_AND_GROUPS",
237 + "EDR_SYSTEM_VULNERABILITIES",
238 + "EDR_SYSTEM_SECURITY_AUDIT",
239 + "EDR_SYSTEM_PROCESSES",
240 + "EDR_PROCESS_INJECTION",
241 + "EDR_OPEN_AUDIT",
242 + "EDR_NETWORK_SCAN",
243 + "EDR_NETWORK_CONNECTIONS",
244 + "EDR_MITRE",
245 + "EDR_FIM",
246 + "EDR_DOCKER_MONITORING",
247 + "EDR_DNS_REQUESTS",
248 + "EDR_DLL_SIDE_LOADING",
249 + "EDR_COMPLIANCE",
250 + "EDR_AV_MALWARE_IOC",
251 + "EDR_AGENT_INVENTORY",
252 + "EDR_AD_INVENOTRY",
253 + ],
254 + "organizationId": 1,
255 + "folderId": 1,
256 + "datasourceUid": "wazuh",
257 + },
258 + ],
259 )
260 grafana_org_id: int = Field(
261 ...,
backend/app/customers/schema/customers.py
+44 -34
@@ -2,7 +2,8 @@ from datetime import datetime
2 from typing import List
3 from typing import Optional
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7 from pydantic import Field
8
9
@@ -26,24 +27,27 @@ class CustomerRequestBody(BaseModel):
27 customer_type: Optional[str] = Field(None, description="Type of the customer")
28 logo_file: Optional[str] = Field(None, description="Logo file for the customer")
29 is_provisioned: Optional[bool] = Field(None, description="Whether the customer has been provisioned")
29 - model_config = ConfigDict(from_attributes=True, json_schema_extra={
30 - "example": {
31 - "customer_code": "CUST123",
32 - "customer_name": "Sample Customer",
33 - "contact_last_name": "Doe",
34 - "contact_first_name": "John",
35 - "phone": "123-456-7890",
36 - "address_line1": "123 Main St",
37 - "address_line2": "Apt 4",
38 - "city": "Anytown",
39 - "state": "CA",
40 - "postal_code": "12345",
41 - "country": "USA",
42 - "customer_type": "Enterprise",
43 - "logo_file": "logo.png",
44 - "is_provisioned": True,
30 + model_config = ConfigDict(
31 + from_attributes=True,
32 + json_schema_extra={
33 + "example": {
34 + "customer_code": "CUST123",
35 + "customer_name": "Sample Customer",
36 + "contact_last_name": "Doe",
37 + "contact_first_name": "John",
38 + "phone": "123-456-7890",
39 + "address_line1": "123 Main St",
40 + "address_line2": "Apt 4",
41 + "city": "Anytown",
42 + "state": "CA",
43 + "postal_code": "12345",
44 + "country": "USA",
45 + "customer_type": "Enterprise",
46 + "logo_file": "logo.png",
47 + "is_provisioned": True,
48 + },
49 },
46 - })
50 + )
51
52
53 class CustomerResponse(BaseModel):
@@ -100,18 +104,21 @@ class CustomerMetaRequestBody(BaseModel):
104 None,
105 description="Portainer stack ID for the customer",
106 )
103 - model_config = ConfigDict(from_attributes=True, json_schema_extra={
104 - "example": {
105 - "customer_meta_graylog_index": "graylog_index",
106 - "customer_meta_graylog_stream": "graylog_stream",
107 - "customer_meta_grafana_org_id": "grafana_org",
108 - "customer_meta_wazuh_group": "wazuh_group",
109 - "customer_meta_index_retention": "30D",
110 - "customer_meta_wazuh_registration_port": "1514",
111 - "customer_meta_wazuh_log_ingestion_port": "1515",
112 - "customer_meta_wazuh_auth_password": "wazuh_password",
107 + model_config = ConfigDict(
108 + from_attributes=True,
109 + json_schema_extra={
110 + "example": {
111 + "customer_meta_graylog_index": "graylog_index",
112 + "customer_meta_graylog_stream": "graylog_stream",
113 + "customer_meta_grafana_org_id": "grafana_org",
114 + "customer_meta_wazuh_group": "wazuh_group",
115 + "customer_meta_index_retention": "30D",
116 + "customer_meta_wazuh_registration_port": "1514",
117 + "customer_meta_wazuh_log_ingestion_port": "1515",
118 + "customer_meta_wazuh_auth_password": "wazuh_password",
119 + },
120 },
114 - })
121 + )
122
123
124 class CustomerMetaResponse(BaseModel):
@@ -159,9 +166,12 @@ class DeleteCustomerResponse(BaseModel):
166
167 success: bool
168 message: str
162 - model_config = ConfigDict(from_attributes=True, json_schema_extra={
163 - "example": {
164 - "success": True,
165 - "message": "Customer 'customer_code' deleted successfully",
169 + model_config = ConfigDict(
170 + from_attributes=True,
171 + json_schema_extra={
172 + "example": {
173 + "success": True,
174 + "message": "Customer 'customer_code' deleted successfully",
175 + },
176 },
167 - })
177 + )
backend/app/data_store/data_store_schema.py
+2 -1
@@ -1,7 +1,8 @@
1 from datetime import datetime
2 from typing import Optional
3
4 -from pydantic import ConfigDict, BaseModel
4 +from pydantic import BaseModel
5 +from pydantic import ConfigDict
6 from pydantic import Field
7
8
backend/app/healthchecks/agents/schema/agents.py
+3 -1
@@ -4,8 +4,10 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import field_validator, ConfigDict, BaseModel
7 +from pydantic import BaseModel
8 +from pydantic import ConfigDict
9 from pydantic import Field
10 +from pydantic import field_validator
11
12
13 class AgentModel(BaseModel):
backend/app/incidents/models.py
+20 -5
@@ -245,7 +245,10 @@ class VeloSigmaExclusion(SQLModel, table=True):
245 title: Optional[str] = Field(max_length=255, nullable=True, description="Sigma rule title to match (exact match)")
246
247 # Field matching data - stored as JSON to allow flexible field matching
248 - field_matches: Optional[Dict] = Field(sa_column=Column(JSON, nullable=True), description="JSON of field names and values to match in the event data")
248 + field_matches: Optional[Dict] = Field(
249 + sa_column=Column(JSON, nullable=True),
250 + description="JSON of field names and values to match in the event data",
251 + )
252
253 # Metadata
254 customer_code: Optional[str] = Field(
@@ -271,7 +274,10 @@ class ThresholdAlertMetadata(SQLModel, table=True):
274 replay_query: str = Field(sa_column=Column(Text, nullable=False), description="Lucene query from Graylog replay_info")
275 timerange_start: datetime = Field(nullable=False, description="Start of the threshold evaluation window")
276 timerange_end: datetime = Field(nullable=False, description="End of the threshold evaluation window")
274 - group_by_fields: Optional[Dict] = Field(sa_column=Column(JSON, nullable=True), description="Group-by field key/value pairs from the threshold event")
277 + group_by_fields: Optional[Dict] = Field(
278 + sa_column=Column(JSON, nullable=True),
279 + description="Group-by field key/value pairs from the threshold event",
280 + )
281 source_streams: Optional[List] = Field(sa_column=Column(JSON, nullable=True), description="Graylog source stream IDs")
282 source: str = Field(max_length=50, nullable=False, description="SOURCE field value (e.g. wazuh)")
283 resolved_index_name: str = Field(max_length=255, nullable=False, description="OpenSearch index of the resolved event")
@@ -327,7 +333,10 @@ class CaseTemplateTask(SQLModel, table=True):
333 template_id: int = Field(foreign_key="incident_management_case_template.id", nullable=False)
334 title: str = Field(max_length=500, nullable=False)
335 description: Optional[str] = Field(sa_column=Column(Text, nullable=True))
330 - guidelines: Optional[str] = Field(sa_column=Column(Text, nullable=True), description="Best practices / steps the analyst should follow when executing this task")
336 + guidelines: Optional[str] = Field(
337 + sa_column=Column(Text, nullable=True),
338 + description="Best practices / steps the analyst should follow when executing this task",
339 + )
340 mandatory: bool = Field(
341 default=False,
342 nullable=False,
@@ -374,7 +383,10 @@ class CaseTask(SQLModel, table=True):
383 nullable=False,
384 description="One of TODO, DONE, NOT_NECESSARY (NOT_NECESSARY only valid when mandatory=False).",
385 )
377 - evidence_comment: Optional[str] = Field(sa_column=Column(Text, nullable=True), description="Free-form notes / evidence (logs, command output) attached when status changes.")
386 + evidence_comment: Optional[str] = Field(
387 + sa_column=Column(Text, nullable=True),
388 + description="Free-form notes / evidence (logs, command output) attached when status changes.",
389 + )
390 completed_by: Optional[str] = Field(max_length=100, nullable=True)
391 completed_at: Optional[datetime] = Field(default=None, nullable=True)
392
@@ -408,7 +420,10 @@ class CaseEvent(SQLModel, table=True):
420 )
421 actor: str = Field(max_length=100, nullable=False, description="user_name that performed the action")
422 timestamp: datetime = Field(default_factory=datetime.utcnow, index=True)
411 - payload: Optional[Dict] = Field(sa_column=Column(JSON, nullable=True), description="Event-type-specific JSON payload (e.g., from_status/to_status, alert_id, task_id).")
423 + payload: Optional[Dict] = Field(
424 + sa_column=Column(JSON, nullable=True),
425 + description="Event-type-specific JSON payload (e.g., from_status/to_status, alert_id, task_id).",
426 + )
427
428
429 class TagAccessSettings(SQLModel, table=True):
backend/app/incidents/schema/case_templates.py
+3 -1
@@ -14,8 +14,10 @@ from typing import Dict
14 from typing import List
15 from typing import Optional
16
17 -from pydantic import field_validator, ConfigDict, BaseModel
17 +from pydantic import BaseModel
18 +from pydantic import ConfigDict
19 from pydantic import Field
20 +from pydantic import field_validator
21
22 # ---------------------------------------------------------------------------
23 # Enums
backend/app/incidents/schema/db_operations.py
+2 -1
@@ -5,7 +5,8 @@ from typing import List
5 from typing import Optional
6
7 from fastapi import HTTPException
8 -from pydantic import field_validator, BaseModel
8 +from pydantic import BaseModel
9 +from pydantic import field_validator
10 from pydantic import model_validator
11
12 from app.incidents.models import Alert
backend/app/incidents/schema/incident_alert.py
+13 -10
@@ -5,7 +5,8 @@ from typing import Dict
5 from typing import List
6 from typing import Optional
7
8 -from pydantic import ConfigDict, BaseModel
8 +from pydantic import BaseModel
9 +from pydantic import ConfigDict
10 from pydantic import Field
11
12
@@ -55,16 +56,18 @@ class AutoCreateAlertResponse(BaseModel):
56 alerts_failed: int = 0
57 batches_processed: int = 0
58 alerts_remaining: int = 0
58 - model_config = ConfigDict(json_schema_extra={
59 - "example": {
60 - "success": True,
61 - "message": "Processed 5 batches: 487 alerts created, 13 failed. 2000 alerts remaining for next run",
62 - "alerts_created": 487,
63 - "alerts_failed": 13,
64 - "batches_processed": 5,
65 - "alerts_remaining": 2000,
59 + model_config = ConfigDict(
60 + json_schema_extra={
61 + "example": {
62 + "success": True,
63 + "message": "Processed 5 batches: 487 alerts created, 13 failed. 2000 alerts remaining for next run",
64 + "alerts_created": 487,
65 + "alerts_failed": 13,
66 + "batches_processed": 5,
67 + "alerts_remaining": 2000,
68 + },
69 },
67 - })
70 + )
71
72
73 class IndexNamesResponse(BaseModel):
backend/app/incidents/schema/velo_sigma.py
+74 -66
@@ -7,8 +7,10 @@ from typing import Optional
7 from typing import Union
8
9 from loguru import logger
10 -from pydantic import field_validator, ConfigDict, BaseModel
10 +from pydantic import BaseModel
11 +from pydantic import ConfigDict
12 from pydantic import Field
13 +from pydantic import field_validator
14
15
16 class SystemProvider(BaseModel):
@@ -146,6 +148,7 @@ class PowerShellEventData(BaseModel):
148 # Generic event data model that accepts any fields
149 class GenericEventData(BaseModel):
150 """Generic event data structure that accepts any fields"""
151 +
152 model_config = ConfigDict(extra="allow")
153
154
@@ -307,62 +310,65 @@ class VelociraptorSigmaAlert(BaseModel):
310
311 # Use generic model for other event types
312 return GenericEvent(**event_data)
310 - model_config = ConfigDict(json_schema_extra={
311 - "example": {
312 - "computer": "WIN-HFOU106TD7K",
313 - "clientID": "C.475df76785008b04",
314 - "channel": "Microsoft-Windows-Sysmon/Operational",
315 - "title": "Proc Access (Sysmon Alert)",
316 - "level": "high",
317 - "event": (
318 - '{"System":{"Provider":{"Name":"Microsoft-Windows-Sysmon","Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9"},'
319 - '"EventID":{"Value":10},"Version":3,"Level":4,"Task":10,"Opcode":0,"Keywords":9223372036854775808,'
320 - '"TimeCreated":{"SystemTime":1744233485.0778975},"EventRecordID":564617,"Correlation":{},'
321 - '"Execution":{"ProcessID":2320,"ThreadID":3540},"Channel":"Microsoft-Windows-Sysmon/Operational",'
322 - '"Computer":"WIN-HFOU106TD7K","Security":{"UserID":"S-1-5-18"}},"EventData":{"RuleName":"technique_id=T1003,'
323 - 'technique_name=Credential Dumping","UtcTime":"2025-04-09 21:18:05.064",'
324 - '"SourceProcessGUID":"691FF406-E40B-67F6-2901-000000003A00","SourceProcessId":4964,"SourceThreadId":4448,'
325 - '"SourceImage":"C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe",'
326 - '"TargetProcessGUID":"691FF406-DDC8-67F6-0C00-000000003A00","TargetProcessId":668,'
327 - '"TargetImage":"C:\\\\Windows\\\\system32\\\\lsass.exe","GrantedAccess":2097151,'
328 - '"CallTrace":"C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9fc24|C:\\\\Windows\\\\System32\\\\wow64.dll+3cf4|'
329 - "C:\\\\Windows\\\\System32\\\\wow64.dll+7783|C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1783|"
330 - "C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1199|C:\\\\Windows\\\\System32\\\\wow64.dll+cfda|"
331 - "C:\\\\Windows\\\\System32\\\\wow64.dll+cea0|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+757db|"
332 - "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+756c3|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7566e|"
333 - "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7070c(wow64)|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+10eca8(wow64)|"
334 - "C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe+876e|"
335 - "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL+20419(wow64)|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+6662d(wow64)|"
336 - 'C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+665fd(wow64)","SourceUser":"WIN-HFOU106TD7K\\\\Administrator",'
337 - '"TargetUser":"NT AUTHORITY\\\\SYSTEM"},'
338 - '"Message":"Process accessed:\\nRuleName: technique_id=T1003,technique_name=Credential Dumping!s!\\n'
339 - "UtcTime: 2025-04-09 21:18:05.064!s!\\n"
340 - "SourceProcessGUID: 691FF406-E40B-67F6-2901-000000003A00!s!\\n"
341 - "SourceProcessId: 4964!s!\\n"
342 - "SourceThreadId: 4448!s!\\n"
343 - "SourceImage: C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe!s!\\n"
344 - "TargetProcessGUID: 691FF406-DDC8-67F6-0C00-000000003A00!s!\\n"
345 - "TargetProcessId: 668!s!\\n"
346 - "TargetImage: C:\\\\Windows\\\\system32\\\\lsass.exe!s!\\n"
347 - "GrantedAccess: 2097151!s!\\n"
348 - "CallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9fc24|C:\\\\Windows\\\\System32\\\\wow64.dll+3cf4|"
349 - "C:\\\\Windows\\\\System32\\\\wow64.dll+7783|C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1783|"
350 - "C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1199|C:\\\\Windows\\\\System32\\\\wow64.dll+cfda|"
351 - "C:\\\\Windows\\\\System32\\\\wow64.dll+cea0|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+757db|"
352 - "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+756c3|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7566e|"
353 - "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7070c(wow64)|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+10eca8(wow64)|"
354 - "C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe+876e|"
355 - "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL+20419(wow64)|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+6662d(wow64)|"
356 - "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+665fd(wow64)!s!\\n"
357 - "SourceUser: WIN-HFOU106TD7K\\\\Administrator!s!\\n"
358 - 'TargetUser: NT AUTHORITY\\\\SYSTEM!s!\\r\\n"}'
359 - ),
360 - "type": "sigma-alert",
361 - "source": "velociraptor",
362 - "index_pattern": "wazuh-*",
363 - "sourceRef": "754600692",
313 +
314 + model_config = ConfigDict(
315 + json_schema_extra={
316 + "example": {
317 + "computer": "WIN-HFOU106TD7K",
318 + "clientID": "C.475df76785008b04",
319 + "channel": "Microsoft-Windows-Sysmon/Operational",
320 + "title": "Proc Access (Sysmon Alert)",
321 + "level": "high",
322 + "event": (
323 + '{"System":{"Provider":{"Name":"Microsoft-Windows-Sysmon","Guid":"5770385F-C22A-43E0-BF4C-06F5698FFBD9"},'
324 + '"EventID":{"Value":10},"Version":3,"Level":4,"Task":10,"Opcode":0,"Keywords":9223372036854775808,'
325 + '"TimeCreated":{"SystemTime":1744233485.0778975},"EventRecordID":564617,"Correlation":{},'
326 + '"Execution":{"ProcessID":2320,"ThreadID":3540},"Channel":"Microsoft-Windows-Sysmon/Operational",'
327 + '"Computer":"WIN-HFOU106TD7K","Security":{"UserID":"S-1-5-18"}},"EventData":{"RuleName":"technique_id=T1003,'
328 + 'technique_name=Credential Dumping","UtcTime":"2025-04-09 21:18:05.064",'
329 + '"SourceProcessGUID":"691FF406-E40B-67F6-2901-000000003A00","SourceProcessId":4964,"SourceThreadId":4448,'
330 + '"SourceImage":"C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe",'
331 + '"TargetProcessGUID":"691FF406-DDC8-67F6-0C00-000000003A00","TargetProcessId":668,'
332 + '"TargetImage":"C:\\\\Windows\\\\system32\\\\lsass.exe","GrantedAccess":2097151,'
333 + '"CallTrace":"C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9fc24|C:\\\\Windows\\\\System32\\\\wow64.dll+3cf4|'
334 + "C:\\\\Windows\\\\System32\\\\wow64.dll+7783|C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1783|"
335 + "C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1199|C:\\\\Windows\\\\System32\\\\wow64.dll+cfda|"
336 + "C:\\\\Windows\\\\System32\\\\wow64.dll+cea0|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+757db|"
337 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+756c3|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7566e|"
338 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7070c(wow64)|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+10eca8(wow64)|"
339 + "C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe+876e|"
340 + "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL+20419(wow64)|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+6662d(wow64)|"
341 + 'C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+665fd(wow64)","SourceUser":"WIN-HFOU106TD7K\\\\Administrator",'
342 + '"TargetUser":"NT AUTHORITY\\\\SYSTEM"},'
343 + '"Message":"Process accessed:\\nRuleName: technique_id=T1003,technique_name=Credential Dumping!s!\\n'
344 + "UtcTime: 2025-04-09 21:18:05.064!s!\\n"
345 + "SourceProcessGUID: 691FF406-E40B-67F6-2901-000000003A00!s!\\n"
346 + "SourceProcessId: 4964!s!\\n"
347 + "SourceThreadId: 4448!s!\\n"
348 + "SourceImage: C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe!s!\\n"
349 + "TargetProcessGUID: 691FF406-DDC8-67F6-0C00-000000003A00!s!\\n"
350 + "TargetProcessId: 668!s!\\n"
351 + "TargetImage: C:\\\\Windows\\\\system32\\\\lsass.exe!s!\\n"
352 + "GrantedAccess: 2097151!s!\\n"
353 + "CallTrace: C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+9fc24|C:\\\\Windows\\\\System32\\\\wow64.dll+3cf4|"
354 + "C:\\\\Windows\\\\System32\\\\wow64.dll+7783|C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1783|"
355 + "C:\\\\Windows\\\\System32\\\\wow64cpu.dll+1199|C:\\\\Windows\\\\System32\\\\wow64.dll+cfda|"
356 + "C:\\\\Windows\\\\System32\\\\wow64.dll+cea0|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+757db|"
357 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+756c3|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7566e|"
358 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+7070c(wow64)|C:\\\\Windows\\\\System32\\\\KERNELBASE.dll+10eca8(wow64)|"
359 + "C:\\\\Users\\\\ADMINI~1\\\\AppData\\\\Local\\\\Temp\\\\2\\\\AttackSim\\\\procdump.exe+876e|"
360 + "C:\\\\Windows\\\\System32\\\\KERNEL32.DLL+20419(wow64)|C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+6662d(wow64)|"
361 + "C:\\\\Windows\\\\SYSTEM32\\\\ntdll.dll+665fd(wow64)!s!\\n"
362 + "SourceUser: WIN-HFOU106TD7K\\\\Administrator!s!\\n"
363 + 'TargetUser: NT AUTHORITY\\\\SYSTEM!s!\\r\\n"}'
364 + ),
365 + "type": "sigma-alert",
366 + "source": "velociraptor",
367 + "index_pattern": "wazuh-*",
368 + "sourceRef": "754600692",
369 + },
370 },
365 - })
371 + )
372
373
374 class VelociraptorSigmaAlertResponse(BaseModel):
@@ -392,17 +398,19 @@ class VeloSigmaExclusionCreate(VeloSigmaExclusionBase):
398
399 # Make created_by optional so it can be set by the server
400 created_by: Optional[str] = Field(None, description="User who created this exclusion rule")
395 - model_config = ConfigDict(json_schema_extra={
396 - "example": {
397 - "name": "Chainsaw Batch Script Exclusion",
398 - "description": "Exclude alerts from chainsaw batch scripts in Windows Temp folder",
399 - "channel": "Microsoft-Windows-Sysmon/Operational",
400 - "title": "HackTool - Powerup Write Hijack DLL",
401 - "field_matches": {"TargetFilename": "C:\\Windows\\Temp\\chainsaw_batch.bat"},
402 - "customer_code": None, # Optional, NULL means apply to all customers
403 - "enabled": True,
401 + model_config = ConfigDict(
402 + json_schema_extra={
403 + "example": {
404 + "name": "Chainsaw Batch Script Exclusion",
405 + "description": "Exclude alerts from chainsaw batch scripts in Windows Temp folder",
406 + "channel": "Microsoft-Windows-Sysmon/Operational",
407 + "title": "HackTool - Powerup Write Hijack DLL",
408 + "field_matches": {"TargetFilename": "C:\\Windows\\Temp\\chainsaw_batch.bat"},
409 + "customer_code": None, # Optional, NULL means apply to all customers
410 + "enabled": True,
411 + },
412 },
405 - })
413 + )
414
415
416 class VeloSigmaExclusionUpdate(BaseModel):
backend/app/integrations/alert_escalation/schema/escalate_alert.py
+4 -2
@@ -4,7 +4,8 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import ConfigDict, BaseModel
7 +from pydantic import BaseModel
8 +from pydantic import ConfigDict
9 from pydantic import Field
10
11
@@ -175,7 +176,8 @@ class IrisAlertContext(BaseModel):
176 )
177 alert_level: int = Field(..., description="Severity level of the alert", examples=[3])
178 process_name: Optional[List[str]] = Field(
178 - None, examples=[["No process name found"]],
179 + None,
180 + examples=[["No process name found"]],
181 description="Name of the process",
182 )
183 model_config = ConfigDict(extra="allow")
backend/app/integrations/bitdefender/schema/provision.py
+2 -1
@@ -2,8 +2,9 @@ from typing import Any
2 from typing import Dict
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class ProvisionBitdefenderRequest(BaseModel):
backend/app/integrations/carbonblack/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionCarbonBlackRequest(BaseModel):
@@ -59,27 +61,29 @@ class CarbonBlackEventStream(BaseModel):
61 None,
62 description="Associated content pack, if any",
63 )
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "title": "CarbonBlack SIEM EVENTS - Example Company",
65 - "description": "CarbonBlack SIEM EVENTS - Example Company",
66 - "index_set_id": "12345",
67 - "rules": [
68 - {
69 - "field": "customer_code",
70 - "type": 1,
71 - "inverted": False,
72 - "value": "ExampleCode",
73 - },
74 - {
75 - "field": "integration",
76 - "type": 1,
77 - "inverted": False,
78 - "value": "huntress",
79 - },
80 - ],
81 - "matching_type": "AND",
82 - "remove_matches_from_default_stream": True,
83 - "content_pack": None,
64 + model_config = ConfigDict(
65 + json_schema_extra={
66 + "example": {
67 + "title": "CarbonBlack SIEM EVENTS - Example Company",
68 + "description": "CarbonBlack SIEM EVENTS - Example Company",
69 + "index_set_id": "12345",
70 + "rules": [
71 + {
72 + "field": "customer_code",
73 + "type": 1,
74 + "inverted": False,
75 + "value": "ExampleCode",
76 + },
77 + {
78 + "field": "integration",
79 + "type": 1,
80 + "inverted": False,
81 + "value": "huntress",
82 + },
83 + ],
84 + "matching_type": "AND",
85 + "remove_matches_from_default_stream": True,
86 + "content_pack": None,
87 + },
88 },
85 - })
89 + )
backend/app/integrations/cato/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionCatoRequest(BaseModel):
@@ -59,27 +61,29 @@ class CatoEventStream(BaseModel):
61 None,
62 description="Associated content pack, if any",
63 )
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "title": "Cato SIEM EVENTS - Example Company",
65 - "description": "Cato SIEM EVENTS - Example Company",
66 - "index_set_id": "12345",
67 - "rules": [
68 - {
69 - "field": "customer_code",
70 - "type": 1,
71 - "inverted": False,
72 - "value": "ExampleCode",
73 - },
74 - {
75 - "field": "integration",
76 - "type": 1,
77 - "inverted": False,
78 - "value": "cato",
79 - },
80 - ],
81 - "matching_type": "AND",
82 - "remove_matches_from_default_stream": True,
83 - "content_pack": None,
64 + model_config = ConfigDict(
65 + json_schema_extra={
66 + "example": {
67 + "title": "Cato SIEM EVENTS - Example Company",
68 + "description": "Cato SIEM EVENTS - Example Company",
69 + "index_set_id": "12345",
70 + "rules": [
71 + {
72 + "field": "customer_code",
73 + "type": 1,
74 + "inverted": False,
75 + "value": "ExampleCode",
76 + },
77 + {
78 + "field": "integration",
79 + "type": 1,
80 + "inverted": False,
81 + "value": "cato",
82 + },
83 + ],
84 + "matching_type": "AND",
85 + "remove_matches_from_default_stream": True,
86 + "content_pack": None,
87 + },
88 },
85 - })
89 + )
backend/app/integrations/copilot_action/schema/copilot_action.py
+2 -1
@@ -6,8 +6,9 @@ from typing import List
6 from typing import Optional
7 from typing import Union
8
9 -from pydantic import field_validator, BaseModel
9 +from pydantic import BaseModel
10 from pydantic import Field
11 +from pydantic import field_validator
12 from pydantic import model_validator
13
14
backend/app/integrations/copilot_mcp/schema/copilot_mcp.py
+2 -1
@@ -5,8 +5,9 @@ from typing import List
5 from typing import Optional
6
7 from fastapi import HTTPException
8 -from pydantic import field_validator, BaseModel
8 +from pydantic import BaseModel
9 from pydantic import Field
10 +from pydantic import field_validator
11
12
13 class MCPServerType(str, Enum):
backend/app/integrations/copilot_searches/schema/copilot_searches.py
+14 -10
@@ -157,12 +157,14 @@ class ExecuteSearchRequest(BaseModel):
157 parameters: dict[str, Any] = Field(
158 default_factory=dict,
159 description="Parameter values to substitute in the query",
160 - examples=[{
161 - "AGENT_NAME": "my-server",
162 - "CUSTOMER_CODE": "lab",
163 - "START_TIME": "now-24h",
164 - "END_TIME": "now",
165 - }],
160 + examples=[
161 + {
162 + "AGENT_NAME": "my-server",
163 + "CUSTOMER_CODE": "lab",
164 + "START_TIME": "now-24h",
165 + "END_TIME": "now",
166 + },
167 + ],
168 )
169 size: Optional[int] = Field(
170 default=None,
@@ -226,10 +228,12 @@ class ExecuteGraylogQueryRequest(BaseModel):
228 parameters: dict[str, Any] = Field(
229 default_factory=dict,
230 description="Parameter values to substitute in the query",
229 - examples=[{
230 - "AGENT_NAME": "my-server",
231 - "CUSTOMER_CODE": "lab",
232 - }],
231 + examples=[
232 + {
233 + "AGENT_NAME": "my-server",
234 + "CUSTOMER_CODE": "lab",
235 + },
236 + ],
237 )
238
239
backend/app/integrations/crowdstrike/schema/provision.py
+2 -1
@@ -2,8 +2,9 @@ from typing import Any
2 from typing import Dict
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class ProvisionCrowdstrikeRequest(BaseModel):
backend/app/integrations/darktrace/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionDarktraceRequest(BaseModel):
@@ -59,27 +61,29 @@ class DarktraceEventStream(BaseModel):
61 None,
62 description="Associated content pack, if any",
63 )
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "title": "Darktrace SIEM EVENTS - Example Company",
65 - "description": "Darktrace SIEM EVENTS - Example Company",
66 - "index_set_id": "12345",
67 - "rules": [
68 - {
69 - "field": "customer_code",
70 - "type": 1,
71 - "inverted": False,
72 - "value": "ExampleCode",
73 - },
74 - {
75 - "field": "integration",
76 - "type": 1,
77 - "inverted": False,
78 - "value": "darktrace",
79 - },
80 - ],
81 - "matching_type": "AND",
82 - "remove_matches_from_default_stream": True,
83 - "content_pack": None,
64 + model_config = ConfigDict(
65 + json_schema_extra={
66 + "example": {
67 + "title": "Darktrace SIEM EVENTS - Example Company",
68 + "description": "Darktrace SIEM EVENTS - Example Company",
69 + "index_set_id": "12345",
70 + "rules": [
71 + {
72 + "field": "customer_code",
73 + "type": 1,
74 + "inverted": False,
75 + "value": "ExampleCode",
76 + },
77 + {
78 + "field": "integration",
79 + "type": 1,
80 + "inverted": False,
81 + "value": "darktrace",
82 + },
83 + ],
84 + "matching_type": "AND",
85 + "remove_matches_from_default_stream": True,
86 + "content_pack": None,
87 + },
88 },
85 - })
89 + )
backend/app/integrations/defender_for_endpoint/schema/provision.py
+2 -1
@@ -2,8 +2,9 @@ from typing import Any
2 from typing import Dict
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class ProvisionDefenderForEndpointRequest(BaseModel):
backend/app/integrations/duo/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionDuoRequest(BaseModel):
@@ -59,27 +61,29 @@ class DuoEventStream(BaseModel):
61 None,
62 description="Associated content pack, if any",
63 )
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "title": "Duo SIEM EVENTS - Example Company",
65 - "description": "Duo SIEM EVENTS - Example Company",
66 - "index_set_id": "12345",
67 - "rules": [
68 - {
69 - "field": "customer_code",
70 - "type": 1,
71 - "inverted": False,
72 - "value": "ExampleCode",
73 - },
74 - {
75 - "field": "integration",
76 - "type": 1,
77 - "inverted": False,
78 - "value": "huntress",
79 - },
80 - ],
81 - "matching_type": "AND",
82 - "remove_matches_from_default_stream": True,
83 - "content_pack": None,
64 + model_config = ConfigDict(
65 + json_schema_extra={
66 + "example": {
67 + "title": "Duo SIEM EVENTS - Example Company",
68 + "description": "Duo SIEM EVENTS - Example Company",
69 + "index_set_id": "12345",
70 + "rules": [
71 + {
72 + "field": "customer_code",
73 + "type": 1,
74 + "inverted": False,
75 + "value": "ExampleCode",
76 + },
77 + {
78 + "field": "integration",
79 + "type": 1,
80 + "inverted": False,
81 + "value": "huntress",
82 + },
83 + ],
84 + "matching_type": "AND",
85 + "remove_matches_from_default_stream": True,
86 + "content_pack": None,
87 + },
88 },
85 - })
89 + )
backend/app/integrations/github_audit/model.py
+8 -2
@@ -58,7 +58,10 @@ class GitHubAuditConfig(SQLModel, table=True):
58 default="all",
59 description="'all', 'include', or 'exclude'",
60 )
61 - repo_filter_list: Optional[List[str]] = Field(sa_column=Column(JSON, nullable=True), description="List of repos to include/exclude based on filter_mode")
61 + repo_filter_list: Optional[List[str]] = Field(
62 + sa_column=Column(JSON, nullable=True),
63 + description="List of repos to include/exclude based on filter_mode",
64 + )
65
66 # Notification settings
67 notify_on_critical: bool = Field(default=True, description="Send notification on critical findings")
@@ -209,7 +212,10 @@ class GitHubAuditBaseline(SQLModel, table=True):
212 description: Optional[str] = Field(sa_column=Column(Text, nullable=True))
213
214 # Expected values
212 - expected_checks: Optional[Dict] = Field(sa_column=Column(JSON, nullable=True), description="Expected check results by check_id: {check_id: expected_status}")
215 + expected_checks: Optional[Dict] = Field(
216 + sa_column=Column(JSON, nullable=True),
217 + description="Expected check results by check_id: {check_id: expected_status}",
218 + )
219
220 # Baseline from a previous report
221 baseline_report_id: Optional[int] = Field(
backend/app/integrations/huntress/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionHuntressRequest(BaseModel):
@@ -59,27 +61,29 @@ class HuntressEventStream(BaseModel):
61 None,
62 description="Associated content pack, if any",
63 )
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "title": "Huntress SIEM EVENTS - Example Company",
65 - "description": "Huntress SIEM EVENTS - Example Company",
66 - "index_set_id": "12345",
67 - "rules": [
68 - {
69 - "field": "customer_code",
70 - "type": 1,
71 - "inverted": False,
72 - "value": "ExampleCode",
73 - },
74 - {
75 - "field": "integration",
76 - "type": 1,
77 - "inverted": False,
78 - "value": "huntress",
79 - },
80 - ],
81 - "matching_type": "AND",
82 - "remove_matches_from_default_stream": True,
83 - "content_pack": None,
64 + model_config = ConfigDict(
65 + json_schema_extra={
66 + "example": {
67 + "title": "Huntress SIEM EVENTS - Example Company",
68 + "description": "Huntress SIEM EVENTS - Example Company",
69 + "index_set_id": "12345",
70 + "rules": [
71 + {
72 + "field": "customer_code",
73 + "type": 1,
74 + "inverted": False,
75 + "value": "ExampleCode",
76 + },
77 + {
78 + "field": "integration",
79 + "type": 1,
80 + "inverted": False,
81 + "value": "huntress",
82 + },
83 + ],
84 + "matching_type": "AND",
85 + "remove_matches_from_default_stream": True,
86 + "content_pack": None,
87 + },
88 },
85 - })
89 + )
backend/app/integrations/mimecast/schema/mimecast.py
+3 -1
@@ -9,9 +9,11 @@ from typing import Dict
9 from typing import List
10 from typing import Optional
11
12 -from pydantic import model_validator, ConfigDict, BaseModel
12 +from pydantic import BaseModel
13 +from pydantic import ConfigDict
14 from pydantic import Field
15 from pydantic import HttpUrl
16 +from pydantic import model_validator
17
18
19 class PipelineRuleTitles(Enum):
backend/app/integrations/mimecast/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionMimecastRequest(BaseModel):
@@ -54,27 +56,29 @@ class MimecastEventStream(BaseModel):
56 None,
57 description="Associated content pack, if any",
58 )
57 - model_config = ConfigDict(json_schema_extra={
58 - "example": {
59 - "title": "Mimecast EVENTS - Example Company",
60 - "description": "Mimecast EVENTS - Example Company",
61 - "index_set_id": "12345",
62 - "rules": [
63 - {
64 - "field": "agent_labels_customer",
65 - "type": 1,
66 - "inverted": False,
67 - "value": "ExampleCode",
68 - },
69 - {
70 - "field": "agent_labels_integration",
71 - "type": 1,
72 - "inverted": False,
73 - "value": "Office365",
74 - },
75 - ],
76 - "matching_type": "AND",
77 - "remove_matches_from_default_stream": True,
78 - "content_pack": None,
59 + model_config = ConfigDict(
60 + json_schema_extra={
61 + "example": {
62 + "title": "Mimecast EVENTS - Example Company",
63 + "description": "Mimecast EVENTS - Example Company",
64 + "index_set_id": "12345",
65 + "rules": [
66 + {
67 + "field": "agent_labels_customer",
68 + "type": 1,
69 + "inverted": False,
70 + "value": "ExampleCode",
71 + },
72 + {
73 + "field": "agent_labels_integration",
74 + "type": 1,
75 + "inverted": False,
76 + "value": "Office365",
77 + },
78 + ],
79 + "matching_type": "AND",
80 + "remove_matches_from_default_stream": True,
81 + "content_pack": None,
82 + },
83 },
80 - })
84 + )
backend/app/integrations/modules/schema/carbonblack.py
+2 -1
@@ -1,8 +1,9 @@
1 from typing import Optional
2
3 from fastapi import HTTPException
4 -from pydantic import field_validator, BaseModel
4 +from pydantic import BaseModel
5 from pydantic import Field
6 +from pydantic import field_validator
7
8
9 class InvokeCarbonBlackRequest(BaseModel):
backend/app/integrations/modules/schema/cato.py
+2 -1
@@ -1,6 +1,7 @@
1 from fastapi import HTTPException
2 -from pydantic import field_validator, BaseModel
2 +from pydantic import BaseModel
3 from pydantic import Field
4 +from pydantic import field_validator
5
6
7 class InvokeCatoRequest(BaseModel):
backend/app/integrations/modules/schema/darktrace.py
+2 -1
@@ -1,6 +1,7 @@
1 from fastapi import HTTPException
2 -from pydantic import field_validator, BaseModel
2 +from pydantic import BaseModel
3 from pydantic import Field
4 +from pydantic import field_validator
5
6
7 class InvokeDarktraceRequest(BaseModel):
backend/app/integrations/modules/schema/duo.py
+2 -1
@@ -1,6 +1,7 @@
1 from fastapi import HTTPException
2 -from pydantic import field_validator, BaseModel
2 +from pydantic import BaseModel
3 from pydantic import Field
4 +from pydantic import field_validator
5
6
7 class InvokeDuoRequest(BaseModel):
backend/app/integrations/modules/schema/huntress.py
+2 -1
@@ -1,6 +1,7 @@
1 from fastapi import HTTPException
2 -from pydantic import field_validator, BaseModel
2 +from pydantic import BaseModel
3 from pydantic import Field
4 +from pydantic import field_validator
5
6
7 class InvokeHuntressRequest(BaseModel):
backend/app/integrations/modules/schema/mimecast.py
+2 -1
@@ -1,8 +1,9 @@
1 from typing import Optional
2
3 from fastapi import HTTPException
4 -from pydantic import field_validator, BaseModel
4 +from pydantic import BaseModel
5 from pydantic import Field
6 +from pydantic import field_validator
7
8
9 class InvokeMimecastRequest(BaseModel):
backend/app/integrations/modules/schema/sap_siem.py
+2 -1
@@ -2,8 +2,9 @@ from datetime import datetime
2 from datetime import timedelta
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class InvokeSapSiemRequest(BaseModel):
backend/app/integrations/modules/services/sap_siem/collect.py
+3 -1
@@ -148,7 +148,9 @@ async def post_to_copilot_sap_module_brute_force_failed_logins_same_ip(data: Inv
148 Args:
149 data (InvokeSapSiemAnalysis): The data to send to the copilot-sap-module Docker container.
150 """
151 - logger.info(f"Sending POST request to http://copilot-sap-module/sap-siem/brute_force_failed_logins_same_ip with data: {data.model_dump()}")
151 + logger.info(
152 + f"Sending POST request to http://copilot-sap-module/sap-siem/brute_force_failed_logins_same_ip with data: {data.model_dump()}",
153 + )
154 async with httpx.AsyncClient() as client:
155 try:
156 response = await client.post(
backend/app/integrations/monitoring_alert/schema/provision.py
+10 -7
@@ -4,8 +4,9 @@ from typing import List
4 from typing import Optional
5
6 from fastapi import HTTPException
7 -from pydantic import field_validator, BaseModel
7 +from pydantic import BaseModel
8 from pydantic import Field
9 +from pydantic import field_validator
10
11
12 class AvailableMonitoringAlerts(str, Enum):
@@ -451,12 +452,14 @@ class CustomMonitoringAlertProvisionModel(BaseModel):
452 "have a pipeline rule that sets the SYSLOG_LEVEL field to ALERT when "
453 "the Wazuh rule level is greater than 11."
454 ),
454 - examples=[(
455 - "This alert monitors the SYSLOG_LEVEL field in the Wazuh logs. When "
456 - "the level is ALERT, it triggers an alert that is created within "
457 - "DFIR-IRIS. Ensure that you have a pipeline rule that sets the "
458 - "SYSLOG_LEVEL field to ALERT when the Wazuh rule level is greater than 11."
459 - )],
455 + examples=[
456 + (
457 + "This alert monitors the SYSLOG_LEVEL field in the Wazuh logs. When "
458 + "the level is ALERT, it triggers an alert that is created within "
459 + "DFIR-IRIS. Ensure that you have a pipeline rule that sets the "
460 + "SYSLOG_LEVEL field to ALERT when the Wazuh rule level is greater than 11."
461 + ),
462 + ],
463 )
464 alert_priority: AlertPriority = Field(
465 ...,
backend/app/integrations/monitoring_alert/services/provision.py
+8 -4
@@ -1127,7 +1127,8 @@ async def provision_fortinet_wids_wireless_eapol_packet_flooding_detected_monito
1127 Provisions Fortinet WIDS Wireless EAPOL Packet Flooding Detected monitoring alert.
1128 """
1129 logger.info(
1130 - "Invoking provision_fortinet_wids_wireless_eapol_packet_flooding_detected_monitoring_alert " f"with request: {request.model_dump()}",
1130 + "Invoking provision_fortinet_wids_wireless_eapol_packet_flooding_detected_monitoring_alert "
1131 + f"with request: {request.model_dump()}",
1132 )
1133 await provision_alert_definition(
1134 GraylogAlertProvisionModel(
@@ -2432,7 +2433,8 @@ async def provision_sentinelone_new_mitigation_kill_performed_successfully_monit
2433 Provisions SentinelOne: New Mitigation, Kill performed successfully.
2434 """
2435 logger.info(
2435 - "Invoking provision_sentinelone_new_mitigation_kill_performed_successfully_monitoring_alert " f"with request: {request.model_dump()}",
2436 + "Invoking provision_sentinelone_new_mitigation_kill_performed_successfully_monitoring_alert "
2437 + f"with request: {request.model_dump()}",
2438 )
2439 await provision_alert_definition(
2440 GraylogAlertProvisionModel(
@@ -2547,7 +2549,8 @@ async def provision_sentinelone_new_exclusion_was_added_or_modified_by_user_moni
2549 Provisions SentinelOne: New Exclusion was added/modified by user.
2550 """
2551 logger.info(
2550 - "Invoking provision_sentinelone_new_exclusion_was_added_or_modified_by_user_monitoring_alert " f"with request: {request.model_dump()}",
2552 + "Invoking provision_sentinelone_new_exclusion_was_added_or_modified_by_user_monitoring_alert "
2553 + f"with request: {request.model_dump()}",
2554 )
2555 await provision_alert_definition(
2556 GraylogAlertProvisionModel(
@@ -2718,7 +2721,8 @@ async def provision_sentinelone_analyst_verdict_changed_to_false_positive_monito
2721 Provisions SentinelOne: Analyst verdict changed to False Positive.
2722 """
2723 logger.info(
2721 - "Invoking provision_sentinelone_analyst_verdict_changed_to_false_positive_monitoring_alert " f"with request: {request.model_dump()}",
2724 + "Invoking provision_sentinelone_analyst_verdict_changed_to_false_positive_monitoring_alert "
2725 + f"with request: {request.model_dump()}",
2726 )
2727 await provision_alert_definition(
2728 GraylogAlertProvisionModel(
backend/app/integrations/office365/schema/provision.py
+2 -1
@@ -2,8 +2,9 @@ from enum import Enum
2 from typing import Any
3 from typing import Dict
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class PipelineRuleTitles(Enum):
backend/app/integrations/sap_siem/schema/provision.py
+28 -24
@@ -3,8 +3,10 @@ from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import model_validator, ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9 +from pydantic import model_validator
10
11
12 class ProvisionSapSiemRequest(BaseModel):
@@ -59,27 +61,29 @@ class SapSiemEventStream(BaseModel):
61 None,
62 description="Associated content pack, if any",
63 )
62 - model_config = ConfigDict(json_schema_extra={
63 - "example": {
64 - "title": "SAP SIEM EVENTS - Example Company",
65 - "description": "SAP SIEM EVENTS - Example Company",
66 - "index_set_id": "12345",
67 - "rules": [
68 - {
69 - "field": "customer_code",
70 - "type": 1,
71 - "inverted": False,
72 - "value": "ExampleCode",
73 - },
74 - {
75 - "field": "integration",
76 - "type": 1,
77 - "inverted": False,
78 - "value": "sap_siem",
79 - },
80 - ],
81 - "matching_type": "AND",
82 - "remove_matches_from_default_stream": True,
83 - "content_pack": None,
64 + model_config = ConfigDict(
65 + json_schema_extra={
66 + "example": {
67 + "title": "SAP SIEM EVENTS - Example Company",
68 + "description": "SAP SIEM EVENTS - Example Company",
69 + "index_set_id": "12345",
70 + "rules": [
71 + {
72 + "field": "customer_code",
73 + "type": 1,
74 + "inverted": False,
75 + "value": "ExampleCode",
76 + },
77 + {
78 + "field": "integration",
79 + "type": 1,
80 + "inverted": False,
81 + "value": "sap_siem",
82 + },
83 + ],
84 + "matching_type": "AND",
85 + "remove_matches_from_default_stream": True,
86 + "content_pack": None,
87 + },
88 },
85 - })
89 + )
backend/app/integrations/sap_siem/schema/sap_siem.py
+2 -1
@@ -5,8 +5,9 @@ from typing import Dict
5 from typing import List
6 from typing import Optional
7
8 -from pydantic import model_validator, BaseModel
8 +from pydantic import BaseModel
9 from pydantic import Field
10 +from pydantic import model_validator
11
12
13 class InvokeSapSiemRequest(BaseModel):
backend/app/integrations/schema.py
+2 -1
@@ -1,7 +1,8 @@
1 from typing import List
2 from typing import Optional
3
4 -from pydantic import ConfigDict, BaseModel
4 +from pydantic import BaseModel
5 +from pydantic import ConfigDict
6 from pydantic import Field
7
8
backend/app/integrations/utils/schema.py
+2 -1
@@ -1,7 +1,8 @@
1 from typing import List
2 from typing import Optional
3
4 -from pydantic import ConfigDict, BaseModel
4 +from pydantic import BaseModel
5 +from pydantic import ConfigDict
6 from pydantic import Field
7
8
backend/app/network_connectors/schema.py
+2 -1
@@ -1,7 +1,8 @@
1 from typing import List
2 from typing import Optional
3
4 -from pydantic import ConfigDict, BaseModel
4 +from pydantic import BaseModel
5 +from pydantic import ConfigDict
6 from pydantic import Field
7
8
backend/app/notifications/schema/notifications.py
+3 -1
@@ -15,8 +15,10 @@ from enum import Enum
15 from typing import List
16 from typing import Optional
17
18 -from pydantic import field_validator, ConfigDict, BaseModel
18 +from pydantic import BaseModel
19 +from pydantic import ConfigDict
20 from pydantic import Field
21 +from pydantic import field_validator
22 from pydantic import model_validator
23
24 # ---------------------------------------------------------------------------
backend/app/siem/schema/dashboards.py
+2 -1
@@ -4,7 +4,8 @@ from typing import Dict
4 from typing import List
5 from typing import Optional
6
7 -from pydantic import ConfigDict, BaseModel
7 +from pydantic import BaseModel
8 +from pydantic import ConfigDict
9 from pydantic import Field
10
11 # ── Template browsing (read from disk) ───────────────────────────
backend/app/siem/schema/event_sources.py
+2 -1
@@ -3,7 +3,8 @@ from enum import Enum
3 from typing import List
4 from typing import Optional
5
6 -from pydantic import ConfigDict, BaseModel
6 +from pydantic import BaseModel
7 +from pydantic import ConfigDict
8 from pydantic import Field
9
10
backend/app/stack_provisioning/graylog/schema/fortinet.py
+2 -1
@@ -2,8 +2,9 @@ from typing import Any
2 from typing import Dict
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class ProvisionFortinetRequest(BaseModel):
backend/app/stack_provisioning/graylog/schema/provision.py
+6 -4
@@ -69,10 +69,12 @@ class ContentPack(BaseModel):
69 class AvailableContentPacksResponse(BaseModel):
70 available_content_packs: List[ContentPack] = Field(
71 ...,
72 - examples=[{
73 - "name": AvailableContentPacks.SOCFORTRESS_WAZUH_CONTENT_PACK.name,
74 - "description": AvailableContentPacks.SOCFORTRESS_WAZUH_CONTENT_PACK.value,
75 - }],
72 + examples=[
73 + {
74 + "name": AvailableContentPacks.SOCFORTRESS_WAZUH_CONTENT_PACK.name,
75 + "description": AvailableContentPacks.SOCFORTRESS_WAZUH_CONTENT_PACK.value,
76 + },
77 + ],
78 description="The available content packs for provisioning in Graylog",
79 )
80 success: bool = Field(
backend/app/stack_provisioning/graylog/schema/sentinelone.py
+2 -1
@@ -2,8 +2,9 @@ from typing import Any
2 from typing import Dict
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class ProvisionSentinelOneRequest(BaseModel):
backend/app/stack_provisioning/graylog/schema/sonicwall.py
+2 -1
@@ -2,8 +2,9 @@ from typing import Any
2 from typing import Dict
3 from typing import Optional
4
5 -from pydantic import model_validator, BaseModel
5 +from pydantic import BaseModel
6 from pydantic import Field
7 +from pydantic import model_validator
8
9
10 class ProvisionSonicwallRequest(BaseModel):
backend/app/threat_intel/schema/socfortress.py
+2 -1
@@ -4,8 +4,9 @@ from typing import List
4 from typing import Optional
5
6 from fastapi import HTTPException
7 -from pydantic import field_validator, BaseModel
7 +from pydantic import BaseModel
8 from pydantic import Field
9 +from pydantic import field_validator
10
11
12 class SocfortressThreatIntelRequest(BaseModel):
backend/app/threat_intel/schema/virustotal.py
+2 -1
@@ -2,7 +2,8 @@ from typing import Dict
2 from typing import List
3 from typing import Optional
4
5 -from pydantic import ConfigDict, BaseModel
5 +from pydantic import BaseModel
6 +from pydantic import ConfigDict
7 from pydantic import Field
8
9
backend/app/utils.py
+2 -1
@@ -15,8 +15,9 @@ from fastapi import Request
15 from fastapi import Security
16 from fastapi.exceptions import RequestValidationError
17 from loguru import logger
18 -from pydantic import field_validator, BaseModel
18 +from pydantic import BaseModel
19 from pydantic import Field
20 +from pydantic import field_validator
21 from pydantic import model_validator
22 from sqlalchemy.ext.asyncio import AsyncSession
23 from sqlalchemy.future import select
backend/app/version/schema/version.py
+15 -12
@@ -1,6 +1,7 @@
1 from typing import Optional
2
3 -from pydantic import ConfigDict, BaseModel
3 +from pydantic import BaseModel
4 +from pydantic import ConfigDict
5
6
7 class VersionCheckResponse(BaseModel):
@@ -12,15 +13,17 @@ class VersionCheckResponse(BaseModel):
13 release_url: Optional[str] = None
14 release_notes: Optional[str] = None
15 published_at: Optional[str] = None
15 - model_config = ConfigDict(json_schema_extra={
16 - "example": {
17 - "success": True,
18 - "message": "New version v0.1.5 available!",
19 - "current_version": "0.1.4",
20 - "latest_version": "0.1.5",
21 - "is_outdated": True,
22 - "release_url": "https://github.com/socfortress/CoPilot/releases/tag/v0.1.5",
23 - "release_notes": "## What's Changed\n* Feature 1\n* Feature 2",
24 - "published_at": "2025-12-03T18:48:20Z",
16 + model_config = ConfigDict(
17 + json_schema_extra={
18 + "example": {
19 + "success": True,
20 + "message": "New version v0.1.5 available!",
21 + "current_version": "0.1.4",
22 + "latest_version": "0.1.5",
23 + "is_outdated": True,
24 + "release_url": "https://github.com/socfortress/CoPilot/releases/tag/v0.1.5",
25 + "release_notes": "## What's Changed\n* Feature 1\n* Feature 2",
26 + "published_at": "2025-12-03T18:48:20Z",
27 + },
28 },
26 - })
29 + )
backend/copilot.py
+1
@@ -100,6 +100,7 @@ environment = os.getenv("ENVIRONMENT", "PRODUCTION")
100 # ssl_keyfile = os.path.join(os.path.dirname(__file__), "../nginx/server.key")
101 # ssl_certfile = os.path.join(os.path.dirname(__file__), "../nginx/server.crt")
102
103 +
104 @asynccontextmanager
105 async def lifespan(_app: FastAPI):
106 # ── startup ──
backend/requirements.txt
+1 -1
@@ -145,8 +145,8 @@ pyasn1==0.6.3
145 pyasn1-modules==0.4.2
146 pycparser==3.0
147 pycryptodome==3.23.0
148 -pydantic[email]==2.13.4
148 pydantic-core==2.46.4
149 +pydantic[email]==2.13.4
150 pydo==0.34.0
151 pyee==13.0.1
152 pygments==2.20.0