main
py 303 lines 9.07 KB
Raw
1 import re
2 from enum import Enum
3 from typing import List
4 from typing import Optional
5
6 from fastapi import HTTPException
7 from pydantic import BaseModel
8 from pydantic import Field
9 from pydantic import field_validator
10
11
12 class SocfortressThreatIntelRequest(BaseModel):
13 ioc_value: str
14 customer_code: Optional[str] = Field(
15 "socfortress_copilot",
16 description="The customer code for the customer",
17 )
18
19
20 class VirusTotalThreatIntelRequest(BaseModel):
21 ioc_value: str
22
23
24 class IoCMapping(BaseModel):
25 comment: Optional[str] = Field(None, description="Comment about the IOCs")
26 ioc_source: str = Field(
27 "SOCFortress Threat Intel",
28 description="Identifier for the source of the IOC",
29 )
30 report_url: Optional[str] = Field(None, description="URL for the related report")
31 score: Optional[int] = Field(
32 None,
33 description="Score indicating the severity or importance",
34 )
35 timestamp: Optional[str] = Field(None, description="Timestamp for the data")
36 type: Optional[str] = Field(
37 None,
38 description="Type of indicator, e.g., Domain-Name",
39 )
40 value: Optional[str] = Field(None, description="The actual value of the indicator")
41 virustotal_url: Optional[str] = Field(
42 None,
43 description="URL to the VirusTotal report",
44 )
45
46 @field_validator("score", mode="before")
47 @classmethod
48 def convert_score_to_int(cls, v):
49 """Convert score from string to integer"""
50 if v is None:
51 return None
52 if isinstance(v, str):
53 try:
54 # Convert string to float first, then to int to handle "100.0" format
55 return int(float(v))
56 except (ValueError, TypeError):
57 # If conversion fails, return None or raise an error
58 return None
59 elif isinstance(v, (int, float)):
60 return int(v)
61 return v
62
63 def to_dict(self):
64 return self.model_dump()
65
66
67 class IoCResponse(BaseModel):
68 data: Optional[IoCMapping] = Field(None, description="The data for the IoC")
69 success: bool = Field(..., description="Indicates if it was successful")
70 message: Optional[str] = Field(None, description="Message about the IoC")
71
72 def to_dict(self):
73 return self.model_dump()
74
75
76 class SocfortressProcessNameAnalysisRequest(BaseModel):
77 process_name: str = Field(
78 ...,
79 description="The process name to evaluate.",
80 )
81
82 @field_validator("process_name", mode="before")
83 @classmethod
84 def extract_filename(cls, v):
85 match = re.search(r"[^\\]+$", v)
86 return match.group() if match else v
87
88
89 class SyslogType(str, Enum):
90 WAZUH = "wazuh"
91 FORTINET = "fortinet"
92 # Add other valid syslog types here if needed
93
94
95 class SocfortressAiAlertRequest(BaseModel):
96 integration: str = Field(..., examples=["SOCFORTRESS AI"])
97 alert_payload: dict = Field(..., examples=[{"alert": "test"}])
98
99 @field_validator("integration")
100 @classmethod
101 def check_integration(cls, v):
102 if v != "SOCFORTRESS AI":
103 raise HTTPException(
104 status_code=400,
105 detail="Invalid integration. Only 'SOCFORTRESS AI' is supported.",
106 )
107 return v
108
109 @field_validator("alert_payload")
110 @classmethod
111 def check_syslog_type(cls, v):
112 if v.get("syslog_type") not in SyslogType.__members__.values():
113 raise HTTPException(
114 status_code=400,
115 detail=f"Invalid syslog_type. Only {', '.join([e.value for e in SyslogType])} are supported.",
116 )
117 # Remove 'message' and 'full_log' fields if they exist
118 v.pop("message", None)
119 v.pop("full_log", None)
120 v.pop("gl2_processing_error", None)
121 v.pop("gl2_accounted_message_size", None)
122 v.pop("gl2_source_input", None)
123 v.pop("gl2_remote_ip", None)
124 v.pop("gl2_message_id", None)
125 v.pop("gl2_remote_port", None)
126 return v
127
128
129 class SocfortressAiAlertResponse(BaseModel):
130 message: str
131 success: bool
132 analysis: str = Field(description="The analysis of the alert.")
133 base64_decoded: Optional[str] = None
134 confidence_score: float = Field(
135 description="Confidence score for the response.",
136 ge=0,
137 le=1,
138 )
139 threat_indicators: Optional[str] = Field(
140 default=None,
141 description="The threat indicators that make the decoded payload potentially malicious.",
142 )
143
144 risk_evaluation: Optional[str] = Field(
145 default=None,
146 description="A conclusion indicating whether the content is `low`, `medium`, or `high` risk.",
147 )
148
149
150 class SocfortressAiWazuhExclusionRuleResponse(BaseModel):
151 message: str
152 success: bool
153 wazuh_exclusion_rule: Optional[str] = Field(
154 default=None,
155 description="The rule that was excluded from the analysis in XML format.",
156 )
157 wazuh_exclusion_rule_justification: Optional[str] = Field(
158 default=None,
159 description="The justification for excluding the rule and the reason for selecting the field names that were selected to include within the exclusion rule.",
160 )
161
162
163 class Path(BaseModel):
164 directory: str
165 percentage: float
166
167
168 class ProcessInfo(BaseModel):
169 name: str
170 percentage: float
171
172
173 class HashInfo(BaseModel):
174 hash: str
175 percentage: float
176
177
178 class NetworkInfo(BaseModel):
179 port: str
180 usage: float
181
182
183 class TagInfo(BaseModel):
184 category: str
185 type: str
186 description: str
187 field4: Optional[str] = None
188 field5: Optional[str] = None
189 color: str
190
191
192 class TruncatedInfo(BaseModel):
193 paths: int
194 parents: int
195 grandparents: int
196 children: int
197 network: int
198 hashes: int
199
200
201 class SocfortressProcessNameAnalysisAPIResponse(BaseModel):
202 rank: int
203 host_prev: str
204 eps: str
205 paths: List[Path]
206 parents: List[ProcessInfo]
207 hashes: List[HashInfo]
208 network: List[NetworkInfo]
209 description: str
210 intel: str
211 truncated: TruncatedInfo
212 tags: Optional[List[TagInfo]] = None
213
214
215 class SocfortressProcessNameAnalysisResponse(BaseModel):
216 success: bool
217 message: str
218 data: SocfortressProcessNameAnalysisAPIResponse
219
220 def to_dict(self):
221 return self.model_dump()
222
223
224 class Artifacts(BaseModel):
225 description: str = Field(..., description="Description of the artifact.")
226 name: str = Field(..., description="Name of the artifact.")
227
228
229 class OS(str, Enum):
230 Windows = "Windows"
231 Linux = "Linux"
232 MacOS = "MacOS"
233
234
235 class VelociraptorArtifactRecommendationRequest(BaseModel):
236 integration: str = Field(..., examples=["SOCFORTRESS AI"])
237 artifacts: Optional[List[Artifacts]] = Field(
238 None,
239 description="List of artifacts to recommend.",
240 )
241 os: OS = Field(..., description="The operating system of the endpoint.")
242 alert_payload: dict = Field(..., examples=[{"alert": "test"}])
243
244 @field_validator("integration")
245 @classmethod
246 def check_integration(cls, v):
247 if v != "SOCFORTRESS AI":
248 raise HTTPException(
249 status_code=400,
250 detail="Invalid integration. Only 'SOCFORTRESS AI' is supported.",
251 )
252 return v
253
254 @field_validator("alert_payload")
255 @classmethod
256 def check_syslog_type(cls, v):
257 if v.get("syslog_type") != "wazuh":
258 raise HTTPException(
259 status_code=400,
260 detail="Invalid syslog_type. Only 'wazuh' is supported.",
261 )
262 # Remove 'message' and 'full_log' fields if they exist
263 v.pop("message", None)
264 v.pop("full_log", None)
265 v.pop("gl2_processing_error", None)
266 v.pop("gl2_accounted_message_size", None)
267 v.pop("gl2_source_input", None)
268 v.pop("gl2_remote_ip", None)
269 v.pop("gl2_message_id", None)
270 v.pop("gl2_remote_port", None)
271 return v
272
273
274 class VelociraptorArtifactRecommendation(BaseModel):
275 name: str = Field(..., description="The name of the artifact.")
276 description: str = Field(..., description="A description of the artifact.")
277 explanation: str = Field(
278 ...,
279 description="A detailed explanation of the purpose and why the artifact was selected.",
280 )
281
282
283 class AiVelociraptorArtifactsRecommendationModel(BaseModel):
284 artifact_recommendations: List[VelociraptorArtifactRecommendation] = Field(
285 description="The recommended artifacts which detail the name, description, and explanation of why the artifact was selected.",
286 )
287 general_thoughts: str = Field(
288 description="General thoughts on the artifacts and why they were selected.",
289 )
290
291
292 class VelociraptorArtifactRecommendationResponse(BaseModel):
293 artifact_recommendations: List[VelociraptorArtifactRecommendation] = Field(
294 description="The recommended artifacts which detail the name, description, and explanation of why the artifact was selected.",
295 )
296 success: bool = Field(..., description="Whether the request was successful.")
297 message: str = Field(
298 ...,
299 description="A message describing the result of the request.",
300 )
301 general_thoughts: str = Field(
302 description="General thoughts on the artifacts and why they were selected.",
303 )