main
py 412 lines 14.5 KB
Raw
1 from datetime import datetime
2 from datetime import timedelta
3 from enum import Enum
4 from typing import Dict
5 from typing import List
6 from typing import Optional
7
8 from pydantic import BaseModel
9 from pydantic import Field
10 from pydantic import model_validator
11
12
13 class InvokeSapSiemRequest(BaseModel):
14 customer_code: str = Field(
15 ...,
16 description="The customer code.",
17 examples=["00002"],
18 )
19 integration_name: str = Field(
20 "SAP SIEM",
21 description="The integration name.",
22 examples=["SAP SIEM"],
23 )
24 threshold: Optional[int] = Field(
25 3,
26 description="Number of 'Invalid LoginID' before the first 'OK'",
27 )
28 time_range: Optional[str] = Field(
29 "15m",
30 pattern="^[1-9][0-9]*[mhdw]$",
31 description="Time range for the query (1m, 1h, 1d, 1w)",
32 )
33
34 lower_bound: str = None
35 upper_bound: str = None
36
37 @model_validator(mode="before")
38 @classmethod
39 def set_time_bounds(cls, values):
40 time_range = values.get("time_range")
41 if time_range:
42 unit = time_range[-1]
43 amount = int(time_range[:-1])
44
45 now = datetime.utcnow()
46
47 if unit == "m":
48 lower_bound = now - timedelta(minutes=amount)
49 elif unit == "h":
50 lower_bound = now - timedelta(hours=amount)
51 elif unit == "d":
52 lower_bound = now - timedelta(days=amount)
53 elif unit == "w":
54 lower_bound = now - timedelta(weeks=amount)
55
56 values["lower_bound"] = lower_bound.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
57 values["upper_bound"] = now.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
58 return values
59
60
61 class InvokeSAPSiemResponse(BaseModel):
62 success: bool
63 message: str
64
65
66 class SapSiemAuthKeys(BaseModel):
67 API_KEY: str = Field(
68 ...,
69 description="YOUR API KEY",
70 examples=["3_yUWT3uDMs9E1N87r4Ey"],
71 )
72 SECRET_KEY: str = Field(
73 ...,
74 description="YOUR SECRET KEY",
75 examples=["4ijD6uMCca"],
76 )
77 USER_KEY: Optional[str] = Field(
78 None,
79 description="YOUR USER KEY",
80 examples=["AK9zAL"],
81 )
82 API_DOMAIN: str = Field(
83 ...,
84 description="YOUR API DOMAIN",
85 examples=["audit.eu1.gigya.com"],
86 )
87
88
89 class CollectSapSiemRequest(BaseModel):
90 customer_code: str = Field(
91 ...,
92 description="The customer code.",
93 examples=["00002"],
94 )
95 apiKey: str = Field(..., description="API key for authorization")
96 secretKey: str = Field(..., description="Secret key for authorization")
97 userKey: str = Field(..., description="User key for identification")
98 apiDomain: str = Field(..., description="API domain")
99 threshold: Optional[int] = Field(
100 1,
101 description="Number of 'Invalid LoginID' before the first 'OK'",
102 )
103 lower_bound: str = None
104 upper_bound: str = None
105
106
107 ######### ! SAP API RESPONSE ! #########
108 class HttpReq(BaseModel):
109 SDK: str = Field(
110 ...,
111 description="Software Development Kit used for the HTTP request",
112 )
113 country: str = Field(..., description="Country code")
114
115
116 class Params(BaseModel):
117 clientContext: Optional[str] = Field(None, description="Client context information")
118 include: Optional[str] = Field(None, description="Data to include in the response")
119 password: str = Field(..., description="Password for the user")
120 loginID: str = Field(..., description="Login ID of the user")
121 apiKey: str = Field(..., description="API key for authorization")
122 format: Optional[str] = Field(None, description="Response format")
123 secret: Optional[str] = Field(None, description="Secret key for authorization")
124 userKey: Optional[str] = Field(None, description="User key for identification")
125
126
127 class UserAgent(BaseModel):
128 os: str = Field(..., description="Operating system of the user")
129 browser: str = Field(..., description="Browser used by the user")
130 raw: Optional[str] = Field(None, description="Raw user agent string")
131 version: str = Field(..., description="Browser version")
132 platform: str = Field(..., description="Platform type (desktop/mobile)")
133
134
135 class UserKeyDetails(BaseModel):
136 name: Optional[str] = Field(None, description="Name of the user")
137 emailDomain: Optional[str] = Field(None, description="Email domain of the user")
138
139
140 class Restrictions(BaseModel):
141 ipWhitelistRestricted: bool = Field(
142 ...,
143 description="Is IP whitelisting restricted",
144 )
145 ipWhitelistRestrictionEnforced: bool = Field(
146 ...,
147 description="Is IP whitelist restriction enforced",
148 )
149 ipBlacklistRestricted: bool = Field(
150 ...,
151 description="Is IP blacklisting restricted",
152 )
153 ipBlacklistRestrictionEnforced: bool = Field(
154 ...,
155 description="Is IP blacklist restriction enforced",
156 )
157
158
159 class Result(BaseModel):
160 callID: str = Field(..., description="Unique identifier for the call")
161 authType: Optional[str] = Field(None, description="Type of authentication used")
162 timestamp: str = Field(
163 ...,
164 alias="@timestamp",
165 description="Timestamp of the event",
166 )
167 errCode: str = Field(..., description="Error code")
168 errDetails: Optional[str] = Field(None, description="Detailed error message")
169 errMessage: str = Field(..., description="Error message")
170 endpoint: str = Field(..., description="API endpoint hit")
171 userKey: Optional[str] = Field(None, description="User key for identification")
172 httpReq: HttpReq = Field(..., description="HTTP request details")
173 ip: str = Field(..., description="IP address of the user")
174 serverIP: str = Field(..., description="Server IP address")
175 params: Params = Field(..., description="Parameters passed in the request")
176 uid: Optional[str] = Field(
177 "No uid found",
178 description="Unique identifier for the user",
179 )
180 apikey: str = Field(..., description="API key used for the request")
181 userAgent: UserAgent = Field(..., description="User agent details")
182 userKeyDetails: Optional[UserKeyDetails] = Field(
183 None,
184 description="Details related to user key",
185 )
186 XffFirstIp: str = Field(..., description="First IP in the X-Forwarded-For header")
187 restrictions: Restrictions = Field(..., description="IP restrictions")
188 riskScore: Optional[str] = Field(
189 None,
190 description="Risk score associated with the request",
191 )
192 event_timestamp: Optional[datetime] = Field(
193 None,
194 description="Timestamp of the event",
195 )
196 case_created: Optional[str] = Field(
197 "False",
198 description="Whether a case has been created for the event",
199 )
200 event_analyzed: Optional[str] = Field(
201 "False",
202 description="Whether the event has been analyzed",
203 )
204 event_analyzed_multiple_logins: Optional[str] = Field(
205 "False",
206 description="Whether the event has been analyzed for multiple logins",
207 )
208 event_analyzed_success_login_diff_ip: Optional[str] = Field(
209 "False",
210 description="Whether the event has been analyzed for successful login from different IP",
211 )
212 event_analyzed_same_user_failed_diff_ip: Optional[str] = Field(
213 "False",
214 description="Whether the event has been analyzed for same user failed login from different IP",
215 )
216 event_analyzed_same_user_failed_diff_geo: Optional[str] = Field(
217 "False",
218 description="Whether the event has been analyzed for same user failed login from different geo",
219 )
220 event_analyzed_same_user_successful_diff_geo: Optional[str] = Field(
221 "False",
222 description="Whether the event has been analyzed for same user successful login from different geo",
223 )
224 event_analyzed_brute_force_ip: Optional[str] = Field(
225 "False",
226 description="Whether the event has been analyzed for brute force IP",
227 )
228 event_analyzed_brute_force_same_ip: Optional[str] = Field(
229 "False",
230 description="Whether the event has been analyzed for brute force same IP",
231 )
232 event_analyzed_successful_login_after_failures_diff_loginID: Optional[str] = Field(
233 "False",
234 description="Whether the event has been analyzed for successful login after failures",
235 )
236
237
238 class SapSiemResponseBody(BaseModel):
239 results: List[Result] = Field(..., description="List of result objects")
240 totalCount: int = Field(..., description="Total count of results")
241 statusCode: int = Field(..., description="Status code of the response")
242 errorCode: int = Field(..., description="Error code of the response")
243 statusReason: str = Field(..., description="Status reason of the response")
244 callId: str = Field(..., description="Unique identifier for the overall call")
245 time: str = Field(..., description="Time of the response")
246 objectsCount: int = Field(..., description="Count of objects in results")
247
248
249 #### ! WAZUH INDEXER RESULTS ! ####
250
251
252 class SapSiemSource(BaseModel):
253 logSource: Optional[str] = Field(None, description="The source of the log")
254 params_loginID: str = Field(..., description="The login ID of the user")
255 errCode: str = Field(..., description="The error code")
256 ip: str = Field(..., description="The IP address of the user")
257 httpReq_country: str = Field(..., description="The country from which the HTTP request originated")
258 event_timestamp: str = Field(..., description="The timestamp of the event")
259 errMessage: Optional[str] = Field(None, description="The error message")
260 customer_code: str = Field(..., description="The customer code")
261 errDetails: Optional[str] = Field(None, description="Detailed error message")
262
263
264 class SapSiemHit(BaseModel):
265 index: str = Field(..., description="The index of the hit", alias="_index")
266 id: str = Field(..., description="The ID of the hit", alias="_id")
267 score: Optional[float] = Field(None, description="The score of the hit", alias="_score")
268 source: SapSiemSource = Field(..., description="The source data of the hit", alias="_source")
269 sort: Optional[List[int]] = Field(None, description="The sort order of the hit")
270
271
272 class SapSiemTotal(BaseModel):
273 value: int = Field(..., description="The total number of hits")
274 relation: str = Field(..., description="The relation of the total hits")
275
276
277 class SapSiemHits(BaseModel):
278 total: SapSiemTotal = Field(..., description="The total hits data")
279 max_score: Optional[float] = Field(None, description="The maximum score among the hits")
280 hits: List[SapSiemHit] = Field(..., description="The list of hits")
281
282
283 class SapSiemShards(BaseModel):
284 total: int = Field(..., description="The total number of shards")
285 successful: int = Field(..., description="The number of successful shards")
286 skipped: int = Field(..., description="The number of skipped shards")
287 failed: int = Field(..., description="The number of failed shards")
288
289
290 class SapSiemWazuhIndexerResponse(BaseModel):
291 scroll_id: Optional[str] = Field(None, description="The scroll ID", alias="_scroll_id")
292 took: int = Field(..., description="The time it took to execute the request")
293 timed_out: bool = Field(..., description="Whether the request timed out")
294 shards: SapSiemShards = Field(..., description="The shards data", alias="_shards")
295 hits: SapSiemHits = Field(..., description="The hits data")
296
297
298 class SuspiciousLogin(BaseModel):
299 customer_code: str
300 logSource: Optional[str] = Field(None)
301 loginID: str
302 country: Optional[str] = None
303 ip: str
304 event_timestamp: str
305 errMessage: str
306 index: Optional[str] = Field(None, description="The index of the hit", alias="_index")
307 id: Optional[str] = Field(None, description="The ID of the hit", alias="_id")
308 errDetails: Optional[str] = Field(None, description="Detailed error message")
309
310
311 class ErrCode(Enum):
312 """
313 Error codes for SAP SIEM
314 """
315
316 INVALID_LOGIN_ID = "403042" # Invalid LoginID
317 IP_BLOCKED = "403051" # IP is blocked
318 ACCOUNT_TEMPORARILY_LOCKED = "403120" # Account temporarily locked
319 OK = "0" # Successful login
320
321
322 ################# ! IRIS CASE CREATION SCHEMA ! #################
323 class IrisCasePayload(BaseModel):
324 case_name: str = Field(..., description="The name of the case.")
325 case_description: str = Field(..., description="The description of the case.")
326 case_customer: int = Field(1, description="The customer of the case.")
327 case_classification: int = Field(
328 1,
329 description="The classification of the case.",
330 )
331 soc_id: str = Field("1", description="The SOC ID of the case.")
332 custom_attributes: Optional[Dict] = Field(
333 None,
334 description="The custom attributes of the case.",
335 )
336 create_customer: bool = Field(
337 False,
338 description="The create customer flag of the case.",
339 )
340
341 def to_dict(self):
342 return self.model_dump(exclude_none=True)
343
344
345 class ModificationHistoryEntry(BaseModel):
346 user: str
347 user_id: int
348 action: str
349
350
351 class CaseData(BaseModel):
352 case_id: int
353 open_date: str
354 modification_history: Dict[str, ModificationHistoryEntry]
355 close_date: Optional[str] = None
356 case_description: str
357 classification_id: int
358 case_soc_id: str
359 case_name: str
360 custom_attributes: Optional[Dict[str, str]] = None
361 case_uuid: str
362 review_status_id: Optional[int] = None
363 state_id: int
364 case_customer: int
365 reviewer_id: Optional[int] = None
366 user_id: int
367 owner_id: int
368 closing_note: Optional[str] = None
369 status_id: int
370
371
372 class CaseResponse(BaseModel):
373 success: bool
374 data: CaseData
375
376
377 ################# ! IRIS ASSET ADD SCHEMA ! #################
378 class AddAssetModel(BaseModel):
379 name: str
380 asset_type: int
381 analysis_status: int = Field(
382 None,
383 description="The analysis status ID of the asset.",
384 )
385 compromise_status: int = Field(
386 None,
387 description="The asset compromise status ID of the asset.",
388 )
389 asset_tags: Optional[List[str]] = Field(
390 None,
391 description="The asset tags of the asset.",
392 )
393 description: Optional[str] = Field(
394 None,
395 description="The asset description of the asset.",
396 )
397 asset_domain: Optional[str] = Field(
398 None,
399 description="The asset domain of the asset.",
400 )
401 ip: Optional[str] = Field(None, description="The asset IP of the asset.")
402 ioc_links: Optional[List[int]] = Field(
403 None,
404 description="The IoC links of the asset.",
405 )
406 custom_attributes: Optional[Dict[str, str]] = Field(
407 None,
408 description="The custom attributes of the asset.",
409 )
410
411 def to_dict(self):
412 return self.model_dump(exclude_none=True)