FireEyeNX

FireEye Network Security is an effective cyber threat protection solution that helps organizations minimize the risk of costly breaches by accurately detecting and immediately stopping advanced, targeted, and other evasive attacks hiding in internet traffic.

Network Security · FireEye Network Security (NX)

Details

IDFireEyeNX
ProviderTrellix
CategoryNetwork Security
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

FireEye Network Security is an effective cyber threat protection solution that helps organizations minimize the risk of costly breaches by accurately detecting and immediately stopping advanced, targeted, and other evasive attacks hiding in internet traffic.
This integration was integrated and tested with version 2.0.0 of FireEyeNX APIs.

Configure FireEyeNX in Cortex

Parameter Description Required
url URL True
credentials Username True
request_timeout HTTP(S) Request Timeout (in seconds) False
isFetch Fetch incidents False
incidentType Incident type False
first_fetch First fetch time interval False
max_fetch Fetch Limit False
fetch_type Fetch Types False
fetch_mvx_correlated_events Fetches MVX-correlated events only. False
malware_type Alert Malware Type False
replace_alert_url Use instance URL for all the fetched alerts URL. False
fetch_artifacts Fetch artifacts for each alert. False
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

fireeye-nx-get-alerts


Search and retrieve FireEye alerts based on several filters.

Base Command

fireeye-nx-get-alerts

Input

Argument Name Description Required
alert_id The ID number of the alert to retrieve. To retrieve the alert ID, execute the fireeye-nx-get-alerts command without specifying the alert_id. Optional
src_ip The source IPv4 address related to the malware alert to retrieve. Optional
dst_ip The destination IPv4 address related to the malware alert to retrieve. Optional
duration The time interval to search. This filter is used with either the start_time or end_time filter. If duration, start time, and end time are not specified, the system defaults to duration=12_hours, end_time=current_time. If only the duration is specified, the end_time defaults to the current_time. Possible values are: “1_hour”, “2_hours”, “6_hours”, “12_hours”, “24_hours”, and “48_hours”. Optional
start_time The start time of the search. This filter is used with the duration filter. If the start_time is specified but not the duration, the system defaults to duration=12_hours, starting at the specified start_time.
Formats:
YYYY-MM-dd
YYYY-MM-ddTHH:mm:ss
N days
N hours
Example:
2020-05-01
2020-05-01T00:00:00
2 days
5 hours
Optional
end_time The end time of the search. This filter is used with the duration filter. If the end_time is specified but not the duration, the system defaults to duration=12_hours, ending at the specified end_time.
Formats:
YYYY-MM-dd
YYYY-MM-ddTHH:mm:ss
N days
N hours
Example:
2020-05-01
2020-05-01T00:00:00
2 days
5 hours
Optional
file_name The name of the malware file to retrieve. Optional
file_type The malware file type to retrieve. Optional
info_level The level of information to retrieve. Possible values are: “concise”, “normal”, and “extended”. Optional
malware_name The name of the malware object to retrieve. Optional
malware_type The type of the malware object to retrieve. Possible values are: “domain_match”, “malware_callback”, “malware_object”, “web_infection”, and “infection_match”. Optional
md5 The MD5 hash of the alert to retrieve. This filter is not time dependent; it does not default to duration=12_hours. Optional
url A specific alert URL to retrieve. Optional

Context Output

Path Type Description
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Md5Sum String The md5sum of malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Sha256 String The SHA256 hash of malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Application String The application of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.HttpHeader String The HTTP header of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Original String The filename of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Name String The name of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Sid String The SID of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Type String The file type of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Stype String The STYPE of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Url String The URL of the malware associated with the alert.
FireEyeNX.Alert.Explanation.MalwareDetected.Malware.Content String The content of the malware associated with the alert.
FireEyeNX.Alert.Explanation.CncServices.CncService.Address String The CNC service IP address associated with the alert.
FireEyeNX.Alert.Explanation.CncServices.CncService.Channel String The CNC service channel associated with the alert.
FireEyeNX.Alert.Explanation.CncServices.CncService.Port Number The CNC service port address associated with the alert.
FireEyeNX.Alert.Explanation.CncServices.CncService.Protocol String The CNC service protocol associated with the alert.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Totalmemory Number The total memory of heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Lastbytesreceived Number The last byte received in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Processinfo.Imagepath String The image path of the process in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Processinfo.Md5sum String The md5sum of the process in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Processinfo.Pid Number The PID of the process in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.IncrementCount Number The increment count in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Name String The name of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Bytesreceived Number The bytes received in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Lasttotalmemory Number The last total memory in heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Type String The type of heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Timestamp Number The timestamp of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.RCount Number The RCount of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.TotalSize String The total size of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.RUnit String The RUnit of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Mode String The mode of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Pattern String The pattern of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Entry.Percentage Number The entry percentage of the bytes list in the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Entry.Byte String The entry byte of the bytes list in the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Entry.Count Number The entry count of the bytes list in the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Entry.FirstOffset String The entry offset of the bytes list in the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Entry.IsNOP String If entry NOP appears in the bytes list in the heap spraying then yes, otherwise no.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Distinct Number The distinct number of the byte list in the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.BytesList.Count Number The number of the byte list in the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Javascript String If heap spraying has javascript then yes, otherwise no.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.DNA Number The DNA of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.TotalRCount Number The total row count of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.ProcessedRCount Number The processed row count of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Heapspraying.Processed String The processed memory of the heap spraying.
FireEyeNX.Alert.Explanation.OsChanges.Process.Fid.Ads String The FID ads of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Fid.Content Number The FID content of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.ParentUserAccount.UserSid String The parent user account SID of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.ParentUserAccount.SessionId Number The parent user account session ID of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.ParentUserAccount.UserAccountName String The parent user account name of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.ParentUserAccount.AuthenticationId String The parent user account authentication ID of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.ParentUserAccount.SuperPrivilegesPresent Number If super privileges are present in this process then 1, otherwise 0.
FireEyeNX.Alert.Explanation.OsChanges.Process.Parentname String The path of the parent process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Sha256sum String The sha256sum of the parent process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Pid Number The PID of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Filesize Number File size of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Ppid Number The PPID of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Mode String The mode of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Cmdline String The path of the command associated with the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Sha1sum String The sha1sum of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.Md5sum String The md5sum of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.SrcThread String The source thread name of the process.
FireEyeNX.Alert.Explanation.osChanges.Process.Value String The value of the path in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.UserAccount.UserSid String The SID of the user account for the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.UserAccount.SessionId Number The session ID of the user account for the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.UserAccount.UserAccountName String The name of the user account for the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.UserAccount.AuthenticationId String The authentication ID of the user account for the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.UserAccount.SuperPrivilegesPresent Number If super privileges are present in this user account then 1, otherwise 0.
FireEyeNX.Alert.Explanation.OsChanges.Process.Timestamp Number The timestamp of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.MemoryData String The memory data of the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.LocalThreadCount Number The local thread count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.FileOpenCount Number The file open count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.FileModifyCount Number The file modify count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.FileCreateCount Number The file created count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.ChildProcessCount Number The file process count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.FileFailedCount Number The file failed count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.HttpReqCount Number The HTTP request count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.RemoteThreadCount Number The remote thread count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Process.TelemetryData.MutexCreateCount Number The mutex-created count of the telemetry data in the process.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Mode String The mode of the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Processinfo.Imagepath String The image path of the process in the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Processinfo.Md5sum String The md5sum of the process in the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Processinfo.Pid Number The PID of the process in the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Ntstatus String The NTSTATUS of the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Suppressed Boolean If the registry key was suppressed then true, otherwise false.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Value String The value of the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Timestamp Number The timestamp of the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.SrcThread String The source thread name of the registry key.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Randomized Boolean If the registry key was randomized then true, otherwise false.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.Buffered Boolean If the registry key was buffered then true, otherwise false.
FireEyeNX.Alert.Explanation.OsChanges.Regkey.NoExtend Boolean If the registry key has no_extend then true, otherwise false.
FireEyeNX.Alert.Explanation.OsChanges.Os.Name String The name of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.Os.Arch String The architecture of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.Os.Version String The version of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.Os.Sp Number The service pack version of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.OsMonitor.Date String The monitored date of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.OsMonitor.Build Number The monitored build of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.OsMonitor.Time String The monitored time of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.OsMonitor.Version String The monitored version of the operating system.
FireEyeNX.Alert.Explanation.OsChanges.Analysis.Mode String The mode of the analysis.
FireEyeNX.Alert.Explanation.OsChanges.Analysis.Product String The product name of the analysis.
FireEyeNX.Alert.Explanation.OsChanges.Analysis.Ftype String The file type of the analysis.
FireEyeNX.Alert.Explanation.OsChanges.Analysis.Version String The version of the analysis.
FireEyeNX.Alert.Explanation.OsChanges.Network.Mode String The mode of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.ProtocolType String The protocol type of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Ipaddress String The IP address of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.DestinationPort Number The destination port address of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Processinfo.Imagepath String The image path of the process in the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Processinfo.Tainted Boolean If the process state is tainted then true, otherwise false for the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Processinfo.Md5sum String The md5sum of the process in the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Processinfo.Pid Number The PID of the process in the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.HttpRequest String The HTTP request of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Timestamp Number The timestamp of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Hostname String The hostname of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.Qtype String The QTYPE of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.AnswerNumber Number The answer number of the network.
FireEyeNX.Alert.Explanation.OsChanges.Network.DnsResponseCode Number The DNS response code of the network.
FireEyeNX.Alert.Explanation.OsChanges.ActionFopen.Mode String The mode of opening the file.
FireEyeNX.Alert.Explanation.OsChanges.ActionFopen.Ext String The extension of opening the file.
FireEyeNX.Alert.Explanation.OsChanges.ActionFopen.Buffered Boolean If the opened file was buffered then true, otherwise false.
FireEyeNX.Alert.Explanation.OsChanges.ActionFopen.NoExtend Boolean If the opened file has no_extend then true, otherwise false.
FireEyeNX.Alert.Explanation.OsChanges.ActionFopen.Name String The name of the action for opening the file.
FireEyeNX.Alert.Explanation.OsChanges.ActionFopen.Timestamp Number The timestamp of opening the file.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Dllname String The DLL file name of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Apiname String The API name of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Address String The address of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Processinfo.Imagepath String The image path of the process in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Processinfo.Md5sum String The md5sum of the process in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Processinfo.Pid Number The PID of the process in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.SrcThread String The source thread name of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Protection String The protection number of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Callstack.CallstackEntry.SymbolName String The symbol name of the call stack entry in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Callstack.CallstackEntry.FrameNumber Number The frame number of the call stack entries in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Callstack.CallstackEntry.ModuleName String The module name of the call stack entry in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Callstack.CallstackEntry.InstructionAddress String The instruction address of the call stack entry in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Callstack.CallstackEntry.SymbolDisplacement String The symbol displacement of the call stack entry in the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Params.Param.Id Number The ID parameter of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Params.Param.Content String The path parameter of the exploit code.
FireEyeNX.Alert.Explanation.OsChanges.Exploitcode.Timestamp Number The timestamp of the exploit codes.
FireEyeNX.Alert.Explanation.OsChanges.Folder.Mode String The mode of the folder.
FireEyeNX.Alert.Explanation.OsChanges.Folder.Processinfo.Imagepath String The image path of the process in the folder.
FireEyeNX.Alert.Explanation.OsChanges.Folder.Processinfo.Md5sum String The md5sum of the process in the folder.
FireEyeNX.Alert.Explanation.OsChanges.Folder.Processinfo.Pid Number The PID of the process in the folder.
FireEyeNX.Alert.Explanation.OsChanges.Folder.SrcThread String The source thread name of the folder.
FireEyeNX.Alert.Explanation.OsChanges.Folder.Value String The path of the folder.
FireEyeNX.Alert.Explanation.OsChanges.Folder.Timestamp Number The timestamp of the folder.
FireEyeNX.Alert.Explanation.OsChanges.File.Mode String The mode of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Fid.Ads String The Alternate Data Stream (ADS) of the FID for the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Fid.Content Number The content of the FID in the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Processinfo.Imagepath String The image path of the process for the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Processinfo.Md5sum String The md5sum of the process for the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Processinfo.Pid Number The PID of the process for the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Processinfo.Tainted Boolean If the process state is tainted then true, otherwise false for the file.
FireEyeNX.Alert.Explanation.OsChanges.File.SrcThread String The source thread name of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Ntstatus String The NTSTATUS of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Filesize Number The size of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Value String The value of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.CreateOptions String The created option of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Timestamp Number The timestamp of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Type String The type of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Sha256sum String The sha256sum of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Sha1sum String The sha1sum of the file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.InspectionType String The inspection type of the portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.TimeDateStamp String The time date stamp of the portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.Characteristics.Names.Name Unknown The list of characteristic names in the portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.Characteristics.Value String The characteristic value in the portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.DllCharacteristics.Names String The characteristic name in the DLL portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.DllCharacteristics.Value String The characteristic value in the DLL portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.Dll String If the portable file is a DLL file then yes, otherwise no file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.Magic String The magic hex value of the portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.Subsystem String The subsystem of the portable executable file.
FireEyeNX.Alert.Explanation.OsChanges.File.PE.Machine String The hexadecimal address of the machine in the file.
FireEyeNX.Alert.Explanation.OsChanges.File.Md5sum String The md5sum of the file.
FireEyeNX.Alert.Explanation.OsChanges.Application.AppName String The app name of the application.
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.Processinfo.Imagepath String The image path of the queried system process.
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.Processinfo.Md5sum String The system time process info of the md5sum that is queried.
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.Processinfo.Pid Number The system time process info of the PID (process ID) that is queried
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.Ntstatus String The NTSTATUS of the system time that is queried.
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.Timestamp Number The timestamp of the system that is queried.
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.SystemTime.Value String The time value of the system that is queried.
FireEyeNX.Alert.Explanation.OsChanges.QuerySystemTime.SystemTime.Time String The time of the system that is queried.
FireEyeNX.Alert.Explanation.OsChanges.EndOfReport String The end of the report.
FireEyeNX.Alert.Explanation.OsChanges.MaliciousAlert.Classtype String The class type of the malicious alert.
FireEyeNX.Alert.Explanation.OsChanges.MaliciousAlert.DisplayMsg String The display message of the malicious alert.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.Hwnd String The hexadecimal address of the dialog detected.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.Processinfo.Imagepath String The image path of the process for the dialog detected.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.Processinfo.Pid Number The PID of the process for the dialog detected.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.Buffered Boolean A flag indicating whether the dialog detected is buffered.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.NoExtend Boolean A flag indicating whether NoExtend is true in the dialog detected.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.Timestamp Number The timestamp of the dialog detected.
FireEyeNX.Alert.Explanation.OsChanges.DialogDetected.DlgId String The dialog ID of the dialog detected.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.Note String A note in the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.Hwnd String The hexadecimal address of the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.Processinfo.Imagepath String The image path of the process for the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.Processinfo.Pid Number The PID of the process for the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.Buffered Boolean A flag indicating whether the dismissed dialog is buffered.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.NoExtend Boolean A flag indicating whether NoExtend is true in the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.Timestamp Number The timestamp of the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.DialogDismissed.DlgId String The dialog ID of the dismissed dialog.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Processinfo.Imagepath String The image path of the process for the Windows Management Instrumentation (WMI) query.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Processinfo.Md5sum String The md5sum of the process for the WMI query.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Processinfo.Pid Number The PID of the process for the WMI query.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Wmicontents.Wmiconent.Query String The query for the WMI content for WMI query.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Wmicontents.Wmicontent.Lang String Language of the WMI content for the WMI query.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Timestamp Number The timestamp of the WMI query.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.Buffered Boolean A flag indicating whether the WMI query is buffered.
FireEyeNX.Alert.Explanation.OsChanges.Wmiquery.NoExtend Boolean A flag indicating whether NoExtend is true in the WMI query.
FireEyeNX.Alert.Explanation.OsChanges.Uac.Mode String The mode of the User Account Control (UAC).
FireEyeNX.Alert.Explanation.OsChanges.Uac.Value String The value of the User Account Control.
FireEyeNX.Alert.Explanation.OsChanges.Uac.Timestamp Number The timestamp of the User Account Control.
FireEyeNX.Alert.Explanation.OsChanges.Uac.Status String The status of the User Account Control.
FireEyeNX.Alert.Explanation.StaticAnalysis.Static.Value String The value of the static analysis.
FireEyeNX.Alert.Explanation.StolenData.Info.Field Unknown The information field of the stolen data.
FireEyeNX.Alert.Explanation.StolenData.Info.Type String The information type of the stolen data.
FireEyeNX.Alert.Explanation.StolenData.EventId Number The event ID of the stolen data.
FireEyeNX.Alert.Src.Ip String The source IP address of the alert.
FireEyeNX.Alert.Src.Mac String The source MAC address of the alert.
FireEyeNX.Alert.Src.Port Number The source port address of the alert.
FireEyeNX.Alert.Src.Host String The source host of the alert.
FireEyeNX.Alert.AlertUrl String The alert URL.
FireEyeNX.Alert.Action String The action of the alert.
FireEyeNX.Alert.Occurred String The time when the alert occurred.
FireEyeNX.Alert.AttackTime String The time when an attack occurred.
FireEyeNX.Alert.Dst.Mac String The destination MAC address of the alert.
FireEyeNX.Alert.Dst.Port Number The destination port address of the alert.
FireEyeNX.Alert.Dst.Ip String The destination IP address of the alert.
FireEyeNX.Alert.ApplianceId String The appliance ID of the alert.
FireEyeNX.Alert.Id Number The ID of the alert.
FireEyeNX.Alert.Name String The type of the alert.
FireEyeNX.Alert.Severity String The severity of the alert.
FireEyeNX.Alert.Uuid String The universally unique identifier (UUID) of the alert.
FireEyeNX.Alert.Ack String A flag indicating whether an acknowledgment is received.
FireEyeNX.Alert.Product String The product name of the alert.
FireEyeNX.Alert.Vlan Number The virtual LAN (VLAN) of the alert.
FireEyeNX.Alert.Malicious String A flag indicating whether the alert is malicious.
FireEyeNX.Alert.ScVersion String The SC version of the alert.

Command Example


#### Context Example

{
“FireEyeNX”: {
“Alert”: [
{
“Ack”: “no”,
“Action”: “notified”,
“AlertUrl”: “https://fireeye-941918/event_stream/events_for_bot?ev_id=11364”,
“ApplianceId”: “866ED7558A08”,
“AttackTime”: “2020-09-29 18:30:01 +0000”,
“Dst”: {
“Mac”: “xx:xx:xx:xx:xx:xx”,
“Ip”: “1.1.1.1”,
“Port”: 0
},
“Explanation”: {
“MalwareDetected”: {
“Malware”: [
{
“Name”: “dummy malware name 1”
}
]
}
},
“Id”: 1,
“Malicious”: “yes”,
“Name”: “dummy name 1”,
“Occurred”: “0000-00-00 02:12:53 +0000”,
“Product”: “WEB_MPS”,
“ScVersion”: “1.000”,
“Severity”: “MINR”,
“Src”: {
“Ip”: “1.1.1.1”,
“Port”: 0,
“Mac”: “xx:xx:xx:xx:xx:xx”
},
“Uuid”: “0b0b0b0b0-0b0b0b-0b0b-0b0b-0b0b0b0b0b”,
“Vlan”: 0
},
{
“Ack”: “no”,
“Action”: “notified”,
“AlertUrl”: “https://fireeye-941918/event_stream/events_for_bot?ev_id=11365”,
“ApplianceId”: “866ED7558A08”,
“AttackTime”: “2020-09-29 19:00:01 +0000”,
“Dst”: {
“Mac”: “xx:xx:xx:xx:xx:xx”,
“Ip”: “1.1.1.1”,
“Port”: 0
},
“Explanation”: {
“MalwareDetected”: {
“Malware”: [
{
“Name”: “dummy malware name 2”
}
]
}
},
“Id”: 2,
“Malicious”: “yes”,
“Name”: “dummy name 2”,
“Occurred”: “0000-00-00 02:12:53 +0000”,
“Product”: “WEB_MPS”,
“ScVersion”: “1.000”,
“Severity”: “MINR”,
“Src”: {
“Ip”: “1.1.1.1”,
“Port”: 0,
“Mac”: “xx:xx:xx:xx:xx:xx”
},
“Uuid”: “0a0a0a0a0-0a0a0a-0a0a-0a0a-0a0a0a0a0a”,
“Vlan”: 0
}
]
}
}


#### Human Readable Output

>### Alert(s) Information
>
>|ID|Distinguisher(UUID)|Malware Name|Alert Type|Victim IP|Time (UTC)|Severity|Malicious|SC Version|Victim Port|Victim MAC Address|Target IP|Target Port|Target MAC Address|
>|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
>| 1 | 0b0b0b0b0-0b0b0b-0b0b-0b0b-0b0b0b0b0b | dummy malware name 1 | dummy name 1 | 1.1.1.1 | 0000-00-00 02:12:53 +0000 | MINR | yes | 1.000 | 0 | xx:xx:xx:xx:xx:xx | 1.1.1.1 | 0 | xx:xx:xx:xx:xx:xx |
>| 2 | 0a0a0a0a0-0a0a0a-0a0a-0a0a-0a0a0a0a0a | dummy malware name 2 | dummy name 2 | 1.1.1.1 | 0000-00-00 02:12:53 +0000 | MINR | yes | 1.000 | 0 | xx:xx:xx:xx:xx:xx | 1.1.1.1 | 0 | xx:xx:xx:xx:xx:xx |

### fireeye-nx-get-artifacts-metadata-by-alert

***
Gets malware artifacts metadata for the specified UUID.

#### Base Command

`fireeye-nx-get-artifacts-metadata-by-alert`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| uuid | Universally unique ID (UUID) of the alert. To retrieve the UUID, execute the fireeye-nx-get-alerts command. | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| FireEyeNX.Alert.Uuid | String | Universally unique ID \(UUID\) of the alert. |
| FireEyeNX.Alert.ArtifactsMetadata.ArtifactType | String | The artifact type. |
| FireEyeNX.Alert.ArtifactsMetadata.ArtifactName | String | The artifact name. |
| FireEyeNX.Alert.ArtifactsMetadata.ArtifactSize | String | The artifact size. |

#### Command Example

```!fireeye-nx-get-artifacts-metadata-by-alert uuid=0b0b0b0b-0b0b-0b0b-0b0b-0b0b0b0b0b0b```

#### Context Example

{
“FireEyeNX”: {
“Alert”: {
“ArtifactsMetadata”: [
{
“ArtifactType”: “artifact type test 1”,
“ArtifactName”: “artifact name test 1”,
“ArtifactSize”: “1010”
},
{
“ArtifactType”: “artifact type test 2”,
“ArtifactName”: “artifact name test 2”,
“ArtifactSize”: “1010”
}
],
“Uuid”: “0b0b0b0b-0b0b-0b0b-0b0b-0b0b0b0b0b0b”
}
}
}


#### Human Readable Output

>### Artifacts Metadata
>
>|Artifact Type|Artifact Name|Artifact Size (Bytes)|
>|---|---|---|
>| artifact type test 1 | artifact name test 1 | 1010 |
>| artifact type test 2 | artifact name test 2 | 1010 |

### fireeye-nx-get-artifacts-by-alert

***
Downloads malware artifacts data for the specified UUID as a zip file.

#### Base Command

`fireeye-nx-get-artifacts-by-alert`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| uuid | The universally unique ID (UUID) of the alert. To get the UUID, execute the fireeye-nx-get-alerts command. | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| File.Size | Number | The size of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Name | String | The name of the file. |
| File.SSDeep | String | The SSDeep hash of the file. |
| File.EntryID | String | The entry ID of the file. |
| File.Info | String | The file information. |
| File.Type | String | The file type. |
| File.MD5 | String | The MD5 hash of the file. |
| File.Extension | String | The file extension. |

#### Command Example

```!fireeye-nx-get-artifacts-by-alert uuid=0b0b0b0b-0b0b-0b0b-0b0b-0b0b0b0b0b0b```

#### Context Example

{
“File”: {
“Size”: 17277,
“SHA1”: “574352bb238d3379429063d71990c0000000000”,
“SHA256”: “1f8ac8eaba9abaf9d12b9b82180a110eab15b14aeec14715f48b4dedaaaaaaaaa”,
“Name”: “0b0b0b0b-0b0b-0b0b-0b0b-0b0b0b0b0b0b.zip”,
“SSDeep”: “000:aaaaaa/aAaAaAaA+AaAaAaAaA:aa0/aAaAaAaAaAaAaA”,
“EntryID”: “150@1”,
“Info”: “zip”,
“Type”: “Zip archive data, at least v1.0 to extract”,
“MD5”: “1aA1aA1aA1aA1aA1aA1aA1aA”,
“Extension”: “zip”
}
}


### fireeye-nx-get-reports

***
Returns reports on selected alerts by specifying a time_frame value or a start_time and end_time of the search range.

#### Base Command

`fireeye-nx-get-reports`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| report_type | The type of report to be queried. | Required |
| type | The output format of the report. Possible values are: "csv" and "pdf", or both depending upon the report type. | Optional |
| start_time | The start time of the search. The search occurs between the start and end times. When specifying a start_time value, you must specify both a start_time and an end_ time value.<br/> Formats:<br/>YYYY-MM-dd<br/>YYYY-MM-ddTHH:mm:ss<br/>N days <br/>N hours<br/>Example:<br/> 2020-05-01 <br/> 2020-05-01T00:00:00 <br/> 2 days <br/> 5 hours. | Optional |
| end_time | The end time of the search. The search occurs between the start and end times. When specifying an end_time value, you must specify both a start_time and an end_time value.<br/> Formats:<br/>YYYY-MM-dd<br/>YYYY-MM-ddTHH:mm:ss<br/>N days <br/>N hours<br/>Example:<br/> 2020-05-01 <br/> 2020-05-01T00:00:00 <br/> 2 days <br/> 5 hours. | Optional |
| time_frame | The time frame in which reports are searched. | Optional |
| limit | The maximum number (N) of items covered by each IPS Top N report. This argument is required only for IPS Top N reports. Possible values are: "25", "50", "75", and "100". | Optional |
| interface | The internet interface. Possible values are: "A", "B", "C", "D", "AB", and "All". This option is required only for IPS reports. | Optional |
| infection_id | The alert ID. To retrieve the alert ID, execute the fireeye-nx-get-alerts command. Use the combination of infection_id and infection_type arguments to specify a unique alert to describe in the Alert Details Report. If one option is used alone and does not specify a unique alert, an error message is produced. | Optional |
| infection_type | The type of the infection. Use the combination of infection_id and infection_type arguments to specify a unique alert to describe in the Alert Details Report. If one option is used alone and does not specify a unique alert, an error message is produced. Possible values are: "malware-object", "malware-callback", "infection-match", "domain-match", and "web-infection". | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| InfoFile.Name | String | The file name. |
| InfoFile.EntryID | String | The ID for locating the file in the War Room. |
| InfoFile.Size | Number | The size of the file \(in bytes\). |
| InfoFile.Type | String | The file type, as determined by libmagic \(same as displayed in the file entries\). |
| InfoFile.Extension | String | The file extension. |
| InfoFile.Info | String | Basic information about the file. |

#### Command Example

```!fireeye-nx-get-reports report_type="IPS Executive Summary Report" type=csv time_frame=between start_time=2020-01-29T23:59:59 end_time=2020-08-29T23:59:59```

#### Context Example

{
“InfoFile”: {
“EntryID”: “1052@8db8b36d-df26-4a3a-8f8a-40e45629ff54”,
“Extension”: “csv”,
“Info”: “csv”,
“Name”: “ips_executive_summary_report_fireeye_20200709_151727878642.csv”,
“Size”: 606,
“Type”: “ASCII text”
}
}


### fireeye-nx-get-events

***
Search and retrieve FireEye events based on several filters.

#### Base Command

`fireeye-nx-get-events`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| duration | The time interval to search. This filter is used with the end_time filter. If the duration is not specified, the system defaults to duration=12_hours, end_time=current_time. | Optional |
| start_time | The start time of the search. This filter is used with the duration filter. If the start_time is specified but not the duration, the system defaults to duration=12_hours, starting at the specified start_time.<br/>Formats:<br/>YYYY-MM-dd<br/>YYYY-MM-ddTHH:mm:ss<br/>N days <br/>N hours<br/>Example:<br/> 2020-05-01 <br/> 2020-05-01T00:00:00 <br/> 2 days <br/> 5 hours | Optional |
| end_time | The end time of the search. This filter is used with the duration filter. If the end_time is specified but not the duration, the system defaults to duration=12_hours, ending at the specified end_time. <br/>Formats:<br/>YYYY-MM-dd<br/>YYYY-MM-ddTHH:mm:ss<br/>N days <br/>N hours<br/>Example:<br/> 2020-05-01 <br/> 2020-05-01T00:00:00 <br/> 2 days <br/> 5 hours | Optional |
| mvx_correlated_only | Whether to include all IPS events or MVX-correlated events only. Default: false | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| FireEyeNX.Event.EventId | Number | The ID of the event. |
| FireEyeNX.Event.Occurred | String | The date and time when the event occurred. |
| FireEyeNX.Event.SrcIp | String | The IP address of the victim. |
| FireEyeNX.Event.SrcPort | Number | The port number of the victim. |
| FireEyeNX.Event.DstIp | String | The IP address of the attacker. |
| FireEyeNX.Event.DstPort | Number | The port number of the attacker. |
| FireEyeNX.Event.Severity | Number | The severity level of the event. |
| FireEyeNX.Event.SignatureRev | Number | The signature revision number of the event. |
| FireEyeNX.Event.SignatureIden | Number | The signature identity number of the event. |
| FireEyeNX.Event.SignatureMatchCnt | Number | The signature match count number of the event. |
| FireEyeNX.Event.Vlan | Number | The virtual LAN \(VLAN\) of the event. |
| FireEyeNX.Event.VmVerified | Boolean | Whether the event VM was verified. |
| FireEyeNX.Event.SrcMac | String | The MAC address of the source machine. |
| FireEyeNX.Event.DstMac | String | The MAC address of the destination machine. |
| FireEyeNX.Event.RuleName | String | The rule name for the event. |
| FireEyeNX.Event.SensorId | String | The sensor ID of the FireEye machine. |
| FireEyeNX.Event.CveId | String | The CVE ID found in the event. |
| FireEyeNX.Event.ActionTaken | Number | The IPS blocking action taken on the event. |
| FireEyeNX.Event.AttackMode | String | The attack mode mentioned in the event. |
| FireEyeNX.Event.InterfaceId | Number | The interface ID of the event. |
| FireEyeNX.Event.Protocol | Number | The protocol used in the event. |
| FireEyeNX.Event.IncidentId | Number | The incident ID of the event on FireEye. |

#### Command Example

```!fireeye-nx-get-events duration=48_hours end_time=2020-08-10T06:31:00```

#### Context Example

{
“FireEyeNX”: {
“Event”: [
{
“EventId”:1,
“Occurred”:”2020-08-10T06:31:00Z”,
“SrcIp”:”1.1.1.1”,
“SrcPort”:1,
“DstIp”:”1.1.1.1”,
“DstPort”:1,
“Vlan”:0,
“SignatureMatchCnt”:1,
“SignatureIden”:1,
“SignatureRev”:1,
“Severity”:1,
“VmVerified”:true,
“SrcMac”:”dummy”,
“DstMac”:”dummy”,
“RuleName”:”dummy”,
“SensorId”:”dummy”,
“CveId”:”CVE-123”,
“ActionTaken”:1,
“AttackMode”:”dummy”,
“InterfaceId”:1,
“Protocol”:1,
“IncidentId”:1
}
]
}
}
```

Human Readable Output

IPS Events

Event ID Time (UTC) Victim IP Attacker IP CVE ID Severity Rule Protocol
1 2020-08-10T06:31:00Z 1.1.1.1 1.1.1.1 CVE-123 1 dummy 1

Configuration parameters

  • url — URL (required)
  • credentials — Username (required)
  • request_timeout — HTTP(S) Request Timeout (in seconds)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • first_fetch — First fetch time interval
  • max_fetch — Fetch Limit
  • fetch_type — Fetch Types
  • fetch_mvx_correlated_events — Fetches MVX-correlated events only
  • malware_type — Alert Malware Type
  • replace_alert_url — Use instance URL for all the fetched alerts URL.
  • fetch_artifacts — Fetch artifacts for each alert
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (5)

  • fireeye-nx-get-alerts

    Search and retrieve FireEye alerts based on several filters.

  • fireeye-nx-get-artifacts-by-alert

    Downloads malware artifacts data for the specified UUID as a zip file.

  • fireeye-nx-get-artifacts-metadata-by-alert

    Gets malware artifacts metadata for the specified UUID.

  • fireeye-nx-get-events

    Search and retrieve FireEye events based on several filters.

  • fireeye-nx-get-reports

    Returns reports on selected alerts by specifying a time_frame value or a start_time and end_time of the search range.

from CommonServerPython import *

""" IMPORTS """

from typing import Any

import dateparser
import urllib3
from requests import Response
from requests.exceptions import (
    InvalidSchema,
    InvalidURL,
    MissingSchema,
    SSLError,
)

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """

DEFAULT_REQUEST_TIMEOUT = 120
REQUEST_TIMEOUT_MAX_VALUE = 9223372036

API_VERSION = "v2.0.0"

DEFAULT_SESSION_TIMEOUT = 15 * 60  # In Seconds
DEFAULT_FETCH_LIMIT = "50"
FORTY_EIGHT_HOURS_IN_SECOND = 172000.0  # Due to API limitations we can retrieve events only from the last 48 hours.
CONTENT_TYPE_JSON = "application/json"
CONTENT_TYPE_ZIP = "application/zip"
DATE_FORMAT_OF_YEAR_MONTH_DAY = "%Y-%m-%d"
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
DATE_FORMAT_WITH_MICROSECOND = "%Y-%m-%dT%H:%M:%S.%fZ"
API_SUPPORT_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.000-00:00"
ALERT_DETAILS_REPORT = "Alert Details Report"
VICTIM_IP = "Victim IP"
TIME_UTC = "Time (UTC)"
DEFAULT_FIRST_FETCH = "12 hours"
ALERT_INCIDENT_TYPE = "FireEye NX Alert"
IPS_EVENT_INCIDENT_TYPE = "FireEye NX IPS Event"

MESSAGES: dict[str, str] = {
    "BAD_REQUEST_ERROR": "An error occurred while fetching the data.",
    "AUTHENTICATION_ERROR": "Unauthenticated. Check the configured Username and Password.",
    "PROXY_ERROR": "Proxy Error - cannot connect to proxy. Either try clearing the 'Use system proxy' check-box or "
    "check the host, authentication details and connection details for the proxy.",
    "BLANK_PROXY_ERROR": "https proxy value is empty. Check XSOAR server configuration ",
    "SSL_CERT_ERROR": "SSL Certificate Verification Failed - try selecting 'Trust any certificate' checkbox in the "
    "integration configuration.",
    "INTERNAL_SERVER_ERROR": "The server encountered an internal error for FireEye NX and was unable to complete "
    "your request.",
    "MISSING_SCHEMA_ERROR": "Invalid API URL. No schema supplied: http(s).",
    "INVALID_SCHEMA_ERROR": "Invalid API URL. Supplied schema is invalid, supports http(s).",
    "INVALID_API_URL": "Invalid API URL.",
    "CONNECTION_ERROR": "Connectivity failed. Check your internet connection or the API URL.",
    "INVALID_ALERT_DETAILS": 'For fetching Alert Details Report, "infection_id" and "infection_type" arguments are required.',
    "INVALID_REPORT_TYPE": "The given value for report_type is invalid.",
    "INVALID_REPORT_OUTPUT_TYPE": "The given value for the argument type (report's format) is invalid. Valid value(s): {}.",
    "NO_RECORDS_FOUND": "No {} were found for the given argument(s).",
    "INVALID_INT_VALUE": "The given value for {} is invalid. Expected integer value.",
    "FETCH_LIMIT_VALIDATION": "Value of Fetch Limit should be an integer and between range 1 to 200.",
    "INVALID_BOOLEAN_VALUE_ERROR": "The given value for {0} argument is invalid. Valid values: true, false.",
    "REQUEST_TIMEOUT_VALIDATION": "HTTP(S) Request timeout parameter must be a positive integer.",
    "REQUEST_TIMEOUT_EXCEED_ERROR": "Value is too large for HTTP(S) Request Timeout.",
    "REQUEST_TIMEOUT": "Request timed out. Check the configured HTTP(S) Request Timeout (in seconds) value.",
    "FIRST_FETCH_ARG_VALIDATION": "The First fetch time interval should be up to 48 hour as per API limitation.",
    "INVALID_TIME_VALIDATION": "The given value for {0} argument is invalid.",
    "INVALID_FETCH_TYPE": "The given value for Fetch Types is invalid. Expected Alerts or/and IPS Events ",
}

URL_SUFFIX: dict[str, str] = {
    "GET_TOKEN": "/auth/login",
    "GET_ARTIFACTS_METADATA": "/artifacts/{}/meta",
    "GET_ARTIFACTS": "/artifacts/{}",
    "GET_REPORTS": "/reports/report",
    "GET_ALERTS": "/alerts",
    "GET_EVENTS": "/events",
}

REPORT_TYPE_LABEL_NAME = {
    "Website Callback Server Report": "mpsCallBackServer",
    "Website Executive Summary": "mpsExecutiveSummary",
    "Website Infected Host Trends": "mpsInfectedHostsTrend",
    "Website Malware Activity": "mpsMalwareActivity",
    "Website Antivirus Report": "mpsWebAVReport",
    "IPS Executive Summary Report": "ipsExecutiveSummary",
    "IPS Top N Attacks Report": "ipsTopNAttack",
    "IPS Top N Attackers Report": "ipsTopNAttacker",
    "IPS Top N Victims Report": "ipsTopNVictim",
    "IPS Top N MVX-Correlated Report": "ipsTopNMvxVerified",
    ALERT_DETAILS_REPORT: "alertDetailsReport",
}
REPORT_TYPE_ALLOWED_FORMAT = {
    "Website Callback Server Report": ["csv"],
    "Website Executive Summary": ["pdf"],
    "Website Infected Host Trends": ["csv"],
    "Website Malware Activity": ["pdf", "csv"],
    "Website Antivirus Report": ["csv"],
    "IPS Executive Summary Report": ["pdf", "csv"],
    "IPS Top N Attacks Report": ["pdf", "csv"],
    "IPS Top N Attackers Report": ["pdf", "csv"],
    "IPS Top N Victims Report": ["pdf", "csv"],
    "IPS Top N MVX-Correlated Report": ["pdf", "csv"],
    ALERT_DETAILS_REPORT: ["pdf"],
}

PLATFORM_SEVERITY_TO_SEVERITY_MAP = {
    "10": 4,
    "9": 4,
    "8": 3,
    "7": 3,
    "6": 2,
    "5": 2,
    "4": 2,
    "3": 1,
    "2": 1,
    "1": 1,
    "0": 0,
}


def strip_blank(args: dict) -> dict:
    nargs = {}
    for key, value in args.items():
        if isinstance(value, str):
            value = value.strip()
        nargs[key] = value
    return nargs


class Client(BaseClient):
    """
    Client to use in integration with powerful http_request.
    It extends the base client and uses the http_request method for the API request.
    Handle some exceptions externally.
    """

    def __init__(
        self,
        base_url: str,
        verify: bool,
        proxy: bool,
        auth: tuple[str, str],
        request_timeout: int,
    ):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy, auth=auth)
        self.request_timeout = request_timeout

    def http_request(
        self,
        method: str,
        url_suffix: str,
        json_data=None,
        params=None,
        headers=None,
        ok_codes=None,
    ):
        """
            Override http_request method from BaseClient class. This method will print an error based on status code
            and exceptions.

        :type method: ``str``
        :param method: The HTTP method, for example: GET, POST, and so on.

        :type url_suffix: ``str``
        :param url_suffix: The API endpoint.

        :type json_data: ``dict``
        :param json_data: The dictionary to send in a 'POST' request.

        :type params: ``dict``
        :param params: URL parameters to specify the query.

        :type headers: ``dict``
        :param headers: Headers to send in the request. If None, will use self._headers.

        :type ok_codes: ``tuple``
        :param ok_codes: The tuple of acceptable status codes. Default is None.

        :return: Depends on the resp_type parameter
        :rtype: ``dict`` or ``str`` or ``requests.Response``
        """
        resp = Response()
        try:
            resp = super()._http_request(
                method=method,
                url_suffix=url_suffix,
                json_data=json_data,
                params=params,
                headers=headers,
                resp_type="response",
                timeout=self.request_timeout,
                ok_codes=ok_codes or (200, 201),
                error_handler=self.handle_error_response,
            )
        except MissingSchema:
            raise ValueError(MESSAGES["MISSING_SCHEMA_ERROR"])
        except InvalidSchema:
            raise ValueError(MESSAGES["INVALID_SCHEMA_ERROR"])
        except InvalidURL:
            raise ValueError(MESSAGES["INVALID_API_URL"])
        except DemistoException as e:
            self.handle_demisto_exception(e)

        if resp.ok:
            content_type = resp.headers.get("Content-Type", "")
            if content_type == CONTENT_TYPE_JSON:
                # Handle empty response
                if resp.text == "":
                    return resp
                else:
                    return resp.json()
            elif self.is_supported_context_type(content_type):
                return resp
            return None
        return None

    @staticmethod
    def is_supported_context_type(content_type: str):
        """
        Check whether content type is supported or not.
        :param content_type: content type of header.
        :return: boolean flag, whether content type is supported or not.
        """
        return (
            content_type == "application/pdf"
            or content_type == "text/csv"
            or content_type == CONTENT_TYPE_ZIP
            or content_type == "application/octet-stream"
        )

    @staticmethod
    def handle_demisto_exception(e):
        """
        Handle Demisto exception based on string.

        :param e: Demisto Exception
        :return: Error message
        """
        if "Proxy Error" in str(e):
            raise ConnectionError(MESSAGES["PROXY_ERROR"])
        elif "ReadTimeoutError" in str(e):
            raise ConnectionError(MESSAGES["REQUEST_TIMEOUT"])
        elif "ConnectionError" in str(e) or "ConnectTimeoutError" in str(e):
            raise ConnectionError(MESSAGES["CONNECTION_ERROR"])
        elif "SSLError" in str(e):
            raise SSLError(MESSAGES["SSL_CERT_ERROR"])
        else:
            raise e

    @staticmethod
    def handle_error_response(resp):
        """
        Handle error response and display user specific error message based on status code.

        :param resp: response from API.
        :return: raise DemistoException based on status code.
        """
        error_message = ""
        error_message_with_reason = ""
        try:
            error_message = resp.json().get("fireeyeapis", {}).get("description", "").strip()
            error_message = error_message.replace("\n", "")
            if error_message:
                error_message_with_reason = f"Reason: {error_message}"
        except ValueError:  # ignoring json parsing errors
            pass
        if resp.headers.get("Content-Type", "") == CONTENT_TYPE_ZIP:
            error_message = error_message_with_reason = resp.text

        status_code_messages = {
            400: f"{MESSAGES['BAD_REQUEST_ERROR']} {error_message_with_reason}",
            401: MESSAGES["AUTHENTICATION_ERROR"],
            403: error_message,
            404: error_message,
            406: error_message,
            407: MESSAGES["PROXY_ERROR"],
            500: MESSAGES["INTERNAL_SERVER_ERROR"],
            503: MESSAGES["INTERNAL_SERVER_ERROR"],
        }

        if resp.status_code in status_code_messages:
            demisto.debug(f"Response Code: {resp.status_code}, Reason: {status_code_messages[resp.status_code]}")
            raise DemistoException(status_code_messages[resp.status_code])
        else:
            raise DemistoException(resp.raise_for_status())

    def get_api_token(self):
        """
        Retrieve new api token and set it to integration context.
        if api token is not not found or expired, making api call to retrieve api token and set it to integration
        context.

        :return: api-token
        """
        integration_context = demisto.getIntegrationContext()
        api_token = integration_context.get("api_token")
        valid_until = integration_context.get("valid_until")

        # Return api token from integration context, if found and not expired
        if api_token and valid_until and time.time() < valid_until:
            demisto.debug("Retrieved api-token from integration cache.")
            return api_token

        headers = {"Accept": CONTENT_TYPE_JSON}

        demisto.debug("Calling authentication API for retrieve api-token")
        resp = self.http_request(method="POST", url_suffix=URL_SUFFIX["GET_TOKEN"], headers=headers)
        integration_context = self.set_integration_context(resp)

        return integration_context.get("api_token")

    @staticmethod
    def set_integration_context(resp):
        """
        set api token and expiry time in integration configuration context.
        Will raise value error if api-token is not found.

        :param resp: resp from API.
        :return: integration context
        """
        integration_context = demisto.getIntegrationContext()
        api_token = resp.headers.get("X-FeApi-Token")
        if api_token:
            integration_context["api_token"] = api_token
            integration_context["valid_until"] = time.time() + DEFAULT_SESSION_TIMEOUT
        else:
            raise ValueError("No api token found. Please try again")
        demisto.setIntegrationContext(integration_context)
        return integration_context


""" HELPER FUNCTION"""


def set_attachment_file(client, incident: dict, uuid: str, headers: dict):
    """
    Set attachment in incident entry.

    :param client: Client object.
    :param incident: Incident entry.
    :param uuid: uuid of alert.
    :param headers: Header of API which will pass to get artifact API.
    """

    # Call get artifacts data api
    headers["Accept"] = CONTENT_TYPE_ZIP
    artifacts_resp = client.http_request(
        "GET",
        url_suffix=URL_SUFFIX["GET_ARTIFACTS"].format(uuid),
        headers=headers,
        ok_codes=(200, 201, 404),
    )
    if artifacts_resp and int(artifacts_resp.headers.get("Content-Length", "0")) > 0:
        # Create file from Content
        file_name = f"{uuid}.zip"

        attachment_file = fileResult(filename=file_name, data=artifacts_resp.content)

        incident["attachment"] = [{"path": attachment_file["FileID"], "name": file_name}]


def get_incidents_for_alert(**kwargs) -> tuple[list[dict[str, Any]], dict[str, Any]]:
    """
    Return List of incidents for alert.

    :param kwargs: Contains all required arguments.
    :return: Incident List for alert.
    """
    incidents: list[dict[str, Any]] = []
    last_run = kwargs["last_run"]
    next_run = last_run.get("alerts", {})

    headers = {
        "X-FeApi-Token": kwargs["client"].get_api_token(),
        "Accept": CONTENT_TYPE_JSON,
    }

    params = {
        "start_time": time.strftime(API_SUPPORT_DATE_FORMAT, time.localtime(kwargs["start_time"])),
        "duration": "48_hours",
    }

    if kwargs["malware_type"]:
        params["malware_type"] = kwargs["malware_type"]

    # http call
    resp = kwargs["client"].http_request(
        method="GET",
        url_suffix=URL_SUFFIX["GET_ALERTS"],
        params=params,
        headers=headers,
    )

    total_records = resp.get("alertsCount", 0)
    if total_records > 0:
        if kwargs["replace_alert_url"]:
            replace_alert_url_key_domain_to_instance_url(resp.get("alert", []), kwargs["instance_url"])
        count = kwargs["fetch_count"]

        next_incidents_ids: List[str] = []
        alerts = resp.get("alert", [])
        alerts.sort(key=lambda x: x.get("occurred"))
        last_alert_start_time = last_run.get("alerts", {}).get("start_time")
        last_alert_ids = last_run.get("alerts", {}).get("alert_ids", [])
        next_alert_start_time = alerts[: kwargs["fetch_limit"]][-1].get("occurred", "")

        for alert in alerts:
            # skip on duplicate incident
            if (
                last_alert_start_time
                and last_alert_ids
                and last_alert_start_time == alert.get("occurred", "")
                and alert.get("id", "") in last_alert_ids
            ):
                continue
            # set incident
            context_alert = remove_empty_entities(alert)
            context_alert["incidentType"] = ALERT_INCIDENT_TYPE
            if count >= kwargs["fetch_limit"]:
                break

            occurred_date = dateparser.parse(context_alert.get("occurred", ""))
            assert occurred_date is not None
            if ((alert_occurred_time := alert.get("occurred")) and next_alert_start_time == alert_occurred_time) and (
                alert_id := alert.get("id")
            ):
                # Save the alert id for the next fetch dedup
                next_incidents_ids.append(alert_id)

            incident = {
                "name": context_alert.get("name", ""),
                "occurred": occurred_date.strftime(DATE_FORMAT_WITH_MICROSECOND),
                "rawJSON": json.dumps(context_alert),
                "dbotMirrorId": str(alert.get("id")),
            }

            if not kwargs["is_test"] and alert.get("uuid", "") and kwargs["fetch_artifacts"]:
                set_attachment_file(
                    client=kwargs["client"],
                    incident=incident,
                    uuid=alert.get("uuid", ""),
                    headers=headers,
                )

            remove_nulls_from_dictionary(incident)
            incidents.append(incident)
            count += 1

        parsed_incidents_str = [
            f"Incident name: {incident.get('name')} Incident date: {incident.get('occurred')}\n" for incident in incidents
        ]
        demisto.debug(f"FireeyeNX Alerts: {parsed_incidents_str}")
        if next_incidents_ids:
            next_run = {"start_time": next_alert_start_time, "alert_ids": next_incidents_ids}
    return incidents, next_run


def get_incidents_for_event(client: Client, start_time: float, fetch_limit: int, mvx_correlated: bool, last_run: dict):
    """
    Return List of incidents for event.

    :param client: Client object.
    :param start_time: It contains the timestamp in milliseconds on when to start fetching incidents.
    :param fetch_limit: limit for number of fetch incidents per fetch.
    :param mvx_correlated: The boolean flag that tell us to fetch events which only mvx correlated.
    :param last_run: The last run object.
    :return: Incident List for event.
    """
    incidents: list[dict[str, Any]] = []
    next_run = last_run.get("events", {})

    # Preparing header and parameters
    headers = {
        "X-FeApi-Token": client.get_api_token(),
        "Accept": CONTENT_TYPE_JSON,
    }

    params = {
        "start_time": time.strftime(API_SUPPORT_DATE_FORMAT, time.localtime(start_time)),
        "duration": "48_hours",
        "event_type": "Ips Event",
    }

    if mvx_correlated:
        params["mvx_correlated_only"] = "true"

    # http call
    resp = client.http_request(
        method="GET",
        url_suffix=URL_SUFFIX["GET_EVENTS"],
        params=params,
        headers=headers,
    )

    total_records = len(resp.get("events", []))
    count = 0
    if total_records > 0:
        next_incidents_ids = []
        events = resp.get("events", [])
        events.sort(key=lambda x: x.get("occurred"))
        last_event_start_time = last_run.get("events", {}).get("start_time")
        last_event_ids = last_run.get("events", {}).get("event_ids", [])
        next_event_start_time = events[:fetch_limit][-1].get("occurred", "")

        for event in events:
            # skip on duplicate incident
            if (
                last_event_start_time
                and last_event_ids
                and last_event_start_time == event.get("occurred", "")
                and event.get("eventId", "") in last_event_ids
            ):
                continue

            # set incident
            context_event = remove_empty_entities(event)
            context_event["incidentType"] = IPS_EVENT_INCIDENT_TYPE
            if count >= fetch_limit:
                break
            if ((event_occurred_time := event.get("occurred")) and next_event_start_time == event_occurred_time) and (
                event_id := event.get("eventId")
            ):
                # Save the event id for the next fetch dedup
                next_incidents_ids.append(event_id)
            incident = {
                "name": context_event.get("ruleName", ""),
                "occurred": context_event.get("occurred", ""),
                "severity": PLATFORM_SEVERITY_TO_SEVERITY_MAP.get(str(context_event.get("severity", 0)), 0),
                "rawJSON": json.dumps(context_event),
                "dbotMirrorId": str(event.get("eventId")),
            }
            remove_nulls_from_dictionary(incident)
            incidents.append(incident)
            count += 1
        parsed_incidents_str = [
            f"Incident name: {incident.get('name')} Incident date: {incident.get('occurred')}\n" for incident in incidents
        ]
        demisto.debug(f"FireeyeNX IPS Events: {parsed_incidents_str}")
        if next_incidents_ids:
            next_run = {"start_time": next_event_start_time, "event_ids": next_incidents_ids}
    return incidents, count, next_run


def validate_fetch_type(fetch_type):
    """
    Validate fetch type.

    :param fetch_type: A list contain types which user want to fetch.
    :return:
    """
    if type(fetch_type) is list:
        if len(fetch_type) == 0:
            raise ValueError(MESSAGES["INVALID_FETCH_TYPE"])

        if "Alerts" not in fetch_type and "IPS Events" not in fetch_type:
            raise ValueError(MESSAGES["INVALID_FETCH_TYPE"])


def validate_date_range(fetch_time: str):
    """
    Validate date range and it should be up to 2 days as per API limitation.
    Will raise ValueError() if date is not in range.

    :param fetch_time: A time in format of (<number> <unit>). eg. 1 hour.
    """
    two_days_before_time = datetime.utcnow() - timedelta(hours=48)

    start_time, _ = parse_date_range(fetch_time, utc=True)

    if start_time < two_days_before_time:
        raise ValueError(MESSAGES["FIRST_FETCH_ARG_VALIDATION"])


def pascal_case(st) -> str:
    """
    Covert string to pascal case.

    :param st: string
    :return: pascal case string.
    """
    if st.find("-") != -1 or st.find("_") != -1:
        st = "".join(a.capitalize() for a in re.split("-|_", st))
    return st[:1].upper() + st[1 : len(st)]


def remove_dash_and_underscore_from_key(d):  # type: ignore
    """
    Recursively traverse dict and change keys into pascal case.

    :param d: Input dictionary.
    :return: Dictionary with pascal case key.
    """

    if not isinstance(d, dict | list):
        return d
    elif isinstance(d, list):
        return [remove_dash_and_underscore_from_key(value) for value in d]
    else:
        return {pascal_case(key): remove_dash_and_underscore_from_key(value) for key, value in d.items()}


def get_request_timeout(request_timeout: str) -> int:
    """
    Validate and return the request timeout parameter.
    The parameter must be a positive integer.
    Default value is set to 60 seconds for API request timeout.
    Will raise ValueError if inappropriate input given.

    :params req_timeout: Request timeout value.
    :return: boolean
    """
    try:
        request_timeout_str = request_timeout if request_timeout else str(DEFAULT_REQUEST_TIMEOUT)
        request_timeout_int = int(request_timeout_str)
    except ValueError:
        raise ValueError(MESSAGES["REQUEST_TIMEOUT_VALIDATION"])

    if request_timeout_int <= 0:
        raise ValueError(MESSAGES["REQUEST_TIMEOUT_VALIDATION"])
    elif request_timeout_int > REQUEST_TIMEOUT_MAX_VALUE:
        raise ValueError(MESSAGES["REQUEST_TIMEOUT_EXCEED_ERROR"])

    return request_timeout_int


def get_fetch_limit(fetch_limit):
    """
    Retrieve fetch limit from demisto arguments and validate it.
    Will raise ValueError if inappropriate input given.

    :param fetch_limit: The maximum number of incident want to fetch.
    :return: fetch limit
    """
    fetch_limit = fetch_limit if fetch_limit else DEFAULT_FETCH_LIMIT
    try:
        fetch_limit_int = int(fetch_limit)
        if not 1 <= fetch_limit_int <= 200:
            raise ValueError
    except ValueError:
        raise ValueError(MESSAGES["FETCH_LIMIT_VALIDATION"])

    return fetch_limit_int


def generate_report_file_name(args: dict[str, Any]) -> str:
    """
    Create the filename of the info file of report.

    :param args: Input arguments
    :return: The report file name
    """
    return (
        f"{args.get('report_type', '').lower().replace(' ', '_')}_fireeye_"
        f"{datetime.now().strftime('%Y-%m-%d_%H:%M:%S')}."
        f"{args.get('type', REPORT_TYPE_ALLOWED_FORMAT[args.get('report_type', '')][0])}"
    )


def validate_alert_report_type_arguments(args: dict[str, Any], params: dict[str, Any]) -> dict[str, Any]:
    """
    Validates the arguments required for alert details report type from input arguments of reports command.
    Will raise ValueError if inappropriate input given.

    :param args: Input arguments
    :param params: Params to be passed in API call
    :return: Params to be passed in API call
    """
    arg_keys = args.keys()

    if "infection_id" in arg_keys and "infection_type" in arg_keys:
        params["infection_id"] = args.get("infection_id", "")
        params["infection_type"] = args.get("infection_type", "")
    else:
        raise ValueError(MESSAGES["INVALID_ALERT_DETAILS"])
    return params


def validate_ips_report_type_arguments(args: dict[str, Any], params: dict[str, Any]) -> dict[str, Any]:
    """
    Validates the arguments required for IPS report types from input arguments of reports command.
    Will raise ValueError if inappropriate input given.

    :param args: Input arguments
    :param params: Params to be passed in API call
    :return: Params to be passed in API call
    """
    arg_keys = args.keys()

    if "limit" in arg_keys:
        limit = args.get("limit", "")
        try:
            params["limit"] = int(limit)
        except ValueError:
            raise ValueError(MESSAGES["INVALID_INT_VALUE"].format("limit"))
    if "interface" in arg_keys:
        params["interface"] = args.get("interface", "") if args.get("interface", "") != "All" else "all"
    return params


def validate_time_parameters(args: dict[str, Any], params: dict[str, Any]) -> dict[str, Any]:
    """
    Validates the time arguments from input arguments of reports command.

    :param args: Input arguments
    :param params: Params to be passed in API call
    :return: Params to be passed in API call
    """
    arg_keys = args.keys()

    if "time_frame" in arg_keys:
        params["time_frame"] = args.get("time_frame", "")

    if "start_time" in arg_keys:
        start_time = args.get("start_time", "")
        date_time = dateparser.parse(start_time)
        if date_time:
            params["start_time"] = str(date_time.strftime(API_SUPPORT_DATE_FORMAT))
        else:
            params["start_time"] = start_time

    if "end_time" in arg_keys:
        end_time = args.get("end_time", "")
        date_time = dateparser.parse(end_time)
        if date_time:
            params["end_time"] = str(date_time.strftime(API_SUPPORT_DATE_FORMAT))
        else:
            params["end_time"] = end_time

    return params


def get_reports_params(args: dict[str, Any]) -> dict[str, Any]:
    """
    Validates the input arguments of command and returns parameter dictionary. This function validates the
    report_type, output format(type), time arguments.
    Will raise ValueError if inappropriate input given.

    :param args: Input arguments of command
    :return: Params dict or error message
    """
    params: dict[str, Any] = {}
    arg_keys = args.keys()

    report_type = args.get("report_type", "")
    if report_type not in REPORT_TYPE_LABEL_NAME:
        raise ValueError(MESSAGES["INVALID_REPORT_TYPE"])
    params["report_type"] = REPORT_TYPE_LABEL_NAME[report_type]

    if "type" in arg_keys:
        output_type = args.get("type", "")
        if output_type not in REPORT_TYPE_ALLOWED_FORMAT[report_type]:
            raise ValueError(MESSAGES["INVALID_REPORT_OUTPUT_TYPE"].format(", ".join(REPORT_TYPE_ALLOWED_FORMAT[report_type])))
        params["type"] = output_type

    params = validate_time_parameters(args, params)

    params = validate_ips_report_type_arguments(args, params)

    if report_type == ALERT_DETAILS_REPORT:
        params = validate_alert_report_type_arguments(args, params)

    return params


def add_time_suffix_into_arguments(args: dict[str, Any]):
    """
    Add time suffix into arguments.

    :param args: arguments of alerts.
    :return: Add suffix to date format if full format is not given.
    """
    arg_keys = args.keys()
    if "start_time" in arg_keys:
        start_time = args.get("start_time", "")
        date_time = dateparser.parse(start_time)
        if date_time:
            args["start_time"] = str(date_time.strftime(API_SUPPORT_DATE_FORMAT))
        else:
            raise ValueError(MESSAGES["INVALID_TIME_VALIDATION"].format("start_time"))

    if "end_time" in arg_keys:
        end_time = args.get("end_time", "")
        date_time = dateparser.parse(end_time)
        if date_time:
            args["end_time"] = str(date_time.strftime(API_SUPPORT_DATE_FORMAT))
        else:
            raise ValueError(MESSAGES["INVALID_TIME_VALIDATION"].format("end_time"))


def get_events_params(args: dict[str, Any]) -> dict[str, Any]:
    """
    Validates the input arguments of command and returns parameter dictionary
    or raises ValueError in case of validation failed.

    :param args: Input arguments of command
    :return: Params dict or error message
    """
    params: dict[str, Any] = {"event_type": "Ips Event"}
    arg_keys = args.keys()

    if "duration" in arg_keys:
        params["duration"] = args.get("duration", "")

    if "start_time" in arg_keys:
        start_time = args.get("start_time", "")
        date_time = dateparser.parse(start_time)
        if date_time:
            params["start_time"] = str(date_time.strftime(API_SUPPORT_DATE_FORMAT))
        else:
            raise ValueError(MESSAGES["INVALID_TIME_VALIDATION"].format("start_time"))

    if "end_time" in arg_keys:
        end_time = args.get("end_time", "")
        date_time = dateparser.parse(end_time)
        if date_time:
            params["end_time"] = str(date_time.strftime(API_SUPPORT_DATE_FORMAT))
        else:
            raise ValueError(MESSAGES["INVALID_TIME_VALIDATION"].format("end_time"))

    if "mvx_correlated_only" in arg_keys:
        mvx_correlated_only = args.get("mvx_correlated_only", "").lower()
        try:
            mvx_correlated_only = argToBoolean(mvx_correlated_only)
            params["mvx_correlated_only"] = mvx_correlated_only
        except ValueError:
            raise ValueError(MESSAGES["INVALID_BOOLEAN_VALUE_ERROR"].format("mvx_correlated_only"))

    return params


def prepare_hr_for_artifact_metadata(artifacts_info: list[dict[str, Any]]) -> str:
    """
    Prepare Human readable for get artifact metadata.

    :param artifacts_info: List contain artifact metadata information.
    :return: Markdown format in string.
    """
    artifacts_info_hr_list = []

    for artifact in artifacts_info:
        artifacts_dict = {
            "Artifact Type": artifact.get("artifactType", ""),
            "Artifact Name": artifact.get("artifactName", ""),
            "Artifact Size (Bytes)": artifact.get("artifactSize", ""),
        }
        artifacts_info_hr_list.append(artifacts_dict)

    return tableToMarkdown(
        "Artifacts Metadata",
        artifacts_info_hr_list,
        headers=["Artifact Type", "Artifact Name", "Artifact Size (Bytes)"],
        removeNull=True,
    )


def remove_empty_entities(d):
    """
    Recursively remove empty lists, empty dicts, or None elements from a dictionary.
    Note. This is extended feature of CommonServerPython.py remove_empty_elements() method as it was not removing
    empty character x == ''.

    :param d: Input dictionary.
    :return: Dictionary with all empty lists, and empty dictionaries removed.
    """

    def empty(x):
        return x is None or x == {} or x == [] or x == ""

    if not isinstance(d, dict | list):
        return d
    elif isinstance(d, list):
        return [value for value in (remove_empty_entities(value) for value in d) if not empty(value)]
    else:
        return {
            key: value for key, value in ((key, remove_empty_entities(value)) for key, value in d.items()) if not empty(value)
        }


def prepare_hr_for_alert_response(resp: dict) -> str:
    """
    Prepare human readable for alert response.

    :param resp: Dictionary of API response
    :return: Markdown format for human readable.
    """
    alert_hr_list = []

    for alert in resp.get("alert", []):
        artifacts_dict = {
            "ID": alert.get("id", ""),
            "Distinguisher(UUID)": alert.get("uuid", ""),
            "Malware Name": alert.get("explanation", {}).get("malwareDetected", {}).get("malware", [{}])[0].get("name", ""),
            "Alert Type": alert.get("name", ""),
            VICTIM_IP: alert.get("src", {}).get("ip", ""),
            TIME_UTC: alert.get("occurred", ""),
            "Severity": alert.get("severity", ""),
            "Malicious": alert.get("malicious", ""),
            "SC Version": alert.get("scVersion", ""),
            "Victim Port": alert.get("src", {}).get("port", ""),
            "Victim MAC Address": alert.get("src", {}).get("mac", ""),
            "Target IP": alert.get("dst", {}).get("ip", ""),
            "Target Port": alert.get("dst", {}).get("port", ""),
            "Target MAC Address": alert.get("dst", {}).get("mac", ""),
        }
        alert_hr_list.append(artifacts_dict)
    headers = [
        "ID",
        "Distinguisher(UUID)",
        "Malware Name",
        "Alert Type",
        VICTIM_IP,
        TIME_UTC,
        "Severity",
        "Malicious",
        "SC Version",
        "Victim Port",
        "Victim MAC Address",
        "Target IP",
        "Target Port",
        "Target MAC Address",
    ]
    return tableToMarkdown("Alert(s) Information", alert_hr_list, headers=headers, removeNull=True)


def prepare_hr_for_events(events_info) -> str:
    """
    Prepare the Human readable info for events command.

    :param events_info: The events data.
    :return: Human readable.
    """
    hr_list = []
    for record in events_info:
        hr_record = {
            "Event ID": record.get("eventId", None),
            TIME_UTC: record.get("occurred", ""),
            VICTIM_IP: record.get("srcIp", ""),
            "Attacker IP": record.get("dstIp", ""),
            "CVE ID": record.get("cveId", ""),
            "Severity": record.get("severity", None),
            "Rule": record.get("ruleName", ""),
            "Protocol": record.get("protocol", None),
        }
        hr_list.append(hr_record)

    return tableToMarkdown(
        "IPS Events",
        hr_list,
        [
            "Event ID",
            TIME_UTC,
            VICTIM_IP,
            "Attacker IP",
            "CVE ID",
            "Severity",
            "Rule",
            "Protocol",
        ],
        removeNull=True,
    )


def replace_alert_url_key_domain_to_instance_url(alerts_resp: list, instance_url: str):
    """
    Change domain of 'alertUrl' to the instance URL.

    :param alerts_resp: List contain dictionary of alerts.
    :param instance_url: URL to connect to the FireEye NX.
    """

    def replace_url(alert_url: str, prefix_url: str) -> str:
        """
        Replace alert url domain to prefix url.

        :param alert_url: Actual url that getting from response.
        :param prefix_url: URL to connect to the FireEye NX.
        :return:
        """
        if alert_url.startswith("http://"):  # NOSONAR
            alert_url = alert_url.replace("http://", "")  # NOSONAR

        elif alert_url.startswith("https://"):
            alert_url = alert_url.replace("https://", "")

        if alert_url.startswith("www."):
            alert_url = alert_url.replace("www.", "")

        elif alert_url.startswith("WWW."):
            alert_url = alert_url.replace("WWW.", "")

        if not prefix_url.endswith("/"):
            prefix_url = f"{prefix_url + '/'}"

        alert_url_split = alert_url.split("/", 1)

        suffix_url = "".join(alert_url_split[count] for count in range(len(alert_url_split)) if count != 0)

        return f"{prefix_url + suffix_url}"

    for alert_index in range(len(alerts_resp)):
        if alerts_resp[alert_index].get("alertUrl"):
            alerts_resp[alert_index]["alertUrl"] = replace_url(alerts_resp[alert_index]["alertUrl"], instance_url)


def update_start_time(start_time: float) -> float:
    """Check that the start time is bigger (later) than the current time - ~48 hours (due to API limitations we can fetch
        events only from the last 48 hours) if not it returns the current time - ~48 hours

    Args:
        start_time: The time we use for start fetching.

    Returns:
        The return value. The time we use for start fetching. Validated it's not less (earlier) than current time - ~48 hours.
    """
    current_timestamp = date_to_timestamp(datetime.utcnow(), DATE_FORMAT) / 1000.0
    if current_timestamp - FORTY_EIGHT_HOURS_IN_SECOND > start_time:
        updated_start_time = current_timestamp - FORTY_EIGHT_HOURS_IN_SECOND
        demisto.debug(
            f"Start time {start_time=} is earlier than [current time {current_timestamp=} - ~ 48 hours], "
            f"Start time updated to: {updated_start_time=}"
        )
        return updated_start_time
    return start_time


""" REQUESTS FUNCTIONS """


def test_function(**kwargs) -> str:
    """
    Performs test connectivity by valid http response.

    :param kwargs: Contains all required parameters.
    :return: raise ValueError if any error occurred during connection
    """
    if kwargs["is_fetch"]:
        fetch_limit = get_fetch_limit(kwargs["fetch_limit"])

        # getting numeric value from string representation
        start_time, _ = parse_date_range(kwargs["first_fetch_time"], date_format=DATE_FORMAT, utc=True)

        # validate start_time should be less then 48 hour as per API limitation
        validate_date_range(kwargs["first_fetch_time"])
        validate_fetch_type(kwargs["fetch_type"])

        first_fetch = date_to_timestamp(start_time, date_format=DATE_FORMAT) / 1000
        fetch_incidents(
            client=kwargs["client"],
            last_run=demisto.getLastRun(),
            first_fetch=first_fetch,
            fetch_limit=fetch_limit,
            malware_type=kwargs["malware_type"],
            is_test=True,
            fetch_type=kwargs["fetch_type"],
            mvx_correlated=kwargs["mvx_correlated"],
            replace_alert_url=kwargs["replace_alert_url"],
            instance_url=kwargs["instance_url"],
            fetch_artifacts=kwargs["fetch_artifacts"],
        )
    else:
        headers = {"Accept": CONTENT_TYPE_JSON}
        kwargs["client"].http_request(method="POST", url_suffix=URL_SUFFIX["GET_TOKEN"], headers=headers)

    return "ok"


@logger
def get_artifacts_metadata_by_alert_command(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Gets malware artifacts metadata for the specified UUID.

    :param client: The Client object used for request
    :param args: The command arguments
    :return: CommandResults
    """
    uuid = args.get("uuid", "")
    uuid = uuid.lower()

    # Preparing header
    headers = {
        "Accept": CONTENT_TYPE_JSON,
        "X-FeApi-Token": client.get_api_token(),
    }

    # Call get artifacts metadata api
    resp = client.http_request(
        "GET",
        url_suffix=URL_SUFFIX["GET_ARTIFACTS_METADATA"].format(uuid),
        headers=headers,
    )

    artifacts_info = resp.get("artifactsInfoList", [])
    if len(artifacts_info) == 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("artifacts metadata")

    # Create entry context
    artifacts_metadata_custom_ec = createContext(artifacts_info, removeNull=True)

    # Prepare human readable
    hr = prepare_hr_for_artifact_metadata(artifacts_info)

    custom_ec_for_artifact_metadata = {
        "ArtifactsMetadata": artifacts_metadata_custom_ec,
        "Uuid": uuid,
    }

    # Remove dash, underscore from key and make it pascal case.
    custom_ec = remove_dash_and_underscore_from_key(custom_ec_for_artifact_metadata)

    return CommandResults(
        outputs_prefix="FireEyeNX.Alert",
        outputs_key_field="Uuid",
        outputs=custom_ec,
        readable_output=hr,
        raw_response=resp,
    )


@logger
def get_artifacts_by_alert_command(client: Client, args: dict[str, Any]) -> str | dict[str, Any]:
    """
    Downloads malware artifacts data for the specified UUID as a zip file.

    :param client: The Client object used for request
    :param args: The command arguments
    :return: Dictionary of file info or empty result message
    """
    uuid = args.get("uuid", "")
    uuid = uuid.lower()

    # Preparing header
    headers = {
        "X-FeApi-Token": client.get_api_token(),
    }

    # Call get artifacts data api
    artifacts_resp = client.http_request(
        "GET",
        url_suffix=URL_SUFFIX["GET_ARTIFACTS"].format(uuid),
        headers=headers,
    )

    # Create file from Content
    if int(artifacts_resp.headers.get("Content-Length", "0")) > 0:
        file_name = f"{uuid}.zip"
        file_entry = fileResult(filename=file_name, data=artifacts_resp.content)
        return file_entry
    else:
        return MESSAGES["NO_RECORDS_FOUND"].format("artifacts data")


@logger
def get_reports_command(client: Client, args: dict[str, Any]) -> str | dict[str, Any]:
    """
    Returns reports on selected alerts by specifying a time_frame value or a start_time and end_time
    of the search range.
    Will raise ValueError if inappropriate input given.

    :param client: client object which is used to get response from api
    :param args:The command arguments
    :return: Dictionary of file info or empty result message
    """
    # Validate arguments
    params = get_reports_params(args)

    # Preparing header
    headers = {
        "X-FeApi-Token": client.get_api_token(),
        "Accept": CONTENT_TYPE_JSON,
    }

    # API call
    resp: Response = client.http_request(
        method="GET",
        url_suffix=URL_SUFFIX["GET_REPORTS"],
        params=params,
        headers=headers,
    )

    # Create file from Content
    if int(resp.headers.get("Content-Length", "")) > 0:
        file_entry = fileResult(
            filename=generate_report_file_name(args),
            data=resp.content,
            file_type=EntryType.ENTRY_INFO_FILE,
        )
        return file_entry
    else:
        return MESSAGES["NO_RECORDS_FOUND"].format("report contents")


@logger
def get_alerts_command(
    client: Client,
    args: dict[str, Any],
    replace_alert_url: bool,
    instance_url: str,
) -> str | CommandResults:
    """
    Retrieve list of alerts based on various argument(s).

    :param client: Client object
    :param args: The command arguments provided by user.
    :param replace_alert_url: Replace the domain of the alert URL key to the Instance URL.
    :param instance_url: URL to connect to the FireEye NX.
    :return: Standard command result or no records found message
    """
    add_time_suffix_into_arguments(args)

    # Preparing header
    headers = {
        "X-FeApi-Token": client.get_api_token(),
        "Accept": CONTENT_TYPE_JSON,
    }

    # http call
    resp = client.http_request(
        method="GET",
        url_suffix=URL_SUFFIX["GET_ALERTS"],
        params=args,
        headers=headers,
    )

    total_records = resp.get("alertsCount", 0)
    if total_records <= 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("alert(s)")

    alerts_resp = resp.get("alert", [])

    # Replace the domain of the alertUrl key to Instance URL if it is true.
    if replace_alert_url:
        replace_alert_url_key_domain_to_instance_url(alerts_resp, instance_url)

    # Creating human-readable
    hr = prepare_hr_for_alert_response(resp)

    # Creating entry context
    custom_ec_for_alerts = remove_empty_entities(alerts_resp)

    # Remove dash, underscore from key and make it pascal case.
    custom_ec = remove_dash_and_underscore_from_key(custom_ec_for_alerts)

    return CommandResults(
        outputs_prefix="FireEyeNX.Alert",
        outputs_key_field="Uuid",
        outputs=custom_ec,
        readable_output=hr,
        raw_response=resp,
    )


@logger
def fetch_incidents(
    **kwargs,
) -> tuple[dict[str, Any] | None, list[dict[str, Any]] | None]:
    """
    This function retrieves new incidents every interval.

    :param kwargs : Dictionary contain all required arguments.
    :return: Tuple containing two elements. incidents list and timestamp.
    """
    # Retrieving last run time if not none, otherwise first_fetch will be considered.
    last_run = kwargs["last_run"]
    start_time = kwargs["first_fetch"]
    next_run = last_run

    incidents = []
    fetch_count = 0
    if "IPS Events" in (kwargs["fetch_type"] or []):
        if (events_start_time := last_run.get("events", {}).get("start_time")) and (
            parsed_start_time := dateparser.parse(events_start_time)
        ):
            start_time = parsed_start_time.timestamp()
        start_time = update_start_time(start_time)
        demisto.debug(f"FireeyeNX IPS Events Start Time: {start_time}")
        incidents, fetch_count, next_run_events = get_incidents_for_event(
            kwargs["client"],
            start_time,
            kwargs["fetch_limit"],
            kwargs["mvx_correlated"],
            last_run,
        )
        next_run["events"] = next_run_events

    # reset start time before next fetch type
    start_time = kwargs["first_fetch"]
    if "Alerts" in (kwargs["fetch_type"] or []) and (fetch_count < kwargs["fetch_limit"]):
        if (alerts_start_time := last_run.get("alerts", {}).get("start_time")) and (
            parsed_start_time := dateparser.parse(alerts_start_time)
        ):
            start_time = parsed_start_time.timestamp()
        start_time = update_start_time(start_time)
        demisto.debug(f"FireeyeNX Alerts Start Time: {start_time}")
        alert_incidents, next_run_alerts = get_incidents_for_alert(
            client=kwargs["client"],
            malware_type=kwargs["malware_type"],
            start_time=start_time,
            fetch_limit=kwargs["fetch_limit"],
            replace_alert_url=kwargs["replace_alert_url"],
            instance_url=kwargs["instance_url"],
            is_test=kwargs["is_test"],
            fetch_artifacts=kwargs["fetch_artifacts"],
            fetch_count=fetch_count,
            last_run=last_run,
        )
        incidents.extend(alert_incidents)
        next_run["alerts"] = next_run_alerts

    if kwargs["is_test"]:
        return None, None
    return next_run, incidents


@logger
def get_events_command(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieve list of events based on various argument(s).
    Will raise an exception if validation fails.

    :param client: Client object
    :param args: The command arguments provided by user.
    :return: Standard command result or no records found message
    """

    # Validate arguments
    params = get_events_params(args)

    # Preparing header
    headers = {
        "X-FeApi-Token": client.get_api_token(),
        "Accept": CONTENT_TYPE_JSON,
    }

    # http call
    resp = client.http_request(
        method="GET",
        url_suffix=URL_SUFFIX["GET_EVENTS"],
        params=params,
        headers=headers,
    )

    total_records = resp.get("events", [])
    if not total_records:
        return MESSAGES["NO_RECORDS_FOUND"].format("event(s)")

    # Creating entry context
    custom_ec_for_event = createContext(total_records, removeNull=True)
    custom_ec = remove_dash_and_underscore_from_key(custom_ec_for_event)

    # Creating human-readable
    hr = prepare_hr_for_events(total_records)

    return CommandResults(
        outputs_prefix="FireEyeNX.Event",
        outputs_key_field="EventId",
        outputs=custom_ec,
        readable_output=hr,
        raw_response=resp,
    )


def main() -> None:
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """
    # Commands dict
    commands = {
        "fireeye-nx-get-artifacts-metadata-by-alert": get_artifacts_metadata_by_alert_command,
        "fireeye-nx-get-reports": get_reports_command,
        "fireeye-nx-get-artifacts-by-alert": get_artifacts_by_alert_command,
        "fireeye-nx-get-events": get_events_command,
    }
    commands_with_params = {"fireeye-nx-get-alerts": get_alerts_command}

    command = demisto.command()
    demisto.info(f"Command being called is {command}")

    try:
        url = demisto.params().get("url")
        username = demisto.params().get("credentials", {}).get("identifier")
        password = demisto.params().get("credentials", {}).get("password")
        if password:
            password = password.encode("utf-8")

        verify_certificate = not demisto.params().get("insecure", False)
        proxy = demisto.params().get("proxy", False)
        request_timeout = demisto.params().get("request_timeout")
        request_timeout = get_request_timeout(request_timeout)

        base_url = f"{url}/wsapis/{API_VERSION}"

        # prepare client class object
        client = Client(
            base_url=base_url,
            verify=verify_certificate,
            proxy=proxy,
            auth=(username, password),
            request_timeout=request_timeout,
        )

        # Trim the arguments
        args = strip_blank(demisto.args())

        # This is the call made when pressing the integration Test button.
        if demisto.command() == "test-module":
            is_fetch = demisto.params().get("isFetch")
            first_fetch_time = demisto.params().get("first_fetch")

            # Set first fetch time as default if user leave empty
            first_fetch_time = first_fetch_time if first_fetch_time else DEFAULT_FIRST_FETCH

            malware_type = demisto.params().get("malware_type")

            fetch_limit = demisto.params().get("max_fetch")

            fetch_type = demisto.params().get("fetch_type")

            mvx_correlated = demisto.params().get("fetch_mvx_correlated_events", False)

            replace_alert_url = demisto.params().get("replace_alert_url", False)

            fetch_artifacts = demisto.params().get("fetch_artifacts", False)

            result = test_function(
                client=client,
                first_fetch_time=first_fetch_time,
                fetch_limit=fetch_limit,
                malware_type=malware_type,
                is_fetch=is_fetch,
                fetch_type=fetch_type,
                mvx_correlated=mvx_correlated,
                replace_alert_url=replace_alert_url,
                instance_url=url,
                fetch_artifacts=fetch_artifacts,
            )
            demisto.results(result)

        elif demisto.command() == "fetch-incidents":
            malware_type = demisto.params().get("malware_type", "")

            first_fetch_time = demisto.params().get("first_fetch")

            # Set first fetch time as default if user leave empty
            first_fetch_time = first_fetch_time if first_fetch_time else DEFAULT_FIRST_FETCH

            fetch_limit = demisto.params().get("max_fetch")

            fetch_limit = get_fetch_limit(fetch_limit)
            demisto.debug(f"Fetch Limit {fetch_limit}")

            fetch_type = demisto.params().get("fetch_type")

            mvx_correlated = demisto.params().get("fetch_mvx_correlated_events", False)

            # Getting numeric value from string representation
            start_time, _ = parse_date_range(first_fetch_time, date_format=DATE_FORMAT, utc=True)

            # Validate start_time should be less then 48 hour as per API limitation
            validate_date_range(first_fetch_time)

            validate_fetch_type(fetch_type)
            # Flag indicate to replace the 'alertUrl' domain to Integration URL or not.
            replace_alert_url = demisto.params().get("replace_alert_url", False)

            fetch_artifacts = demisto.params().get("fetch_artifacts", False)

            next_run, incidents = fetch_incidents(
                client=client,
                malware_type=malware_type,
                last_run=demisto.getLastRun(),
                fetch_limit=fetch_limit,
                first_fetch=date_to_timestamp(start_time, date_format=DATE_FORMAT) / 1000,
                fetch_type=fetch_type,
                mvx_correlated=mvx_correlated,
                replace_alert_url=replace_alert_url,
                instance_url=url,
                fetch_artifacts=fetch_artifacts,
                is_test=False,
            )
            # saves next_run for the time fetch-incidents is invoked.
            demisto.debug(f"FireEyeNX setting next run to: {next_run}")
            demisto.setLastRun(next_run)
            demisto.incidents(incidents)

        elif command in commands:
            return_results(commands[command](client, args))

        elif command in commands_with_params:
            # Flag indicate to replace alertUrl domain to Integration URL or not.
            replace_alert_url = demisto.params().get("replace_alert_url", False)

            return_results(commands_with_params[command](client, args, replace_alert_url, url))

    # Log exceptions
    except Exception as e:
        return_error(f"Error: {e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()