main
py 511 lines 25.6 KB
Raw
1 from enum import Enum
2 from typing import Dict
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 AvailableMonitoringAlerts(str, Enum):
13 """
14 The available monitoring alerts.
15 """
16
17 WAZUH_SYSLOG_LEVEL_ALERT = (
18 "This alert monitors the SYSLOG_LEVEL field in the Wazuh logs. When the level is ALERT, "
19 "it triggers an alert that is created within CoPilot. Ensure that you have a pipeline "
20 "rule that sets the SYSLOG_LEVEL field to ALERT when the Wazuh rule level is greater than 11."
21 )
22 SURICATA_ALERT_SEVERITY_1 = (
23 "This alert monitors the Suricata logs. When an the alert_severity field is 1, it triggers "
24 "an alert that is created within CoPilot. Ensure that you have a pipeline rule that sets "
25 )
26 OFFICE365_EXCHANGE_ONLINE = (
27 "This alert monitors the Office365 Exchange events. When an alert is detected, it triggers an "
28 "alert that is created within CoPilot. Ensure that you have a pipeline rule that sets the "
29 "alert_severity field to 1 when the Office365 alert is detected."
30 )
31 OFFICE365_THREAT_INTEL = (
32 "This alert monitors the Office365 Threat Intelligence events. When an alert is detected, it triggers an "
33 "alert that is created within CoPilot. Ensure that you have a pipeline rule that sets the "
34 "alert_severity field to 1 when the Office365 alert is detected."
35 )
36 CROWDSTRIKE_ALERT = (
37 "This alert monitors the CrowdStrike events. When an alert is detected, it triggers an "
38 "alert that is created within CoPilot. Ensure that you have a pipeline rule that sets the "
39 "alert_severity field to 1 when the CrowdStrike alert is detected."
40 )
41 # ! --- Fortinet / FortiGate alerts ---
42 FORTINET_SYSTEM = (
43 "This alert monitors the Fortinet System events. When an alert is detected, it triggers an "
44 "alert that is created within CoPilot. Ensure that you have a pipeline rule that sets the "
45 "alert_severity field to 1 when the Fortinet alert is detected."
46 )
47 FORTINET_UTM = (
48 "This alert monitors the Fortinet UTM events. When an alert is detected, it triggers an "
49 "alert that is created within CoPilot. Ensure that you have a pipeline rule that sets the "
50 "alert_severity field to 1 when the Fortinet alert is detected."
51 )
52 FORTINET_FORTIWEB_PATH_TRAVERSAL_VULNERABILITY_EXPLOITATION_ATTEMPT = (
53 "Detects potential exploitation attempts targeting CVE-2025-64446, a critical path traversal vulnerability "
54 "affecting Fortinet FortiWeb Web Application Firewalls (WAF). "
55 "An adversary can abuse this flaw, which requires no authentication, to create new, unauthorized "
56 "administrative user accounts on the exposed device. "
57 "This provides the threat actor with full administrative control over the security appliance, allowing "
58 "them to bypass security policies, neutralize the WAF, and establish a persistent backdoor for further "
59 "network intrusions."
60 )
61 FORTINET_WIDS_WIRELESS_VALID_CLIENT_MISASSOCIATION_DETECTED = (
62 "Detects when FortiGate Wireless IDS identifies an incident where a legitimate wireless client associates "
63 "with a rogue or unauthorized access point (AP), a behavior known as valid client misassociation. "
64 "Attackers may set up malicious APs to impersonate trusted networks, tricking legitimate clients into "
65 "connecting. "
66 "This tactic is commonly used in evil twin attacks to intercept traffic, harvest credentials, or inject "
67 "malicious payloads. Identifying such associations is essential to safeguarding wireless network integrity "
68 "and preventing data leakage."
69 )
70 FORTINET_WIDS_WIRELESS_MANAGEMENT_FLOODING_DETECTED = (
71 "Detects when FortiGate Wireless IDS identifies abnormal surges of wireless management frames, such as "
72 "authentication, association, or probe requests, which may indicate a management frame flooding attack. "
73 "Adversaries use this technique to disrupt wireless network operations, exhaust access point resources, or "
74 "perform denial-of-service (DoS) attacks. "
75 "Continuous monitoring of management frame activity helps with the early identification and mitigation of "
76 "wireless network disruptions."
77 )
78 FORTINET_WIDS_WIRELESS_EAPOL_PACKET_FLOODING_DETECTED = (
79 "Detects a flood of EAPOL (Extensible Authentication Protocol over LAN) packets on the wireless network "
80 "identified by FortiGate Wireless IDS. "
81 "Such flooding can exhaust network resources, disrupt normal authentication processes, or exploit "
82 "weaknesses in WPA/WPA2 handshakes. "
83 "Monitoring for this behavior is crucial to maintaining secure and stable wireless authentication services "
84 "in enterprise environments."
85 )
86 FORTINET_WIDS_ROGUE_ACCESS_POINT_DETECTED = (
87 "Detects the rogue access point (AP) in the network as reported by FortiGate Wireless IDS. "
88 "Rogue APs are unauthorized wireless access points connected to a network, often used by attackers to "
89 "bypass security controls, capture sensitive data, or conduct man-in-the-middle attacks. "
90 "Detection of rogue APs is critical to maintaining wireless network integrity and preventing unauthorized "
91 "access."
92 )
93 FORTINET_WIDS_WIRELESS_LONG_DURATION_ATTACK_DETECTED = (
94 "Detects a long duration attack on the wireless network identified by FortiGate Wireless IDS. "
95 "These attacks often involve persistent connections to rogue access points or the use of compromised "
96 "clients to maintain unauthorized access over an extended period. "
97 "Such activity may be used by adversaries for sustained data exfiltration, network reconnaissance, or to "
98 "establish footholds in the environment. "
99 "Monitoring these patterns is crucial to detecting stealthy and persistent wireless threats."
100 )
101 FORTINET_FIREWALL_VIRUS_DETECTED = (
102 "Detects the virus in the network identified by FortiGate Firewall. "
103 "This may indicate the presence of malware or a malicious file attempting to execute or transfer within "
104 "the network. "
105 "Threat actors may use malware to gain access, maintain persistence, or exfiltrate data. Monitoring such "
106 "events can help identify compromised systems or prevent further infection spread."
107 )
108 FORTINET_WIDS_WIRELESS_THREAT_DETECTED = (
109 "Detects potential wireless-based security threats as identified by FortiGate Wireless IDS. "
110 "These threats may include spoofed access points, EAPOL flooding, deauthentication attacks, or other "
111 "suspicious wireless behaviors. "
112 "Monitoring such events is critical to protecting against wireless intrusion attempts, maintaining the "
113 "integrity of the Wi-Fi network, and preventing unauthorized access or denial-of-service conditions caused "
114 "by malicious actors."
115 )
116 FORTINET_WIDS_WIRELESS_INVALID_MAC_OUI_DETECTED = (
117 "Detects instances where a FortiGate Wireless IDS identifies a client with an invalid or unrecognized MAC "
118 "Organizationally Unique Identifier (OUI). "
119 "This may indicate the presence of unauthorized, rogue, or potentially malicious devices attempting to "
120 "connect to the wireless network. "
121 "Monitoring for invalid MAC OUIs helps strengthen network access controls and prevent unauthorized access."
122 )
123 FORTINET_WIDS_WIRELESS_ASLEAP_ATTACK_DETECTED = (
124 "Detects the presence of an Asleap attack in a wireless network identified by FortiGate Wireless IDS. "
125 "Asleap is a tool used to exploit weak authentication in LEAP (Lightweight Extensible Authentication "
126 "Protocol), potentially allowing attackers to capture and crack wireless credentials. "
127 "Monitoring for this activity helps identify unauthorized attempts to compromise wireless network security "
128 "and protect sensitive credentials."
129 )
130 FORTINET_IPS_MALICIOUS_URL_DETECTED = (
131 "Detects when FortiGate Intrusion Prevention System (IPS) identifies access to a known malicious URL. "
132 "This activity may indicate attempts to connect to command and control infrastructure, deliver malware, or "
133 "exfiltrate data. "
134 "Monitoring these detections helps identify potential threats, prevent compromise, and maintain network "
135 "security."
136 )
137 FORTINET_IPS_BOTNET_ACTIVITY_DETECTED = (
138 "Detects botnet-related activity identified by FortiGate Intrusion Prevention System (IPS). This may "
139 "indicate that a host within the network is communicating with known botnet command and control servers or "
140 "exhibiting behavior consistent with botnet infections. Monitoring these events helps identify compromised "
141 "systems, prevent data exfiltration, and mitigate the spread of malicious activity within the environment."
142 )
143 FORTINET_ADMIN_USER_CREATED_FROM_PUBLIC_IP = (
144 "Detects the creation of a new administrator user account on a Fortinet FortiGate device originating from "
145 "a public IP address. "
146 "An adversary who gains access to the management interface may create unauthorized admin accounts to "
147 "establish persistent, privileged control over the firewall. "
148 "By creating these accounts from external or atypical network locations, attackers can maintain long-term "
149 "access, modify security policies, exfiltrate sensitive data, or prepare the environment for additional "
150 "malicious activity."
151 )
152 FORTINET_SUSPICIOUS_CONFIG_FILE_ACCESS_FROM_EXTERNAL_NETWORK = (
153 "Detects attempts to download a FortiGate configuration file from an external or publicly accessible "
154 "network source. "
155 "Adversaries may abuse this behavior to obtain sensitive configuration data, including administrative "
156 "credentials, network topology details, VPN settings, or firewall policies. "
157 "Access to this information can enable further compromise through targeted lateral movement, privilege "
158 "escalation, or tailored exploitation of exposed services."
159 )
160 FORTINET_WIDS_WIRELESS_WEAK_ENCRYPTION_DETECTED = (
161 "Detects wireless access points using weak or deprecated encryption protocols, as reported by FortiGate "
162 "Wireless IDS. "
163 "Risky encryption methods, such as WEP or misconfigured WPA settings, may allow adversaries to eavesdrop "
164 "on network traffic or perform cryptographic attacks to gain unauthorized access. "
165 "Identifying and remediating such vulnerabilities is essential to ensure wireless network confidentiality "
166 "and compliance with security best practices."
167 )
168 FORTINET_SUSPICIOUS_SUPER_ADMIN_LOGIN_DETECTED = (
169 "Detects a super admin login attempt to a FortiGate firewall originating from a suspicious or public IP "
170 "address. "
171 "This may indicate an attempt to exploit CVE-2025-24472 which allows unauthenticated attackers to gain "
172 "super admin privileges on vulnerable FortiOS devices (<7.0.16) with exposed management interfaces."
173 )
174 # ! --- Palo Alto Networks (PANW) alerts ---
175 PALOALTO_ALERT = (
176 "This alert monitors the PaloAlto events. When an alert is detected, it triggers an "
177 "alert that is created within CoPilot. Ensure that you have a pipeline rule that sets the "
178 "alert_severity field to 1 when the PaloAlto alert is detected."
179 )
180 PALOALTO_FIREWALL_TRAFFIC_TO_PHISHING_URL_ALLOWED = (
181 "Detects when the Palo Alto Networks firewall does not block traffic to a URL known to be used in phishing "
182 "attacks. "
183 "An adversary can abuse this by directing victims to the phishing site, potentially stealing credentials, "
184 "deploying malware, or conducting other malicious activities."
185 )
186 PALOALTO_FIREWALL_TRAFFIC_TO_MALICIOUS_URL_ALLOWED = (
187 "Detects when the Palo Alto Networks firewall does not block traffic to a URL associated with malware "
188 "distribution or operation. "
189 "This typically indicates a lapse in the firewall's threat intelligence or a misconfiguration. "
190 "An adversary can abuse this by using the unblocked URL to download malware onto a target system, establish "
191 "a command and control channel, or exfiltrate data."
192 )
193 PALOALTO_FIREWALL_VIRUS_ALLOWED = (
194 "Detects active network communication associated with known malware that is being allowed by the Palo Alto "
195 "Networks firewall. "
196 "This may indicate an ongoing security threat, where malicious traffic is bypassing firewall protections, "
197 "potentially leading to system compromise, data exfiltration, or further infiltration within the network."
198 )
199 PALOALTO_FIREWALL_TOR_TRAFFIC_ALLOWED = (
200 "Detects allowed network traffic to the TOR network. Adversaries can use TOR to anonymize their network "
201 "activity, bypass security controls, and evade detection while conducting malicious operations. "
202 "This could lead to unauthorized access, data exfiltration, and compliance violations if deemed malicious."
203 )
204 PALOALTO_FIREWALL_MEDIUM_SEVERITY_CORRELATION_EVENT_DETECTED = (
205 "Detects medium severity correlation events generated by Palo Alto Networks firewall's automated correlation "
206 "engine. "
207 "The correlation engine connects isolated network events and looks for patterns that indicate a more "
208 "significant event. "
209 "This helps identify suspicious traffic patterns and network anomalies which, when correlated, indicate with "
210 "a high probability that a host on the network has been compromised."
211 )
212 # ! --- SentinelOne alerts ---
213 SENTINELONE_NEW_ACTIVE_THREAT_MALICIOUS_DETECTED = "Threat with confidence level malicious detected"
214 SENTINELONE_NEW_ACTIVE_THREAT_SUSPICIOUS_DETECTED = "Threat with confidence level suspicious detected"
215 SENTINELONE_NEW_MITIGATION_KILL_PERFORMED_SUCCESSFULLY = "Kill performed successfully"
216 SENTINELONE_NEW_MITIGATION_QUARANTINE_PERFORMED_SUCCESSFULLY = "Quarantine performed successfully"
217 SENTINELONE_NEW_EXCLUSION_WAS_ADDED_OR_MODIFIED_BY_USER = "Exclusion was added/modified by user"
218 SENTINELONE_NEW_PATH_EXCLUSION_ADDED = "Path Exclusion added"
219 SENTINELONE_ANALYST_VERDICT_CHANGED_TO_TRUE_POSITIVE = 'A management user changed the analyst verdict to "True Positive".'
220 SENTINELONE_ANALYST_VERDICT_CHANGED_TO_FALSE_POSITIVE = 'A management user changed the analyst verdict to "False Positive".'
221 # ! --- Mimecast alerts ---
222 MIMECAST_COMPROMISED_SITE_URL_ACCESSED = (
223 "Detects user clicks on compromised websites classified by Mimecast, which were delivered via email. "
224 "Adversaries can exploit these links to direct users to malicious sites hosting malware, phishing pages, or spam content."
225 )
226 MIMECAST_EXECUTABLE_FILE_ATTACHMENT_DELIVERED = (
227 "Detects emails containing executable file attachments that have been delivered to a user's mailbox, as identified by Mimecast. "
228 "An adversary can abuse this delivery method to distribute malicious payloads, including malware, ransomware, or other executables designed to compromise the recipient's system."
229 )
230 MIMECAST_MALICIOUS_EMAIL_ATTACHMENT_DELIVERED = (
231 "Detects emails containing malicious attachments identified by Mimecast that have been delivered to a user's mailbox. "
232 "Attackers commonly use spearphishing emails with malicious attachments to compromise systems by tricking recipients into opening them."
233 )
234 MIMECAST_MALICIOUS_EMAIL_LINK_ACCESSED = (
235 "Detects instances where users click on malicious URLs embedded in emails. "
236 "These URLs may redirect to phishing sites, initiate malware downloads, or enable advanced threats, posing risks to user and organizational security."
237 )
238 MIMECAST_P2P_FILE_SHARING_URL_ACCESSED = (
239 "Detects a user clicking on a peer-to-peer file sharing URL, as classified by Mimecast, that was delivered via email. "
240 "Adversaries can abuse P2P file-sharing platforms to distribute malicious files, such as malware, ransomware, or unauthorized software, by embedding these links in phishing emails."
241 )
242 MIMECAST_ANONYMIZER_URL_ACCESSED = (
243 "Detects user clicks on anonymizer URLs classified by Mimecast that arrived in an email. "
244 "An adversary can abuse anonymizer services to mask their identity and hide the origin of malicious traffic, making it harder to trace their activities."
245 )
246 MIMECAST_IMPERSONATION_EMAIL_DELIVERED = (
247 "Detects unblocked email messages flagged by Mimecast as potential impersonation attempts. "
248 "These emails are strong indicators of Business Email Compromise (BEC), a sophisticated phishing tactic in which attackers impersonate trusted entities to deceive recipients."
249 )
250 MIMECAST_MALICIOUS_OUTBOUND_EMAIL = (
251 "Detects outbound emails identified by Mimecast as malicious, including those containing phishing links, malware-laden attachments, or other suspicious content. "
252 "Monitoring these events is crucial in identifying potential account compromises or unauthorized activities aimed at distributing threats to external recipients."
253 )
254 MIMECAST_MALICIOUS_RTF_ATTACHMENT_DELIVERED = (
255 "Detects emails containing malicious rtf file attachments identified by Mimecast that have been delivered to a user's mailbox. "
256 "Adversaries can abuse malicious RTF files to exploit vulnerabilities in applications that process these files, potentially executing arbitrary code or delivering malware."
257 )
258 MIMECAST_PHISHING_EMAIL_DELIVERED = (
259 "Detects unblocked email messages flagged as phishing by Mimecast, indicating their successful delivery to recipients' mailboxes. "
260 "Such emails may contain malicious attachments, URLs, or deceptive content."
261 )
262 MIMECAST_SOURCE_CODE_FILE_IN_EMAIL_ATTACHMENT = (
263 "Detects the presence of source code files in email attachments by analyzing Mimecast email logs. "
264 "This activity may indicate potential insider threats, as internal users could be attempting to exfiltrate sensitive or proprietary information."
265 )
266 MIMECAST_URL_WITH_DANGEROUS_FILE_TYPE_ACCESSED = (
267 "Detects user clicks on URLs containing dangerous file types, as classified by Mimecast, that were delivered via email. "
268 "Adversaries often use emails with embedded URLs linking to files with extensions commonly associated with malware, such as .exe, .bat, .js, or .msi."
269 )
270
271
272 class AvailableMonitoringAlertsResponse(BaseModel):
273 """
274 The available monitoring alerts response.
275 """
276
277 success: bool
278 message: str
279 available_monitoring_alerts: List[Dict[str, str]]
280
281
282 class ProvisionMonitoringAlertRequest(BaseModel):
283 search_within_last: int = Field(
284 ...,
285 description="The time in seconds to search within for the alert.",
286 )
287 execute_every: int = Field(
288 ...,
289 description="The time in seconds to execute the alert search.",
290 )
291 alert_name: str = Field(
292 "WAZUH_SYSLOG_LEVEL_ALERT",
293 description="The name of the alert to provision.",
294 )
295
296 @field_validator("alert_name")
297 @classmethod
298 def validate_alert_name(cls, v):
299 v = v.replace(" ", "_").upper()
300 if v not in AvailableMonitoringAlerts.__members__:
301 raise HTTPException(
302 status_code=400,
303 detail=f"Invalid alert name: {v}. Must be one of: {', '.join(AvailableMonitoringAlerts.__members__)}",
304 )
305 return v
306
307 @field_validator("search_within_last", "execute_every")
308 @classmethod
309 def validate_non_zero(cls, v):
310 if v == 0:
311 raise HTTPException(
312 status_code=400,
313 detail=f"Invalid value: {v}. Must be greater than 0.",
314 )
315 return v
316
317
318 class ProvisionWazuhMonitoringAlertResponse(BaseModel):
319 success: bool
320 message: str
321
322
323 ########## ! GRAYLOG WHITELIST URL CREATION ! ##########
324 class GraylogUrlWhitelistEntryConfig(BaseModel):
325 id: str = Field(
326 ...,
327 description="The ID of the URL whitelist entry.",
328 )
329 title: str = Field(
330 ...,
331 description="The title of the URL whitelist entry.",
332 )
333 type: str = Field(
334 "literal",
335 description="The type of the URL whitelist entry.",
336 )
337 value: str = Field(
338 ...,
339 description="The value of the URL whitelist entry.",
340 )
341
342
343 class GraylogUrlWhitelistEntries(BaseModel):
344 entries: List[GraylogUrlWhitelistEntryConfig]
345 disabled: bool
346
347
348 ########## ! GRAYLOG WEBHOOK CREATION ! ##########
349 class GraylogAlertWebhookConfig(BaseModel):
350 url: str = Field(
351 ...,
352 description="The URL to use for the webhook.",
353 )
354 api_key: Optional[str] = Field(
355 None,
356 description="The API key to use for the webhook.",
357 )
358 api_secret: Optional[str] = Field(
359 None,
360 description="The API secret to use for the webhook.",
361 )
362 basic_auth: Optional[str] = Field(
363 None,
364 description="The basic auth to use for the webhook.",
365 )
366 type: str = Field(
367 ...,
368 description="The type of the webhook.",
369 )
370
371
372 class GraylogAlertWebhookNotificationModel(BaseModel):
373 title: str
374 description: str
375 config: GraylogAlertWebhookConfig
376
377
378 ########## ! GRAYLOG EVENT CREATION ! ##########
379 class GraylogAlertProvisionProvider(BaseModel):
380 template: str
381 type: str = Field(..., alias="type")
382 require_values: bool
383
384
385 class GraylogAlertProvisionFieldSpecItem(BaseModel):
386 data_type: str
387 providers: List[GraylogAlertProvisionProvider]
388
389
390 class GraylogAlertProvisionConfig(BaseModel):
391 query: str
392 query_parameters: List
393 streams: List
394 search_within_ms: int
395 execute_every_ms: int
396 group_by: List
397 series: List
398 conditions: Dict
399 type: str = Field(..., alias="type")
400 event_limit: int = Field(1000, description="The event limit for the config")
401
402
403 class GraylogAlertProvisionNotificationSettings(BaseModel):
404 grace_period_ms: int
405 backlog_size: Optional[int] = None
406
407
408 class GraylogAlertProvisionNotification(BaseModel):
409 notification_id: str
410
411
412 class GraylogAlertProvisionModel(BaseModel):
413 title: str
414 description: str
415 priority: int
416 config: GraylogAlertProvisionConfig
417 field_spec: Dict[str, GraylogAlertProvisionFieldSpecItem]
418 key_spec: List
419 notification_settings: GraylogAlertProvisionNotificationSettings
420 notifications: Optional[List[GraylogAlertProvisionNotification]] = []
421 alert: bool
422
423
424 class AlertPriority(Enum):
425 LOW = 1
426 NORMAL = 2
427 HIGH = 3
428
429
430 class CustomFields(BaseModel):
431 name: str
432 value: str
433
434 @field_validator("name")
435 @classmethod
436 def replace_spaces_with_underscores(cls, v):
437 return v.replace(" ", "_")
438
439
440 class CustomMonitoringAlertProvisionModel(BaseModel):
441 alert_name: str = Field(
442 ...,
443 description="The name of the alert to provision.",
444 examples=["WAZUH_SYSLOG_LEVEL_ALERT"],
445 )
446 alert_description: str = Field(
447 ...,
448 description=(
449 "The description of the alert to provision. This alert monitors the "
450 "SYSLOG_LEVEL field in the Wazuh logs. When the level is ALERT, it "
451 "triggers an alert that is created within DFIR-IRIS. Ensure that you "
452 "have a pipeline rule that sets the SYSLOG_LEVEL field to ALERT when "
453 "the Wazuh rule level is greater than 11."
454 ),
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 ...,
466 description="The priority of the alert to provision.",
467 examples=[2],
468 )
469 search_query: str = Field(
470 ...,
471 description="The search query to use for the alert.",
472 examples=["syslog_type:wazuh AND syslog_level:alert"],
473 )
474 streams: Optional[List[str]] = Field(
475 [],
476 description="The streams to use for the alert.",
477 examples=[["5f3e4c3b3f37b70001f3d7b3"]],
478 )
479 custom_fields: Optional[List[CustomFields]] = Field(
480 None,
481 description="The custom fields to use for the alert.",
482 examples=[[{"name": "source", "value": "Wazuh"}]],
483 )
484 search_within_ms: int = Field(
485 ...,
486 description="The time in milliseconds to search within for the alert.",
487 examples=[300000],
488 )
489 execute_every_ms: int = Field(
490 ...,
491 description="The time in milliseconds to execute the alert search.",
492 examples=[300000],
493 )
494
495 # ! I think I can remove the requirement for the CUSTOMER_CODE field.
496 # ! The new incident management doesnt require the CUSTOMER_CODE to be preset but
497 # ! rather looks for valid CustomerCodeKeys(Enum) # !
498 # @root_validator
499 # def check_customer_code(cls, values):
500 # custom_fields = values.get("custom_fields")
501 # if custom_fields is None:
502 # raise HTTPException(
503 # status_code=400,
504 # detail="At least one custom field with name CUSTOMER_CODE is required",
505 # )
506 # if not any(field.name == "CUSTOMER_CODE" for field in custom_fields):
507 # raise HTTPException(
508 # status_code=400,
509 # detail="At least one custom field with name CUSTOMER_CODE is required",
510 # )
511 # return values