SymantecEDR

Symantec EDR (On Prem) endpoints help to detect threats in your network by filter endpoints data to find Indicators of Compromise (IoCs) and take actions to remediate the threat(s). EDR on-premise capabilities allow incident responders to quickly search, identify, and contain all impacted endpoints while investigating threats using a choice of on-premises.

Network Security · Symantec Endpoint Detection and Response

Details

IDSymantecEDR
ProviderBroadcom
CategoryNetwork Security
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Symantec EDR (On Prem) endpoints help to detect threats in your network by filter endpoints data to find Indicators of Compromise (IoCs) and take actions to remediate the threat(s). EDR on-premise capabilities allow incident responders to quickly search, identify, and contain all impacted endpoints while investigating threats using a choice of on-premises.
This integration was integrated and tested with version 4.6 of SymantecEDR

Configure Symantec Endpoint Detection and Response (EDR) - On Prem in Cortex

Parameter Description Required
Server URL (i.e., https://host:port) Symantec EDR (On Prem) Appliance URL True
Client ID OAuth Client ID and Client Secret to authorize third-party applications to communicate with Symantec EDR. True
Client Secret   True
Fetch incidents   False
Incident data source Fetch incident type, e.g., ‘incident’, ‘event’. If not selected, incident will be selected. False
Fetch incidents alerts Retrieve incident related events from EDR database. An additional API call will be made for each fetched incident. False
Fetch incident comments Retrieve incident comments for each fetched incident when checked. An additional API call will be made for each fetched incident. False
Incidents “Status” to filter out fetching as incidents. Comma-separated lists are supported, e.g., Open, In-Progress If not selected, will fetch Open incidents. False
Incidents “Priority” to filter out fetching as incidents. Comma-separated lists are supported, e.g., Medium,High. If not selected, will fetch High and Medium incidents. False
Events “Status” to filter out fetching as incidents. Comma-separated lists are supported, e.g., Unknown, Success If not selected, will fetch Success events. False
Events “Severity” to filter out fetching as incidents. Comma-separated lists are supported, e.g., Info, Warning If not selected, will fetch Info events. False
Query string to fetch incidents/events. For example - log_time:[2017-01-01T00:00:00.000Z TO 2017-01-08T00:00:00.000Z]”   False
First fetch timestamp (<number> <time unit>, e.g., 10 minutes, 12 hours, 7 days) First Fetch timestamp, Default is 3 days. The maximum time range is 30 days. For example, if configured as 60 days based on the current datetime, then data will be fetched according to the time range using start_time=60 days and end_time=30 days. False
Maximum number of incidents to fetch Maximum Number of Incidents fetch limit. Maximum Default limit is 50. False
Trust any certificate (not secure)   False
Use system proxy settings   False
Incident type   False
Source Reliability Reliability of the source providing the intelligence data. 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.

symantec-edr-endpoint-isolate


Isolates or quarantines endpoints by cutting connections that the endpoint(s) has to internal networks and external networks, based on the endpoint device IDs.

Base Command

symantec-edr-endpoint-isolate

Input

Argument Name Description Required
device_id The device ID of the target computers. Required

Context Output

Path Type Description
SymantecEDR.Command.isolate_endpoint.command_id String Command ID
SymantecEDR.Command.isolate_endpoint.error_code Number The status of the command action. Values: -1 = Error, 0 = Command isolate_endpoint successfully requested, 1 = Command isolate_endpoint not supported for target command type, 2 = Command isolate_endpoint failed because the target command is already in terminal state (i.e., completed, error, or cancelled), 3 = Command isolate_endpoint is already in progress for the target command.
SymantecEDR.Command.isolate_endpoint.message String Message explaining the error code.

Command example

!symantec-edr-endpoint-isolate device_id=393b8e82-fe40-429f-8e5e-c6b79a0f2b1c

Context Example

{
    "SymantecEDR": {
        "Command": {
            "Isolate Endpoint": {
                "command_id": "fd6d14933c7e422685634b613cb7963a-2023-02-15",
                "error_code": 0,
                "message": "Command isolate_endpoint successfully requested"
            }
        }
    }
}

Human Readable Output

Command Isolate Endpoint

Message CommandId
Command isolate_endpoint successfully requested fd6d14933c7e422685634b613cb7963a-2023-02-15

symantec-edr-domain-file-association-list


List of domain and file association.

Base Command

symantec-edr-domain-file-association-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
page_size The number of results per page to display. Optional
query Specify a search query as a Lucene query string.
Example: query=”last_seen: 2022-10-22T11:23:26.561Z”

Note: Refer to Symantec (EDR On-Premise) API document for more details https://apidocs.securitycloud.symantec.com/#.
Optional
search_object Specify a filter option in lieu of “query”. These filters will improve the query performance. Possible values are: domain, sha256. Optional
search_value Specify a search value. Supports a comma-separated query with multiple search values. Optional

Context Output

Path Type Description
SymantecEDR.DomainFileAssociation.data_source_url String The URL that was accessed. Example: “http://www.westfallave.com/insight/cloudcar.exe”.
SymantecEDR.DomainFileAssociation.data_source_url_domain String Domain name of the accessed URL. Example: “westfallave.com”.
SymantecEDR.DomainFileAssociation.device_ip String The IPv6 or IPv4 address of the endpoint when this association was last updated. Example: “127.0.0.1”.
SymantecEDR.DomainFileAssociation.device_name String The host name or, if unavailable, the IP address of the endpoint when this association was last updated. Example: “170915-000020”.
SymantecEDR.DomainFileAssociation.device_uid String Unique ID of the endpoint that downloaded the file from the URL. Example: “04cfc04b-5c7a-4aa8-b95b-79be23f768f4”.
SymantecEDR.DomainFileAssociation.first_seen String The timestamp (in ISO 8601 format) that specifies the creation time of the event that resulted in the creation of this association. Example: “2018-01-30T04:13:10.669Z”.
SymantecEDR.DomainFileAssociation.last_seen String The timestamp (in ISO 8601 format) that specifies the last time detected of the event that resulted in the update of this association. Example: “2018-01-30T04:13:10.669Z”.
SymantecEDR.DomainFileAssociation.name String The file name of the downloaded file. This attribute doesn’t include the path of the file. Example: “cloudcar[2].exe”.
SymantecEDR.DomainFileAssociation.sha2 String The SHA256 checksum of the file (hex string) that was downloaded from the URL. Example: “3559378c933cdd434af2083f7535460843d2462033de74ec7c70dbe5f70124f5”.
SymantecEDR.DomainFileAssociation.signature_company_name String The signer company name of the downloaded file. Example: “Microsoft Windows”.

Command example

!symantec-edr-domain-file-association-list limit=1

Context Example

{
    "SymantecEDR": {
        "DomainFileAssociation": [
            {
                "data_source_url": "http://msedge.b.tlu.dl.delivery.mp.microsoft.com/filestreamingservice/files/685cae66-5fe2-498e-b4f1-ed26aafa2801?p1=1667042599&p2=404&p3=2&p4=c3swnk6uktun4vjyhudntbuxv8bilxgbgat1s%2flgzbqw4kjyc0zs8ox7mi1oaisl96huewngvdr%2bnfbl7erlxa%3d%3d",
                "data_source_url_domain": "msedge.b.tlu.dl.delivery.mp.microsoft.com",
                "device_ip": "172.16.14.42",
                "device_name": "win-tfb8l7bi77h",
                "device_uid": "393b8e82-fe40-429f-8e5e-c6b79a0f2b1c",
                "first_seen": "2022-10-22T11:23:26.561Z",
                "last_seen": "2022-10-22T11:23:26.561Z",
                "name": "microsoftedge_x64_106.0.1370.52_106.0.1370.47.exe",
                "sha2": "1291d6eb30cd1683544666692a382aecf325dc2624da9ef395047a64642059dc",
                "signature_company_name": "Microsoft Corporation"
            }
        ]
    }
}

Human Readable Output

Domain File Association List

FirstSeen LastSeen DataSourceUrl DataSourceUrlDomain Sha2 Name SignatureCompanyName DeviceUid DeviceIp DeviceName
2022-10-22T11:23:26.561Z 2022-10-22T11:23:26.561Z http://msedge.b.tlu.dl.delivery.mp.microsoft.com/filestreamingservice/files/685cae66-5fe2-498e-b4f1-ed26aafa2801?p1=1667042599&p2=404&p3=2&p4=c3swnk6uktun4vjyhudntbuxv8bilxgbgat1s%2flgzbqw4kjyc0zs8ox7mi1oaisl96huewngvdr%2bnfbl7erlxa%3d%3d msedge.b.tlu.dl.delivery.mp.microsoft.com 1291d6eb30cd1683544666692a382aecf325dc2624da9ef395047a64642059dc microsoftedge_x64_106.0.1370.52_106.0.1370.47.exe Microsoft Corporation 393b8e82-fe40-429f-8e5e-c6b79a0f2b1c 172.16.14.42 win-tfb8l7bi77h

symantec-edr-endpoint-domain-association-list


List of endpoint and domain association.

Base Command

symantec-edr-endpoint-domain-association-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page_size The number of results per page to display. Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
query Specify a search query as a Lucene query string.
Example: query=”first_seen: [2022-10-01T07:00:58.030Z TO 2022-10-21T06:41:54.452Z]”

Note: For more details refer to Symantec EDR (On-Prem) API document https://apidocs.securitycloud.symantec.com/#.
Optional
search_object Specify a filters option in lieu of “query”. These filters will improve query performance. Possible values are: domain, device_uid. Optional
search_value Specify a search value. Supports a comma-separated query with multiple search values. Optional

Context Output

Path Type Description
SymantecEDR.EndpointDomainAssociation.data_source_url String The URL that was accessed. Example: “http://www.westfallave.com/insight/cloudcar.exe”.
SymantecEDR.EndpointDomainAssociation.data_source_url_domain String Domain name of the accessed URL. Example: “westfallave.com”.
SymantecEDR.EndpointDomainAssociation.device_ip String The IPv6 or IPv4 address of the endpoint when this association was last updated. Example: “127.0.0.1”.
SymantecEDR.EndpointDomainAssociation.device_name String The host name or, if unavailable, the IP address of the endpoint when this association was last updated. Example: “170915-000020”.
SymantecEDR.EndpointDomainAssociation.device_uid String Unique ID of the endpoint that accessed this URL. Example: “04cfc04b-5c7a-4aa8-b95b-79be23f768f4”.
SymantecEDR.EndpointDomainAssociation.first_seen String The timestamp (in ISO 8601 format) that specifies the creation time of the event that resulted in the creation of this association. Example: “2018-01-30T04:13:10.669Z.”
SymantecEDR.EndpointDomainAssociation.last_seen String The timestamp (in ISO 8601 format) that specifies the last time detected of the event that resulted in the update of this association. Example: “2018-01-30T04:13:10.669Z.”

Command example

!symantec-edr-endpoint-domain-association-list limit=1

Context Example

{
    "SymantecEDR": {
        "EndpointDomainAssociation": [
            {
                "data_source_url": "http://ctldl.windowsupdate.com/msdownload/update/v3/static/trustedr/en/disallowedcertstl.cab?4653cf2caa3508f4",
                "data_source_url_domain": "ctldl.windowsupdate.com",
                "device_ip": "172.16.14.42",
                "device_name": "WIN-TFB8L7BI77H",
                "device_uid": "393b8e82-fe40-429f-8e5e-c6b79a0f2b1c",
                "first_seen": "2022-10-21T19:06:39.998Z",
                "last_seen": "2022-10-21T19:06:39.998Z"
            }
        ]
    }
}

Human Readable Output

Endpoint Domain Association List

FirstSeen LastSeen DataSourceUrl DataSourceUrlDomain DeviceUid DeviceIp DeviceName
2022-10-21T19:06:39.998Z 2022-10-21T19:06:39.998Z http://ctldl.windowsupdate.com/msdownload/update/v3/static/trustedr/en/disallowedcertstl.cab?4653cf2caa3508f4 ctldl.windowsupdate.com 393b8e82-fe40-429f-8e5e-c6b79a0f2b1c 172.16.14.42 WIN-TFB8L7BI77H

symantec-edr-endpoint-file-association-list


List of domain and file association.

Base Command

symantec-edr-endpoint-file-association-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page_size The number of results per page to display. Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
query Specify a search query as a Lucene query string.
Example: query=”first_seen: [2022-10-01T07:00:58.030Z TO 2022-10-21T06:41:54.452Z]”

Note: For more details refer to Symantec EDR (On-Prem) API document https://apidocs.securitycloud.symantec.com/#.
Optional
search_object Specify a filters option in lieu of “query”. These filters will improve query performance. Possible values are: device_uid, sha256. Optional
search_value Specify a search value. Supports a comma-separated query with multiple search values. Optional

Context Output

Path Type Description
SymantecEDR.EndpointFileAssociation.device_ip String The IPv6 or IPv4 address of the endpoint when this association was last updated. Example: 127.0.0.1.
SymantecEDR.EndpointFileAssociation.device_name String The host name or, if unavailable, the IP address of the endpoint when this association was last updated. Example: 170915-000020.
SymantecEDR.EndpointFileAssociation.device_uid String Unique ID of the endpoint that has this file. Example: 04cfc04b-5c7a-4aa8-b95b-79be23f768f4.
SymantecEDR.EndpointFileAssociation.first_seen String The timestamp (in ISO 8601 format) that specifies the creation time of the event that resulted into the creation of this association. Example: 2018-02-04T09:00:00.577Z
SymantecEDR.EndpointFileAssociation.folder String The folder where the file resides. This attribute does not include the name of the file. Example:c:\windows\system32\.
SymantecEDR.EndpointFileAssociation.last_seen String The timestamp (in ISO 8601 format) that specifies the last time detected of the event that resulted in the update of this association. Example: YYYY-MM-DDTHH:MM:SS.sssZ
SymantecEDR.EndpointFileAssociation.name String The name of the file. This attribute does not include the path of the file. Example: sc.exe.
SymantecEDR.EndpointFileAssociation.sha2 String The SHA256 checksum of the file (hex string). Example: eaab690ebd8ddf9ae452de1bc03b73c8154264dbd7a292334733b47a668ebf31.

Command example

!symantec-edr-endpoint-file-association-list limit=1

Context Example

{
    "SymantecEDR": {
        "EndpointFileAssociation": [
            {
                "device_ip": "172.16.14.42",
                "device_name": "win-tfb8l7bi77h",
                "device_uid": "393b8e82-fe40-429f-8e5e-c6b79a0f2b1c",
                "first_seen": "2022-10-21T07:00:17.831Z",
                "folder": "csidl_profile\\appdata\\roaming\\microsoft\\windows\\recent\\automaticdestinations",
                "last_seen": "2022-12-09T10:03:21.866Z",
                "name": "3353b940c074fd0c.automaticdestinations-ms",
                "sha2": "1dc0c8d7304c177ad0e74d3d2f1002eb773f4b180685a7df6bbe75ccc24b0164"
            }
        ]
    }
}

Human Readable Output

Endpoint File Association List

FirstSeen LastSeen Sha2 Name Folder DeviceUid DeviceIp DeviceName
2022-10-21T07:00:17.831Z 2022-12-09T10:03:21.866Z 1dc0c8d7304c177ad0e74d3d2f1002eb773f4b180685a7df6bbe75ccc24b0164 3353b940c074fd0c.automaticdestinations-ms csidl_profile\appdata\roaming\microsoft\windows\recent\automaticdestinations 393b8e82-fe40-429f-8e5e-c6b79a0f2b1c 172.16.14.42 win-tfb8l7bi77h

symantec-edr-domain-instance-list


Get domain instances.

Base Command

symantec-edr-domain-instance-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
page_size The number of results per page to display. Optional
query Specify a search query as a Lucene query string.
Example: query=”external_ip: 8.8.8.8”.
Optional

Context Output

Path Type Description
SymantecEDR.DomainInstance.data_source_url String Last URL accessed on this domain. Example: http://www.<domain>.com/shample/shample.exe.
SymantecEDR.DomainInstance.data_source_url_domain String The name of the domain. Example: skyscan.com.
SymantecEDR.DomainInstance.disposition Number Domain disposition: 0 = healthy/good, 1 = unknown, 2 = suspicious, 3 = bad.
SymantecEDR.DomainInstance.first_seen String The timestamp (in ISO 8601 format) that specifies the creation time of the event that resulted into the creation of this instance. Example: YYYY-MM-DDTHH:MM:SS.sssZ
SymantecEDR.DomainInstance.last_seen String The timestamp (in ISO 8601 format) that specifies the last time detected of the event that resulted in the update of this instance. Example: YYYY-MM-DDTHH:MM:SS.sssZ
SymantecEDR.DomainInstance.external_ip String The IP address (IPv4 or IPv6) of the device/machine that accepted the connection. Example: 127.0.0.1.

Command example

!symantec-edr-domain-instance-list limit=1

Context Example

{
    "SymantecEDR": {
        "DomainInstances": [
            {
                "data_source_url": "",
                "data_source_url_domain": "ctldl.windowsupdate.com",
                "disposition": "Healthy",
                "external_ip": "",
                "first_seen": "2022-10-21T13:05:38.000Z",
                "last_seen": "2023-02-14T13:50:42.000Z"
            }
        ]
    }
}

Human Readable Output

Domain Instances List

DataSourceUrlDomain FirstSeen LastSeen Disposition
ctldl.windowsupdate.com 2022-10-21T13:05:38.000Z 2023-02-14T13:50:42.000Z Healthy

symantec-edr-endpoint-instance-list


Get endpoint instances.

Base Command

symantec-edr-endpoint-instance-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
page_size The number of results per page to display. Optional
query Specify a search query as a Lucene query string.
.
Optional

Context Output

Path Type Description
SymantecEDR.EndpointInstance.device_ip String The IP address of the endpoint. IPv4 or IPv6 format. Example: 192.168.0.250.
SymantecEDR.EndpointInstance.device_name String The host name or, if unavailable, the IP address of the endpoint. Example: WIN-CRNK1KQJBC0.
SymantecEDR.EndpointInstance.device_uid String Unique ID of the endpoint. Example: 12b1d2ce-dddb-4bcc-990e-28f44cf8ddcb.
SymantecEDR.EndpointInstance.domain_or_workgroup String Domain or workgroup name depending on the configuration. Example: WORKGROUP.
SymantecEDR.EndpointInstance.time String The timestamp (in ISO 8601 format) that specifies the creation or last update time of this instance. This is the creation time when there were no updates. Otherwise, it is the time of the last update. Example: YYYY-MM-DDTHH:MM:SS.sssZ
SymantecEDR.EndpointInstance.user_name String The name of the user who originated or caused the event. Example: Administrator.
SymantecEDR.EndpointInstance.ip_addresses Unknown Array of all the IP addresses (IPv4 or IPv6) associated with the endpoint. Example: [“192.168.0.250”].

Command example

!symantec-edr-endpoint-instance-list limit=1

Context Example

{
    "SymantecEDR": {
        "EndpointInstances": [
            {
                "device_ip": "172.16.14.58",
                "device_name": "172.16.14.58",
                "device_uid": "c0e1b083-9aba-48c0-9ba1-39c0c37c5851",
                "time": "2022-11-28T10:29:47.251Z"
            }
        ]
    }
}

Human Readable Output

Endpoint Instances List

DeviceUid DeviceName DeviceIp Time
c0e1b083-9aba-48c0-9ba1-39c0c37c5851 172.16.14.58 172.16.14.58 2022-11-28T10:29:47.251Z

symantec-edr-file-instance-list


Get File Instances

Base Command

symantec-edr-file-instance-list

Input

Argument Name Description Required
limit The maximum number of records to return.
Default is 50.
Note: If the page_size argument is set by the user then the limit argument will be ignored.
Optional
page The page number. Default is 1. Optional
page_size The number of requested results per page. Default is 50. Optional
file_sha2 Query unique file identifier (SHA2). Optional
query Specify a search query as a Lucene query string.
Example: query=”name: svchost.exe”.
Optional

Context Output

Path Type Description
SymantecEDR.FileInstance.first_seen String The timestamp (in ISO 8601 format) that specifies the creation time of the event that resulted into the creation of this instance. Example: YYYY-MM-DDTHH:MM:SS.sssZ.
SymantecEDR.FileInstance.folder String The folder where the file resides. This attribute does not include the name of the file. Example: c:\users\public\pictures\.
SymantecEDR.FileInstance.last_seen String The timestamp (in ISO 8601 format) that Specifies the last time detected of the event that resulted in the update of this instance. Example: YYYY-MM-DDTHH:MM:SS.sssZ
SymantecEDR.FileInstance.name String The name of the file. This attribute does not include the path of the file. Example: virus.exe.
SymantecEDR.FileInstance.sha2 String The SHA256 checksum of the file (hex string) Example: eaab690ebd8ddf9ae452de1bc03b73c8154264dbd7a292334733b47a668ebf31.

Command example

!symantec-edr-file-instance-list limit=1

Context Example

{
    "SymantecEDR": {
        "FileInstance": {
            "first_seen": "2022-10-21T07:00:17.831Z",
            "folder": "csidl_profile\\appdata\\roaming\\microsoft\\windows\\recent\\automaticdestinations",
            "last_seen": "2022-12-09T10:03:21.866Z",
            "name": "3353b940c074fd0c.automaticdestinations-ms",
            "sha2": "1dc0c8d7304c177ad0e74d3d2f1002eb773f4b180685a7df6bbe75ccc24b0164"
        }
    }
}

Human Readable Output

File Instances List

FirstSeen LastSeen Sha2 Name Folder
2022-10-21T07:00:17.831Z 2022-12-09T10:03:21.866Z 1dc0c8d7304c177ad0e74d3d2f1002eb773f4b180685a7df6bbe75ccc24b0164 3353b940c074fd0c.automaticdestinations-ms csidl_profile\appdata\roaming\microsoft\windows\recent\automaticdestinations

Command example

!symantec-edr-file-instance-list file_sha2=302c968ab3e1227d54df4e72f39088d7483d25eeb3037f0b16bc39cef2728fa4

Context Example

{
    "SymantecEDR": {
        "FileInstance": {
            "first_seen": "2022-10-21T19:31:20.770Z",
            "folder": "c:\\program files\\google\\chrome\\application\\106.0.5249.119",
            "last_seen": "2023-02-15T11:26:41.109Z",
            "name": "elevation_service.exe",
            "sha2": "302c968ab3e1227d54df4e72f39088d7483d25eeb3037f0b16bc39cef2728fa4"
        }
    }
}

Human Readable Output

File Instances List

FirstSeen LastSeen Sha2 Name Folder
2022-10-21T07:00:39.964Z 2023-02-15T11:26:41.104Z 302c968ab3e1227d54df4e72f39088d7483d25eeb3037f0b16bc39cef2728fa4 elevation_service.exe csidl_program_files\google\chrome\application\106.0.5249.119
2022-10-21T19:31:20.770Z 2023-02-15T11:26:41.109Z 302c968ab3e1227d54df4e72f39088d7483d25eeb3037f0b16bc39cef2728fa4 elevation_service.exe c:\program files\google\chrome\application\106.0.5249.119

symantec-edr-system-activity-list


Get system activities or logs.

Base Command

symantec-edr-system-activity-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page The page number view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
page_size The number of results per page to display. Optional
start_time The earliest time from which to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’). Optional
end_time From current time to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’, now). Optional
query A search query as a Lucene query string.
Example: query=”type_id:(4096 OR 4098 OR 4123)”.
Optional
type_id Request for specific system activities from the following events: \n0 = Application Activity \n1000 = System Health \nRefer to this <a href="https://origin-techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/4-7/search-fields-and-descriptions-v126755396-d38e59231/event-summary-type-ids-v121987556-d38e58861.html">the following</a> to check the type_id for the event type. Optional
severity The severity. Possible values are: info, warning, minor, major, critical, fatal. Optional
status The overall success or failure of the action reported by the event. Possible values are: \nUnknown (0)\nSuccess (1)\nFailure (2). Possible values are: Unknown, Success, Failure. Optional

Context Output

Path Type Description
SymantecEDR.SystemActivity.device_time String The timestamp (in ISO 8601 format) that specifies the time at which the event occurred.
SymantecEDR.SystemActivity.type_id Number The unique identifier for an event. Following is this events link and summary type IDs: https://origin-techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/4-7/search-fields-and-descriptions-v126755396-d38e59231/event-summary-type-ids-v121987556-d38e58861.htmlSystem Activity Log Event Type: Viewing Symantec EDR appliance activities in the System Activity log (broadcom.com)
SymantecEDR.SystemActivity.severity_id Number Event severity that specifies the importance of the event. Possible values are: 1 = info (default), 2 = warning, 3 = minor, 4 = major, 5 = critical, 6 = fatal.
SymantecEDR.SystemActivity.message String Human-readable (possibly multi-line) event message or description of the event.
SymantecEDR.SystemActivity.device_ip String The IPv6 or IPv4 address of the device that originated the event.
SymantecEDR.SystemActivity.atp_node_role Number The role of the ATP appliance that generated the event. Possible values are: 0 = Pre-Bootstrap, 1 = Network Scanner, 2 = Management, 3 = Standalone Network, 4 = Standalone Endpoint, 5 = All in One.
SymantecEDR.SystemActivity.category_id String The event type category. 4 = Audit.
SymantecEDR.SystemActivity.device_cap String Name or caption of the ATP appliance that generated the event.
SymantecEDR.SystemActivity.device_name String The device name (i.e., the name of the endpoint or appliance associated with an event).
SymantecEDR.SystemActivity.feature_name String The name of the feature that originated the event. Applicable events: 1, 20, 21, 1000 Example: Search.
SymantecEDR.SystemActivity.id String The event identifier for applicable events: 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8089, 8090 1 = Exists 2 = Partial. The outcome of the Session Audit event for applicable events: 20 0 = Unknown, 1 = Logon, 2 = Logoff. The outcome of the Entity Audit event for applicable events: 21 0 = Unknown, 1 = Create, 2 = Update, 3 = Delete
SymantecEDR.SystemActivity.log_name String The index of the event. Note: This is for informational purpose and cannot be used as a filter. Use time as start_time to query for events. Example: epmp_events-2015-11-05.
SymantecEDR.SystemActivity.log_time String The time the event was logged. Example: YYYY-MM-DDThh:mm:ss.SSSZ.
SymantecEDR.SystemActivity.remediation String Description of how to fix the issue, if applicable. Applicable events: 1000. Example: Enter valid connection settings for SEPM server [SEPM_DB→<IP>:<PORT>] for Symantec Endpoint Protection Correlation to work properly.
SymantecEDR.SystemActivity.status_detail String The type of failure that may have occurred. The list includes, but is not limited to, the following: service_failure service_unavailable network_error certificate_error sw_update_error internal_error authentication_error connection_error.
SymantecEDR.SystemActivity.status_exception String Low level exception message if available. Applicable events: 1000.
SymantecEDR.SystemActivity.status_id Number The overall success or failure of the action reported by the event. Possible values are: 0 = Unknown 1 = Success 2 = Failure Applicable events: 1, 20, 21, 1000 Example: 1.
SymantecEDR.SystemActivity.uuid Unknown The unique ID for this event. UUID uniquely identifies an event with a single event type (type_id).
SymantecEDR.SystemActivity.process_pid String PID of the service for which an action was taken. Applicable events: 1000 Example: 31337.
SymantecEDR.SystemActivity.data_sepm_server_db_ip_address String IP address of the SEPM database.
SymantecEDR.SystemActivity.data_sepm_server_enabled Boolean Indicates whether ATP is enabled to log on and gather logs from this database. Applicable events: 1000. Default: false. Example: true.
SymantecEDR.SystemActivity.data_sepm_server_db_type String Type of database: MSSQL or Sybase. Applicable events: 1000. Example: SYBASE.
SymantecEDR.SystemActivity.data_sepm_server_user_name String User name of the SEPM database. Applicable events: 1000 Example: ATP_QUERY_USER.
SymantecEDR.SystemActivity.data_sepm_server_status String Status of SEPM database configuration with ATP. Applicable events: 1000 Example: healthy.
SymantecEDR.SystemActivity.data_sepm_server_sepm_name String User-provided name for SEPM database server. Applicable events: 1000 Example: SEPM_DB.
SymantecEDR.SystemActivity.data_sepm_server_db_port Number Database port of SEPM database. Applicable events: 1000 Example: 8081.
SymantecEDR.SystemActivity.data_sepm_server_db_name String SEPM database name.

Command example

!symantec-edr-system-activity-list limit=1

Context Example

{
    "SymantecEDR": {
        "SystemActivity": {
            "atp_node_role": "All in One",
            "device_cap": "EDR",
            "device_ip": "192.168.20.8",
            "device_name": "localhost.localdomain",
            "device_time": "2023-02-15T11:33:54.112Z",
            "feature_name": "AdministratorTask",
            "log_name": "atp_system_log-2023-02-15",
            "log_time": "2023-02-15T11:33:54.153Z",
            "message": "Command submit_to_sandbox with command id 98a42ac7c11c4610b4b977a0371bf0c9-2023-02-15 completed.",
            "pid": 12719,
            "product_name": "Symantec Endpoint Detection and Response",
            "product_ver": "4.6.8-8",
            "severity_id": "Info",
            "status_id": "Success",
            "timezone": 0,
            "type_id": 1,
            "uuid": "a079d400-ad24-11ed-fac3-00000001b4ee"
        }
    }
}

Human Readable Output

System Activities List

Time TypeId SeverityId Message DeviceIp AtpNodeRole StatusId
2023-02-15T11:33:54.112Z 1 Info Command submit_to_sandbox with command id 98a42ac7c11c4610b4b977a0371bf0c9-2023-02-15 completed. 192.168.20.8 All in One Success

symantec-edr-audit-event-list


Get Audit Events

Base Command

symantec-edr-audit-event-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
start_time The beginning of the search time frame. Supports ISO (e.g., “yyyy-MM-dd’T’HH:mm:ss.SSSZ”) or ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’). Optional
end_time The end of the search time frame. Supports ISO (e.g., “yyyy-MM-dd’T’HH:mm:ss.SSSZ”) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’, now). Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
page_size The number of results per page to display. Optional
query A search query as a Lucene query string.
Example: query=”type_id:(4096 OR 4098 OR 4123)”

The search query is broken up into terms and operators.

There are two types of terms: Single Terms and Phrases.
(a) A Single Term is a single word such as “test” or “hello”
(b) A Phrase is a group of words surrounded by double quotes such as “hello dolly”

When creating a search query string, consider the following:

1. You can search any field by specifying the field name followed by a colon “:” and then the term you are looking for
2. Escape special characters that are part of the query syntax. To escape a special character use the \ before the character. The current list of special characters are ‘+, -, &&, ||, !, ( ), { }, [ ], ^, “, ~ ,*, ?, \, :’
3. Date value should follow ISO 8601 date stamp standard format (yyyy-MM-dd’T’HH:mm:ss.SSSXXX)
4. Supported Boolean operators for complex query are: AND OR + - NOT Note: Boolean operators must be ALL CAPS
5. Multiple terms can be combined together with Boolean operators to form a more complex query in the query clause
6. Use parentheses to group clauses to form sub-queries
7. Defaults to all events for the start_time and end_time specified in the query
8. The maximum length of the query string is 10240 characters.
Optional
type_id The type ID. Refer to the event summary type IDs link: https://origin-techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/4-7/search-fields-and-descriptions-v126755396-d38e59231/event-summary-type-ids-v121987556-d38e58861.html. Optional

Context Output

Path Type Description
SymantecEDR.AuditEvent.user_agent_ip String IP address of the endpoint that originated or caused the event.
SymantecEDR.AuditEvent.entity_result.data.incident_management.uuid String The GUID assigned for this incident
SymantecEDR.AuditEvent.entity_result.type String The type of the managed entity.Depending on this entity type, data would contain the corresponding entity content. Refer to this link https://techdocs.broadcom.com/content/dam/broadcom/techdocs/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/generated-pdfs/EDR_API_Legacy.pdf section 3.74 Entity for all the Possible values for managed entity types
SymantecEDR.AuditEvent.entity_result.uid String Unique identifier associated with the managed entity.
SymantecEDR.AuditEvent.entity_result.name String The name of the managed entity.
SymantecEDR.AuditEvent.status_id Number The overall success or failure of the action reported by the event. Values are: 0 = Unknown 1 = Success 2 = Failure.
SymantecEDR.AuditEvent.feature_name String The name of the feature that originated the event. Example: Search.
SymantecEDR.AuditEvent.device_ip String The IPv6 or IPv4 address of the device that originated the event.
SymantecEDR.AuditEvent.user_name String The username or ID that originated or caused the event.
SymantecEDR.AuditEvent.atp_node_role Number The role of the ATP appliance that generated the event. Possible values are: 0 = Pre-Bootstrap, 1 = Network Scanner, 2 = Management, 3 = Standalone Network, 4 = Standalone Endpoint 5 = All in One Applicable events: 1, 20, 21, 1000.
SymantecEDR.AuditEvent.category_id Number The event type category. 4 = Audit.
SymantecEDR.AuditEvent.device_uid String Unique ID of the device that originated the event. Example: 7c056576-860b-4eb9-b49c-3c349edb733f.
SymantecEDR.AuditEvent.log_name String The index of the event. Note: This is for informational purpose and cannot be used as a filter. Use time as start_time to query for events. Example: epmp_events-2015-11-05.
SymantecEDR.AuditEvent.count Number The count of system changes in the event.
SymantecEDR.AuditEvent.device_name String The device name (i.e., the name of the endpoint or appliance associated with an event).
SymantecEDR.AuditEvent.message String Human-readable (multi-line) event message or description of the event.
SymantecEDR.AuditEvent.log_time Date The time the event was logged. Example: YYYY-MM-DDTHH:MM:SS.sssZ.
SymantecEDR.AuditEvent.severity_id Number Event severity that specifies the importance of the event. Values are: 1 = info (default), 2 = warning, 3 = minor, 4 = major, 5 = critical, 6 = fatal.
SymantecEDR.AuditEvent.device_cap String Name or caption of the ATP appliance that generated the event. Example: EDR.
SymantecEDR.AuditEvent.id Number The event identifier. 1 = Exists, 2 = Partial.
SymantecEDR.AuditEvent.device_time Date The timestamp (in ISO 8601 format) that specifies the time at which the event occurred. Example: YYYY-MM-DDTHH:MM:SS.sssZ.
SymantecEDR.AuditEvent.product_ver String The version of the product that originated the event. Example: 4.6.8-8.
SymantecEDR.AuditEvent.device_end_time Date The end time of an event (in format yyyy-MM-dd’T’HH:mm:ss.SSSZ). This is used with the aggregation count field.
SymantecEDR.AuditEvent.type_id Number The unique identifier for an event. The following events are supported: For type_id details refer to “https://origin-techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/4-7/search-fields-and-descriptions-v126755396-d38e59231/event-summary-type-ids-v121987556-d38e58861.html”.
SymantecEDR.AuditEvent.uuid String The unique ID for this event. UUID uniquely identifies an event with a single event type (type_id).
SymantecEDR.AuditEvent.product_name String The name of the product that originated the event. Example: “Symantec Endpoint Detection and Response”.
SymantecEDR.AuditEvent.status_detail String The type of success or failure for the audit events.
SymantecEDR.AuditEvent.timezone Number The timezone offset in minutes. For UTC this will always be 0. Example: 0.
SymantecEDR.AuditEvent.user_uid String Unique ID of the user who originated the event or the user on whose behalf the event occurred.

Command example

!symantec-edr-audit-event-list limit=1

Context Example

{
    "SymantecEDR": {
        "AuditEvent": {
            "atp_node_role": 5,
            "category_id": 4,
            "count": 1,
            "device_cap": "EDR",
            "device_end_time": "2023-02-15T11:34:25.912Z",
            "device_ip": "192.168.20.8",
            "device_name": "localhost.localdomain",
            "device_time": "2023-02-15T11:34:25.912Z",
            "device_uid": "2B034D56-DBDB-9D58-DBA5-1CCB980276F2",
            "feature_name": "Incident",
            "id": 2,
            "log_name": "atp_audit_log-2023-02",
            "log_time": "2023-02-15T11:34:26.159Z",
            "message": "Incident Closed Successfully.",
            "product_name": "Symantec Endpoint Detection and Response",
            "product_ver": "4.6.8-8",
            "severity_id": "Info",
            "status_detail": "Success",
            "status_id": "Success",
            "timezone": 0,
            "type_id": 21,
            "user_agent_ip": "127.0.0.1",
            "user_name": "SEDR API",
            "user_uid": "O2ID.atp-customer.atp-domain.dbq9fmcjk132kmha7f9584qvr7",
            "uuid": "b36e1f80-ad24-11ed-e0f5-00000001b4f0"
        }
    }
}

Human Readable Output

Audit Event List

Time TypeId FeatureName Message UserAgentIp UserName Severity DeviceName DeviceIp Uuid StatusId
2023-02-15T11:34:25.912Z 21 Incident Incident Closed Successfully. 127.0.0.1 SEDR API Info localhost.localdomain 192.168.20.8 b36e1f80-ad24-11ed-e0f5-00000001b4f0 Success

symantec-edr-event-list


Get events or system alerts from EDR on-premise.

Base Command

symantec-edr-event-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
Optional
page_size The number of results per page to display. Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
start_time The earliest time from which to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’). Optional
end_time From current time to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’, now). Optional
query A search query as a Lucene query string.
Example: query=”type_id:(4096 OR 4098 OR 4123)”.
Optional
type_id The type ID. Refer to event summary type IDs link: https://origin-techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/4-7/search-fields-and-descriptions-v126755396-d38e59231/event-summary-type-ids-v121987556-d38e58861.html. Optional
severity The severity. Possible values are: info, warning, minor, major, critical, fatal. Optional

Context Output

Path Type Description
SymantecEDR.Event.type_id Number The unique identifier for an event. Refer to “https://techdocs.broadcom.com/content/dam/broadcom/techdocs/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/generated-pdfs/EDR_API_Legacy.pdf”.
SymantecEDR.Event.severity_id Number Event severity that specifies the importance of the event. Values are: 1 = info (default), 2 = warning, 3 = minor, 4 = major, 5 = critical, 6 = fatal.
SymantecEDR.Event.device_time Date The timestamp (in ISO 8601 format) that specifies the time at which the event occurred.
SymantecEDR.Event.log_time Date The time the event was logged.
SymantecEDR.Event.device_uid String Unique ID of the device that originated the event.
SymantecEDR.Event.device_name String The domain name of the client computer.
SymantecEDR.Event.device_ip String The IPv6 or IPv4 address of the device that originated the event.
SymantecEDR.Event.device_os_name String The operating system running on the device_type that originated the event. The values include, but are not limited to: Windows, Mac OSX, IOS, Android.
SymantecEDR.Event.user_name String The username or ID that originated or caused the event.
SymantecEDR.Event.user_domain String Event user associated with the domain.
SymantecEDR.Event.user_sid String Unique ID of the user who originated the event or the user on whose behalf the event occurred.
SymantecEDR.Event.device_domain String The domain where device resides. Example: “internal.somecompany.com”.
SymantecEDR.Event.operation Number The OS operation that initiated the event. Refer to “https://techdocs.broadcom.com/content/dam/broadcom/techdocs/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/generated-pdfs/EDR_API_Legacy.pdf”.
SymantecEDR.Event.event_actor.pid Number The process identifier as reported by the operating system.
SymantecEDR.Event.event_actor.uid String The unique identifier of the process. Example: “2ef07353-c1d2-409d-addd-2eed37a87e56”. Applicable events: 8007.
SymantecEDR.Event.event_actor.cmd_line String The command line that was used to launch the process. Applicable events: 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009, 8081.
SymantecEDR.Event.event_actor.start_time Date Start time for the originating event.
SymantecEDR.Event.event_actor.signature_level_id Number A numeric representation of the signature level. Possible values are: 0 = UNKNOWN, 10 = UNSIGNED, 20 = SIGNED_BUT_UNTRUSTED, 30 = SIGNED, 40 = CLASS3_SIGNED, 50 = SYMC_SIGNED, 60 = MICROSOFT_SIGNED, 70 = MICROSOFT_OS_COMPONENT Applicable events: 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009.
SymantecEDR.Event.event_actor.integrity_id Number The process integrity level (Windows only). Possible values are: 0 = Unknown, 1 = Untrusted, 2 = Low, 3 = Medium, 4 = Medium Plus, 5 = High, 6 = System, 7 = Protected Applicable events: 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009.
SymantecEDR.Event.event_actor.user.name String The username or ID that originated or caused the event.
SymantecEDR.Event.event_actor.user.sid String Event actor user security identifier.
SymantecEDR.Event.event_actor.file.name String The name of the file.
SymantecEDR.Event.event_actor.file.md5 String The MD5 checksum of the file.
SymantecEDR.Event.event_actor.file.modified Date Threat file modified date in ISO 8601 format.
SymantecEDR.Event.event_actor.file.path String The full path to the object.
SymantecEDR.Event.event_actor.file.normalized_path String The CSIDL normalized path name (for Windows only).
SymantecEDR.Event.event_actor.file.signature_company_name String The name of the company on the certificate.
SymantecEDR.Event.event_actor.file.signature_value_ids Number An integer array that contains one or more of the following signature values as derived from the Signature Bits. Possible values are: 0 = Unsigned, 1 = Signed, 2 = Code Signed, 3 = Class 3 Signed, 4 = Symantec Signed, 5 = Microsoft Signed, 6 = OS Component, 7 = Windows Hardware Wuality Labs (WHQL), 8 = Signer Explicitly Trusted, 9 = Signature Has Extra Date, 10 = Signature Uses MD5, 11 = Signature Uses SHA-1,12 = Signature Chain Not Valid, 13 = Signature From Catalog, 14 = Hash Does Not Match, 15 = Local Trusted Certificate, 16 = Trustworthy, 17 = Well Known Trusted Root Certificate, 18 = Heuristically Trustworthy, 19 = Symantec Internal, 20 = Signature Uses SHA-256, 21 = Signature Uses SHA-384, 22 = Signature Uses SHA-512, 23 = Signer Explicitly Revoked.
SymantecEDR.Event.event_actor.file.sha2 String The SHA256 checksum of the file (hex string).
SymantecEDR.Event.event_actor.file.original_name String The original name of the file.
SymantecEDR.Event.process.pid Number The process identifier as reported by the operating system.
SymantecEDR.Event.process.uid String The unique identifier of the process.
SymantecEDR.Event.process.cmd_line String The command line that was used to launch the process.
SymantecEDR.Event.process.signature_level_id Number A numeric representation of the signature level. Possible values are: 0 = Unknown, 10 = Unsigned, 20 = Signed But Untrusted, 30 = Signed, 40 = Class 3 Signed, 50 = SYMC Signed, 60 = Microsoft Signed, 70 = Microsoft OS Component.
SymantecEDR.Event.process.integrity_id Number The process integrity level (Windows only). Possible values are: 0 = Unknown, 1 = Untrusted, 2 = Low, 3 = Medium, 4 = Medium Plus, 5 = High, 6 = System, 7 = Protected.
SymantecEDR.Event.process.user.name String The username or ID that originated or caused the event.
SymantecEDR.Event.process.user.sid String Event actor user security identifier.
SymantecEDR.Event.process.file.normalized_path String The CSIDL normalized path name (for Windows only).
SymantecEDR.Event.process.file.name String The name of the file.
SymantecEDR.Event.process.file.md5 String The MD5 checksum of the file.
SymantecEDR.Event.process.file.modified Date Threat file modified date in ISO 8601 format.
SymantecEDR.Event.process.file.path String The full path to the object.
SymantecEDR.Event.process.file.signature_company_name String The name of the company on the certificate.
SymantecEDR.Event.process.file.signature_value_ids Number An integer array that contains one or more of the following signature values as derived from the Signature Bits. Possible values are: 0 = Unsigned, 1 = Signed, 2 = Code Signed, 3 = Class 3 Signed, 4 = Symantec Signed, 5 = Microsoft Signed, 6 = OS Component, 7 = Windows Hardware Wuality Labs (WHQL), 8 = Signer Explicitly Trusted, 9 = Signature Has Extra Date, 10 = Signature Uses MD5, 11 = Signature Uses SHA-1,12 = Signature Chain Not Valid, 13 = Signature From Catalog, 14 = Hash Does Not Match, 15 = Local Trusted Certificate, 16 = Trustworthy, 17 = Well Known Trusted Root Certificate, 18 = Heuristically Trustworthy, 19 = Symantec Internal, 20 = Signature Uses SHA-256, 21 = Signature Uses SHA-384, 22 = Signature Uses SHA-512, 23 = Signer Explicitly Revoked.
SymantecEDR.Event.process.file.sha2 String The SHA256 checksum of the file (hex string).
SymantecEDR.Event.process.file.original_name String The original name of the file.
SymantecEDR.Event.enriched_data.rule_name String The name of the IntelliFilter rule that observes all of the endpoint data recorded events on the client.
SymantecEDR.Event.enriched_data.category_name String The IntelliFilter rules fall into the following categories: System File Launched Or Loaded From Unexpected Location = 8001, 8002. Suspicious PowerShell Script Executed = 8001 Suspicious N-gram = 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009. Process Termination = 8001, Process Launch = 8001, Load Point Modification = 8005, 8006, File with Double Exe Extension (.jpg.exe) = 8003, Attempt to Change to Windows Event Logs or Registry Settings = 8005, 8006, Suspicious Protocol-Port Usage By System Processes = 8007, All events = 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009, Applicable events: 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009
SymantecEDR.Event.enriched_data.category_id Number The possible values of supported category_id. 0 = All Events, 1 = Suspicious N-Gram, 2 = Process Launch , 3 = Process Termination , 100 = Suspicious Protocol-Port Usage By System Processes, 102 = Suspicious PowerShell commands.
SymantecEDR.Event.ref_uid String The event reference UID.
SymantecEDR.Event.uuid String The unique ID for this event. UUID uniquely identifies an event with a single event type (type_id).
SymantecEDR.Event.log_name String The index of the event. Note: This is for informational purpose and cannot be used as a filter. Use time as start_time to query for events.

Command example

!symantec-edr-event-list limit=1

Context Example

{
    "SymantecEDR": {
        "Event": {
            "cmd_line": "C:\\Windows\\system32\\DllHost.exe /Processid:{E2B3C97F-6AE1-41AC-817A-F6F92166D7DD}",
            "device_domain": "WORKGROUP",
            "device_ip": "172.16.14.42",
            "device_name": "WIN-TFB8L7BI77H",
            "device_os_name": "Windows Server 2019 ",
            "device_time": "2023-02-15T11:29:31.248Z",
            "device_uid": "393b8e82-fe40-429f-8e5e-c6b79a0f2b1c",
            "enriched_data_category_id": 3,
            "enriched_data_category_name": "Process Termination",
            "enriched_data_rule_name": "eProcessClose",
            "event_actor_cmd_line": "C:\\Windows\\system32\\DllHost.exe /Processid:{E2B3C97F-6AE1-41AC-817A-F6F92166D7DD}",
            "event_actor_integrity_id": 6,
            "event_actor_pid": 17068,
            "event_actor_signature_level_id": 60,
            "event_actor_start_time": "2023-02-15T11:29:26.217Z",
            "event_actor_uid": "EFDEC4CC-ACFF-F1ED-821C-98261F32744E",
            "file_file_md5": "d2ab39ea2c0fcd172751f84bda723a97",
            "file_file_modified": "2018-09-15T07:12:24.564Z",
            "file_file_name": "dllhost.exe",
            "file_file_normalized_path": "CSIDL_SYSTEM\\dllhost.exe",
            "file_file_original_name": "dllhost.exe",
            "file_file_path": "c:\\windows\\system32\\dllhost.exe",
            "file_file_sha2": "c4e078607db2784be7761c86048dffa6f3ef04b551354a32fcdec3b6a3450905",
            "file_file_signature_company_name": "Microsoft Windows",
            "integrity_id": 6,
            "log_name": "epmp_events-fdr-2023-02-15",
            "log_time": "2023-02-15T11:33:30.148Z",
            "operation": 2,
            "pid": 17068,
            "ref_uid": "2C7DF1FF-FB83-4A63-8C85-17327F4C9F26",
            "severity_id": "Info",
            "signature_level_id": 60,
            "type_id": 8001,
            "uid": "EFDEC4CC-ACFF-F1ED-821C-98261F32744E",
            "user_domain": "NT AUTHORITY",
            "user_name": "SYSTEM",
            "user_sid": "S-1-5-18",
            "user_user_name": "SYSTEM",
            "user_user_sid": "S-1-5-18",
            "uuid": "03cbf700-ad24-11ed-c212-00000001b4eb"
        }
    }
}

Human Readable Output

Event List

Time TypeId Description DeviceName SeverityId DeviceIp Operation DeviceDomain UserName
2023-02-15T11:29:31.248Z 8001 logged: WIN-TFB8L7BI77H Info 172.16.14.42 2 WORKGROUP SYSTEM

symantec-edr-incident-event-list


Get events for incidents.

Base Command

symantec-edr-incident-event-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
.
Optional
page_size The number of results per page to display. Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
start_time The earliest time from which to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’).

.
Optional
end_time From current time to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’, now).
.
Optional
query A search query as a Lucene query string.

Example:
query=”type_id:(4096 OR 4098 OR 4123)”.
Optional
type_id The unique identifier for an event. Refer to this link for Event Type IDs :
https://origin-techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-detection-and-response/4-7/search-fields-and-descriptions-v126755396-d38e59231/event-summary-type-ids-v121987556-d38e58861.html.
Optional
severity The severity. Default: All severity types. Possible values are: info, warning, minor, major, critical, fatal. Optional

Context Output

Path Type Description
SymantecEDR.IncidentEvent.type_id Number The unique identifier for an event type.
SymantecEDR.IncidentEvent.severity_id Number Event severity that specifies the importance of the event. Possible values are: 1 = info (default) 2 = warning 3 = minor 4 = major 5 = critical 6 = fatal
SymantecEDR.IncidentEvent.device_time Date The timestamp (in ISO 8601 format) that specifies the time at which the event occurred.
SymantecEDR.IncidentEvent.log_time Date The time the event was logged.
SymantecEDR.IncidentEvent.device_uid String Unique ID of the endpoint that has this file.
SymantecEDR.IncidentEvent.device_name String The host name or, if unavailable, the IP address of the endpoint when this association was last updated.
SymantecEDR.IncidentEvent.device_ip String The IPv6 or IPv4 address of the endpoint when this association was last updated.
SymantecEDR.IncidentEvent.device_os_name String The operating system running on the device_type that originated the event. The possible values include, but are not limited to, the following: Windows, Mac OSX, IOS, Android.
SymantecEDR.IncidentEvent.user_name String The username or ID that originated or caused the event.
SymantecEDR.IncidentEvent.user_domain String Event user associated with the domain
SymantecEDR.IncidentEvent.user_sid String Unique ID of the user that originated the event or the user on whose behalf the event occurred.
SymantecEDR.IncidentEvent.incident String The unique ID of the incident that is related to this event. Applicable events: All events associated with an incident.
SymantecEDR.IncidentEvent.device_domain String The domain where device resides.
SymantecEDR.IncidentEvent.operation Number The OS operation that initiated the event.
SymantecEDR.IncidentEvent.event_actor.pid Number The process identifier as reported by the operating system.
SymantecEDR.IncidentEvent.event_actor.uid String The unique identifier of the process.
SymantecEDR.IncidentEvent.event_actor.cmd_line String The command line that was used to launch the process.
SymantecEDR.IncidentEvent.event_actor.start_time Date Start_time for the originating event.
SymantecEDR.IncidentEvent.event_actor.signature_level_id Number A numeric representation of the signature level. Possible values are: 0 = Unknown, 10 = Unsigned, 20 = Signed But Untrusted, 30 = Signed, 40 = Class 3 Signed, 50 = SYMC Signed, 60 = Microsoft Signed, 70 = Microsoft OS Component
SymantecEDR.IncidentEvent.event_actor.integrity_id Number The process integrity level (Windows only). Possible values are: 0 = Unknown, 1 = Untrusted, 2 = Low, 3 = Medium, 4 = Medium Plus, 5 = High, 6 = System, 7 = Protected
SymantecEDR.IncidentEvent.event_actor.user.name String The username or ID that originated or caused the event.
SymantecEDR.IncidentEvent.event_actor.user.sid String Event Actor user security identifier.
SymantecEDR.IncidentEvent.event_actor.file.name String The name of the file.
SymantecEDR.IncidentEvent.event_actor.file.md5 String The MD5 checksum of the file.
SymantecEDR.IncidentEvent.event_actor.file.modified Date Threat file modified date in ISO 8601 format.
SymantecEDR.IncidentEvent.event_actor.file.path String The full path to the object.
SymantecEDR.IncidentEvent.event_actor.file.normalized_path String The CSIDL normalized path name;Windows Only.
SymantecEDR.IncidentEvent.event_actor.file.signature_company_name String The name of the company on the certificate.
SymantecEDR.IncidentEvent.event_actor.file.signature_value_ids Number An integer array that contains one or more of the following signature values as derived from the Signature Bits. Possible values are: 0 = Unsigned, 1 = Signed, 2 = Code Signed, 3 = Class 3 Signed, 4 = Symantec Signed, 5 = Microsoft Signed, 6 = OS Component, 7 = Windows Hardware Wuality Labs (WHQL), 8 = Signer Explicitly Trusted, 9 = Signature Has Extra Date, 10 = Signature Uses MD5, 11 = Signature Uses SHA-1,12 = Signature Chain Not Valid, 13 = Signature From Catalog, 14 = Hash Does Not Match, 15 = Local Trusted Certificate, 16 = Trustworthy, 17 = Well Known Trusted Root Certificate, 18 = Heuristically Trustworthy, 19 = Symantec Internal, 20 = Signature Uses SHA-256, 21 = Signature Uses SHA-384, 22 = Signature Uses SHA-512, 23 = Signer Explicitly Revoked.
SymantecEDR.IncidentEvent.event_actor.file.sha2 String The SHA256 checksum of the file (hex string).
SymantecEDR.IncidentEvent.event_actor.file.original_name String The original name of the file.
SymantecEDR.IncidentEvent.process.pid Number The process identifier as reported by the operating system.
SymantecEDR.IncidentEvent.process.uid String The unique identifier of the process.
SymantecEDR.IncidentEvent.process.cmd_line String The command line that was used to launch the process.
SymantecEDR.IncidentEvent.process.signature_level_id Number A numeric representation of the signature level. Possible values are: 0 = UNKNOWN, 10 = UNSIGNED, 20 = SIGNED_BUT_UNTRUSTED, 30 = SIGNED, 40 = CLASS3_SIGNED, 50 = SYMC_SIGNED, 60 = MICROSOFT_SIGNED, 70 = MICROSOFT_OS_COMPONENT
SymantecEDR.IncidentEvent.process.integrity_id Number The process integrity level (Windows only). Possible values are: 0 = Unknown, 1 = Untrusted, 2 = Low, 3 = Medium, 4 = Medium Plus, 5 = High, 6 = System, 7 = Protected.
SymantecEDR.IncidentEvent.process.user.name String Process user name
SymantecEDR.IncidentEvent.process.user.sid String Process unique SID
SymantecEDR.IncidentEvent.process.file.normalized_path String The CSIDL normalized path name;Windows Only.
SymantecEDR.IncidentEvent.process.file.name String The name of the file.
SymantecEDR.IncidentEvent.process.file.md5 String The MD5 checksum of the file
SymantecEDR.IncidentEvent.process.file.modified Date The process identifier as reported by the operating system.
SymantecEDR.IncidentEvent.process.file.path String The full path to the object.
SymantecEDR.IncidentEvent.process.file.signature_company_name String The name of the company on the certificate.
SymantecEDR.IncidentEvent.process.file.signature_value_ids Number The issuer of the signature. Applicable events: 4096, 4099
SymantecEDR.IncidentEvent.enriched_data.rule_name String The name of the IntelliFilter rule that observes all of the endpoint data recorded events on the client.
SymantecEDR.IncidentEvent.enriched_data.suspicion_score Number Score that determines the suspiciousness of the action captured in the event. 1. Very Low: 1-25 (Informational) , 2. Low: 26-50 (Suspicious) , 3. Moderate: 51-75 (Suspicious) , 4. Severe: 76-87 (Malicious), 5. Very Severe: 88-100 (Malicious).
SymantecEDR.IncidentEvent.enriched_data.category_name String The IntelliFilter rules fall into the following categories: System File Launched Or Loaded From Unexpected Location = 8001, 8002. Suspicious PowerShell Script Executed = 8001 Suspicious N-gram = 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009. Process Termination = 8001, Process Launch = 8001, Load Point Modification = 8005, 8006, File with Double Exe Extension (.jpg.exe) = 8003, Attempt to Change to Windows Event Logs or Registry Settings = 8005, 8006, Suspicious Protocol-Port Usage By System Processes = 8007, All events = 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009, Applicable events: 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8009
SymantecEDR.IncidentEvent.enriched_data.category_id Number The possible values of supported category_id. 0 = All Events, 1 = Suspicious N-Gram, 2 = Process Launch , 3 = Process Termination , 100 = Suspicious Protocol-Port Usage By System Processes, 102 = Suspicious PowerShell commands
SymantecEDR.IncidentEvent.enriched_data.rule_description String Enriched rule description
SymantecEDR.IncidentEvent.event_uuid String The unique event UUID
SymantecEDR.IncidentEvent.attacks.technique_uid String The MITRE technique ID for the attack. Possible values are listed in https://attack.mitre.org/techniques/enterprise.
SymantecEDR.IncidentEvent.attacks.technique_name String The MITRE technique name for the attack.
SymantecEDR.IncidentEvent.attacks.tactic_ids Number The MITRE tactic ID(s) for the attack. Tactic ID values are: 1 = Initial Access , 2 = Execution , 3 = Persistence , 4 = Privilege Escalation , 5 = Defense Evasion , 6 = Credential Access , 7 = Discovery , 8 = Lateral Movement , 9 = Collection , 10 = Exfltration , 11 = Command and Control
SymantecEDR.IncidentEvent.attacks.tactic_uids String THe tactic Unique IDs.
SymantecEDR.IncidentEvent.event_source Number Indicates the reason of event being related to an incident. Possible values are: 1 - Event triggered the incident , 2 - Event is part of process lineage tracking, 3 - Event is likely related to the incident
SymantecEDR.IncidentEvent.ref_uid String The event reference UID.
SymantecEDR.IncidentEvent.correlation_uid String Event Correlation UID.
SymantecEDR.IncidentEvent.uuid String The unique id for this event.
SymantecEDR.IncidentEvent.log_name String The index of the event. Note: This is for informational purpose and cannot be used as a filter. Use time as start_time to query for events.

Command example

!symantec-edr-incident-event-list limit=1

Context Example

{
    "SymantecEDR": {
        "IncidentEvent": {
            "data_direction": 0,
            "device_ip": "172.16.14.42",
            "device_name": "WIN-TFB8L7BI77H",
            "device_time": "2023-01-26T18:55:27.296Z",
            "device_uid": "393b8e82-fe40-429f-8e5e-c6b79a0f2b1c",
            "event_source": 1,
            "event_uuid": "ff61e400-9daa-11ed-dcb5-00000000e61e",
            "incident": "ffcc1780-9daa-11ed-e218-000000000001",
            "internal_hostname": "WIN-TFB8L7BI77H",
            "internal_ip": "172.16.14.42",
            "log_name": "epmp_incident-2023-01-26",
            "log_time": "2023-01-26T18:55:27.992Z",
            "request_source": "user_submit",
            "sandbox_service": "cynic",
            "sep_installed": true,
            "severity_id": "",
            "type_id": 4117,
            "user_name": "Administrator",
            "uuid": "0025a930-9dab-11ed-f087-00000000000c"
        }
    }
}

Human Readable Output

Event for Incident List

Time TypeId Description DeviceName DeviceIp EventUuid Incident UserName
2023-01-26T18:55:27.296Z 4117 logged: WIN-TFB8L7BI77H 172.16.14.42 ff61e400-9daa-11ed-dcb5-00000000e61e ffcc1780-9daa-11ed-e218-000000000001 Administrator

symantec-edr-incident-list


Get incidents from Symantec EDR on-premise API.

Base Command

symantec-edr-incident-list

Input

Argument Name Description Required
limit The limit for the number of events listed per page.
Default is ‘50’.
.
Optional
page_size The number of results per page to display. Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
start_time The earliest time from which to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’).

.
Optional
end_time From current time to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’, now).
.
Optional
query A search query as a Lucene query string.

Example:
query=”type_id:(4096 OR 4098 OR 4123)”.
Optional
incident_id An incident ID. Optional
priority The incident severity/priority level. Possible values are: High, Medium, Low. Optional
status The incident status. Possible values are: Open, Waiting, In-progress, Closed. Optional

Context Output

Path Type Description
SymantecEDR.Incident.atp_incident_id Number A unique identifier for this incident.
SymantecEDR.Incident.log_name String The index of the incident. Note: This is for informational purpose and cannot be used as a filter. Use time as start_time to query for incidents. Example: epmp_incident-2018-03-01
SymantecEDR.Incident.summary String Summary information about the incident.
SymantecEDR.Incident.priority_level Number Priority level of the incident. Possible values are: Low, Medium, High
SymantecEDR.Incident.last_event_seen Date The creation time (in ISO 8601 format) when the last event associated with the incident was created. Matches the last event’s time field.
SymantecEDR.Incident.time Date The creation time (in ISO 8601 format) of the incident.
SymantecEDR.Incident.rule_name String The name of the rule that triggered this incident.
SymantecEDR.Incident.first_event_seen Date The creation time (in ISO 8601 format) when the first event associated with the incident was created. Matches the first event’s time field. This is likely before the incident’s creation time field given incidents are created after their first event is seen.
SymantecEDR.Incident.state Number The current state of the incident. Possible values are: Open, Waiting, In-progress, Closed
SymantecEDR.Incident.detection_type String Incident detection type.
SymantecEDR.Incident.device_time Date The timestamp (in ISO 8601 format) that specifies the time at which the event occurred.
SymantecEDR.Incident.recommended_action String Recommended action for this incident. Possible actions could be isolating an endpoint, deleting fle from endpoint, blacklist URL, or domain, etc.
SymantecEDR.Incident.updated Date The time (in ISO 8601 format) of last modification.
SymantecEDR.Incident.uuid String The GUID assigned for this incident. Example: “483e3fde-4556-4800-81b1-e8da5ee394b6”.
SymantecEDR.Incident.atp_rule_id String The rule that triggered this incident.
SymantecEDR.Incident.resolution Number The resolution of the closed incident. Possible values are: 0 =INSUFFICIENT_DATA. The incident does not have sufficient information to make a determination. 1 = SECURITY_RISK. The incident indicates a true security threat. 2 = FALSE_POSITIVE. The incident has been incorrectly reported as a security threat. 3 = MANAGED_EXTERNALLY. The incident was exported to an external application and will be triaged there. 4 = NOT_SET. The incident resolution was not set. 5 = BENIGN. The incident detected the activity as expected but is not a security threat. 6 = TEST. The incident was generated due to internal security testing.

Command example

!symantec-edr-incident-list limit=1

Context Example

{
    "SymantecEDR": {
        "Incident": {
            "atp_rule_id": "CynicIncident",
            "description": "Sandbox detection: eicar_com.zip",
            "detection_type": "Sandboxing",
            "first_seen": "2023-02-03T12:13:36.142Z",
            "incident_created": "2023-02-03T12:13:37.018Z",
            "incident_id": 100021,
            "incident_state": "Closed",
            "incident_uuid": "2fd76da0-a3bc-11ed-d519-000000000002",
            "last_seen": "2023-02-07T07:33:43.129Z",
            "last_updated": "2023-02-15T11:27:46.747Z",
            "log_name": "epmp_incident-2023-02-03",
            "priority": "High",
            "recommended_action": "You can isolate the endpoint(s), remove the file(s) and/or clean the system(s).",
            "resolution": "MANAGED_EXTERNALLY. The incident was exported to an external application and will be triaged there.",
            "rule_name": "Critical Cynic Detections"
        }
    }
}

Human Readable Output

Incident List

IncidentId Description IncidentCreated DetectionType LastUpdated Priority IncidentState AtpRuleId RuleName IncidentUuid LogName RecommendedAction Resolution FirstSeen LastSeen
100021 Sandbox detection: eicar_com.zip 2023-02-03T12:13:37.018Z Sandboxing 2023-02-15T11:27:46.747Z High Closed CynicIncident Critical Cynic Detections 2fd76da0-a3bc-11ed-d519-000000000002 epmp_incident-2023-02-03 You can isolate the endpoint(s), remove the file(s) and/or clean the system(s). MANAGED_EXTERNALLY. The incident was exported to an external application and will be triaged there. 2023-02-03T12:13:36.142Z 2023-02-07T07:33:43.129Z

symantec-edr-incident-comment-get


Get incident comments based on incident UUID.

Base Command

symantec-edr-incident-comment-get

Input

Argument Name Description Required
incident_id The unique incident ID. Required
limit The limit for the number of events listed per page. Default is 50. Optional
page The number of results per page to display. Optional
page_size The page number to view. Each page contains page_size values. Must be used along with page_size. Default is 1. Optional
start_time The earliest time from which to get events. Supports ISO (e.g., YYYY-MM-DDTHH:MM:SS.sssZ) and free text (e.g., 10 seconds, 5 minutes, 2 days, 1 weeks). Optional
end_time From current time to get events. Supports ISO (e.g., YYYY-MM-DDTHH:MM:SS.sssZ) and free text (e.g., 10 seconds, 5 minutes, 2 days, 1 weeks, now). Optional

Context Output

Path Type Description
SymantecEDR.IncidentComment.incident_id Number Incident ID.
SymantecEDR.IncidentComment.comment String The comment of the incident.
SymantecEDR.IncidentComment.time String The timestamp (in ISO 8601 format) that specifies the time at which the comment was added to incident.
SymantecEDR.IncidentComment.user_id String The ID of the user who registered the comment. Example: 100000.

Command example

!symantec-edr-incident-comment-get incident_id=100022 limit=1

Context Example

{
    "SymantecEDR": {
        "IncidentComment": [
            {
                "comment": "added as part of testing xsoar command examples",
                "incident_id": "100022",
                "incident_responder_name": "SEDR API",
                "time": "2023-02-15T11:33:54.470Z",
                "user_id": 100000
            }
        ]
    }
}

Human Readable Output

Incident Comment List

IncidentId Comment Time UserId IncidentResponderName
100022 added as part of testing xsoar command examples 2023-02-15T11:33:54.470Z 100000 SEDR API

symantec-edr-deny-list-policy-get


Get deny list policies.

Base Command

symantec-edr-deny-list-policy-get

Input

Argument Name Description Required
limit The maximum number of records to return. Default is 50. Minimum and maximum “limit” values are >= 10 and <= 1000. Note: If the page_size argument is set by the user then the limit argument will be ignored. Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size. Default is 1. Optional
page_size The number of results per page to display. Optional
domain Returns a list of domain type deny list policies that match the specified pattern value. If no value is specified, then all domain type allow list policies will be returned. Optional
denylist_id Returns a specific deny list policy for the specified identifier. If no value is specified, then all deny list policies will be returned. Optional
ip Returns a list of IP type deny list policies that match the specified pattern value. If no value is specified, then all IP type deny list policies will be returned. Optional
sha256 Returns a specific SHA256 type deny list policy for the specified SHA256 value. If no value is specified, then all SHA256 type deny list policies will be returned. Optional
url Returns list of URL type deny list policies that match the specified pattern value. If no value is specified, then all URL type deny list policies will be returned. \n\nNote: URL strings must be specified in encoded URL format. Optional

Context Output

Path Type Description
SymantecEDR.DenyListPolicy.comment String The comment for this deny list policy. If not specified, then defaults to empty string. Example: No monitoring required for Control traffic from this IP.
SymantecEDR.DenyListPolicy.id Number The unique ID of this deny list policy. This ID can be used in a patch or delete request. Note: This is ignored if present in a create request.
SymantecEDR.DenyListPolicy.target_type String The type of this deny list policy. Example: ip enum (ip, domain, url, sha256, incident_trigger_sig_id).
SymantecEDR.DenyListPolicy.target_value String The value of this deny list policy. Example: 1.1.1.1.

Command example

!symantec-edr-deny-list-policy-get limit=10

Context Example

{
    "SymantecEDR": {
        "DenyListPolicy": [
            {
                "comment": "Used for API testing",
                "id": "5",
                "target_type": "url",
                "target_value": "https://facebook.com"
            },
            {
                "id": "6",
                "target_type": "sha256",
                "target_value": "8c12399112cfd22e7d44845ee457b7cf1be7a1a8b780d5a47a70cdbdad9da270"
            }
        ]
    }
}

Human Readable Output

Deny List Policy List

Id TargetType TargetValue Comment
5 url https://facebook.com Used for API testing
6 sha256 8c12399112cfd22e7d44845ee457b7cf1be7a1a8b780d5a47a70cdbdad9da270  

symantec-edr-allow-list-policy-get


Get allow list policies.

Base Command

symantec-edr-allow-list-policy-get

Input

Argument Name Description Required
limit The maximum number of records to return. Default is 50.
Minimum and maximum “limit” values are >= 10 and <= 1000.
Note: If the page_size argument is set by the user then the limit argument will be ignored.
Optional
page The page number to view. Each page contains page_size values. Must be used along with page_size.
Default is ‘1’.
Optional
page_size The number of results per page to display. Optional
domain Returns a list of domain type allow list policies that match the specified pattern value. If no value is specified, then all domain type allow list policies will be returned. Optional
allowlist_id Returns specific allow list policy for the specified identifier. If no value is specified, then all allow list policies will be returned. Optional
ip Returns a list of IP type allow list policies that match the specified pattern value. If no value is specified, then all IP type allow list policies will be returned. Optional
url Returns a list of URL type allow list policies that match the specified pattern value. If no value is specified, then all URL type allow list policies will be returned.

Note: URL strings must be specified in encoded URL format.
Optional
sha256 Returns a specific SHA256 type allow list policy for the specified SHA256 value. If no value is specified, then all SHA256 type allow list policies will be returned. Optional

Context Output

Path Type Description
SymantecEDR.AllowListPolicy.comment String The comment for this allow list policy. If not specified, then defaults to empty string. Example: No monitoring required for control traffic from this IP.
SymantecEDR.AllowListPolicy.id String The unique ID of this allow list policy. This ID can be used in a patch or delete request. Note: This is ignored if present in a create request
SymantecEDR.AllowListPolicy.target_type String The type of this whitelist policy. enum (ip, domain, url, sha256, incident_trigger_sig_id). Example: ip.
SymantecEDR.AllowListPolicy.target_value String The value of this allow list policy. Example: 1.1.1.1.

Command example

!symantec-edr-allow-list-policy-get limit=10

Context Example

{
    "SymantecEDR": {
        "AllowListPolicy": [
            {
                "comment": "Allow List for API testing",
                "id": "1",
                "target_type": "url",
                "target_value": "https://twitter.com/"
            }
        ]
    }
}

Human Readable Output

Allow List Policy List

Id TargetType TargetValue Comment
1 url https://twitter.com/ Allow List for API testing

symantec-edr-incident-update


Incidents patch command to close an incident, update the resolution of a closed incident, or add comments to the incident.

Base Command

symantec-edr-incident-update

Input

Argument Name Description Required
action_type The operation to take on a specified incident.
- add_comment: Add comments to the incident.
- close_incident: Close incident.
- update_resolution: Update resolution of the closed incident. Possible values are: add_comment, close_incident, update_resolution.
Required
incident_id An incident ID for a specific operation. Required
value For add comments: The value should contain a user defined comment. The maximum length of the comment is 512 characters.

For update resolution of a closed incident: Any one of supported resolution values:
0 = INSUFFICIENT_DATA. The incident does not have sufficient information to make a determination.
1 = SECURITY_RISK. The incident indicates a true security threat.
2 = FALSE_POSITIVE. The incident has been incorrectly reported as a security threat.
3 = MANAGED_EXTERNALLY. The incident was exported to an external application and will be triaged there.
4 = NOT_SET. The incident resolution was not set.
5 = BENIGN. The incident detected the activity as expected but is not a security threat.
6 = TEST. The incident was generated due to internal security testing.
.
Optional
start_time The earliest time from which to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’).

Note: Only can provide if incidents are older then 30 days.
Optional
end_time From current time to get events. Supports ISO (e.g., 2021-12-28T00:00:00.000Z) and free text (e.g., ‘10 seconds’, ‘5 minutes’, ‘2 days’, ‘1 weeks’, now).

Note: Only can provide if incidents are older then 30 days.
Optional

Context Output

There is no context output for this command.

Command example

!symantec-edr-incident-update action_type=add_comment incident_id=100022 value="added as part of testing xsoar command examples"

Human Readable Output

Incident Add Comment

incident_id Message
100022 Successfully Updated

Command example

!symantec-edr-incident-update action_type=update_resolution incident_id=100021 value=3

Human Readable Output

Incident Update Status

incident_id Message
100021 Successfully Updated

Command example

!symantec-edr-incident-update action_type=close_incident incident_id=100022

Human Readable Output

Incident Close Incident

incident_id Message
100022 Successfully Updated

symantec-edr-endpoint-status


Get the command status.

Base Command

symantec-edr-endpoint-status

Input

Argument Name Description Required
command_id Command ID to query. Required

Context Output

Path Type Description
SymantecEDR.CommandStatus.command_issuer_name String The display name of the user who issued the command. Example: ATP API.
SymantecEDR.CommandStatus.state String Command state enum (completed, initializing, in_progress, error, cancel_requested, cancelled).
SymantecEDR.CommandStatus.status.error_code String The error codes for a specific target. Possible values:-1 = Error, 1 = In-progress, 9000 = File Is Clean, 9001 = File Is Malware, 9003 = File Size Over Limit (File Size Should Not Exceed 10MB For Sandbox Submission), 9005 = Query To Sandbox Failed (Check Network Connectivity), 9006 = File Type Not Supported (Check With Symantec Support For Sandbox Supported File List), 9007 = File Not Found In FileStore (Use get_endpoint_fle Command To Copy File Into FileStore).
SymantecEDR.CommandStatus.status.message String Message explaining error code. Possible values: Error (-1), In progress (1), File Is Clean (9000), File Is Malware (9001), File Size Over Limit (9003), Query To Sandbox Failed (9005), File Type Not Supported (9006), File Not Found In FileStore (9007),
SymantecEDR.CommandStatus.status.state String The command status for a specific target. Values: 0 = Completed 1 = In progress 2 = Error 3 = Cancelled 4 = Cancel requested.
SymantecEDR.CommandStatus.status.target String The SHA256 of a file.

Command example

!symantec-edr-endpoint-status command_id=b44a351058454c81af41ca98a20d622c-2022-12-18

Context Example

{
    "SymantecEDR": {
        "CommandStatus": {
            "Command Issuer Name": "ATP API",
            "error_code": 301,
            "message": "File was not found on endpoint",
            "state": 0
        }
    }
}

Human Readable Output

Command Status

State Command Issuer Name Message ErrorCode
0 ATP API File was not found on endpoint 301

symantec-edr-endpoint-rejoin


Rejoins endpoints by re-establishing connections that the endpoint(s) has to internal networks and external networks, based on the endpoint IDs.

Base Command

symantec-edr-endpoint-rejoin

Input

Argument Name Description Required
device_id The device ID of the target computer. Required

Context Output

Path Type Description
SymantecEDR.Command.rejoin_endpoint.command_id String Command ID.
SymantecEDR.Command.rejoin_endpoint.error_code Number The status of the command action. Values:-1 = Error 0 = Command rejoin_endpoint successfully requested 1 = Command rejoin_endpoint not supported for target command type 2 = Command rejoin_endpoint failed because the target command is already in terminal state (i.e., completed, error, or cancelled) 3 = Command rejoin_endpoint is already in progress for the target command
SymantecEDR.Command.rejoin_endpoint.message String Message explaining error code. Values: -1 = Error 0 = Command rejoin_endpoint successfully requested 1 = Command rejoin_endpoint not supported for target command type 2 = Command rejoin_endpoint failed because the target command is already in terminal state 3 = Command rejoin_endpoint is already in progress for the target command

Command example

!symantec-edr-endpoint-rejoin device_id=393b8e82-fe40-429f-8e5e-c6b79a0f2b1c

Context Example

{
    "SymantecEDR": {
        "Command": {
            "Rejoin Endpoint": {
                "command_id": "1c576eed2f1b4c3dbefa72594f1d3328-2023-02-15",
                "error_code": 0,
                "message": "Command rejoin_endpoint successfully requested"
            }
        }
    }
}

Human Readable Output

Command Rejoin Endpoint

Message CommandId
Command rejoin_endpoint successfully requested 1c576eed2f1b4c3dbefa72594f1d3328-2023-02-15

symantec-edr-endpoint-delete-file


Deletes a file, i.e., deletes all instances of the file, based on the file hash that you have specified from the endpoint using the device ID.

Base Command

symantec-edr-endpoint-delete-file

Input

Argument Name Description Required
device_id Device ID of the target computer/endpoint,. Required
sha2 The SHA256 value of the target file. Required

Context Output

Path Type Description
SymantecEDR.Command.delete_endpoint_file.command_id String Command ID.
SymantecEDR.Command.delete_endpoint_file.error_code String The status of the command action. Possible values:-1 = Error0= Command delete_endpoint_file successfully requested1 = Command delete_endpoint_file not supported for target command type2 = Command delete_endpoint_file failed because the target command is already in terminal state (i.e.,completed, error, or cancelled)3 = Command delete_endpoint_file is already in progress for the target command.
SymantecEDR.Command.delete_endpoint_file.message String Message explaining error code. Possible Values: -1 = Error 0 = Command delete_endpoint_file successfully requested 1 = Command delete_endpoint_file not supported for target command type 2 = Command delete_endpoint_file failed because the target command is already in terminal state 3 = Command delete_endpoint_file is already in progress for the target command.

Command example

!symantec-edr-endpoint-delete-file device_id=393b8e82-fe40-429f-8e5e-c6b79a0f2b1c sha2=302c968ab3e1227d54df4e72f39088d7483d25eeb3037f0b16bc39cef2728fa4

Context Example

{
    "SymantecEDR": {
        "Command": {
            "Delete Endpoint": {
                "command_id": "1d8cd7cf132746de862cfe208211df7b-2023-02-15",
                "error_code": 0,
                "message": "Command delete_endpoint_file successfully requested"
            }
        }
    }
}

Human Readable Output

Command Delete Endpoint

Message CommandId
Command delete_endpoint_file successfully requested 1d8cd7cf132746de862cfe208211df7b-2023-02-15

symantec-edr-endpoint-cancel-command


Cancel a command that is already in progress. Cancel the command execution on all the endpoints where it is still in progress. \nOnly one command can be cancelled at a time.

Base Command

symantec-edr-endpoint-cancel-command

Input

Argument Name Description Required
command_id Command ID. Example: “f283b7dc9255493daed443e13e726903-2018-05-16”. Required

Context Output

Path Type Description
SymantecEDR.Command.cancel.command_id String Command ID.
SymantecEDR.Command.cancel.error_code String The status of the command action. Possible Values: -1 = Error 0 = Command cancel successfully requested 1 = Command cancel not supported for target command type 2 = Command cancel failed because the target command is already in terminal state (i.e., completed, error, or cancelled) 3 = Command cancel is already in progress for the target command.
SymantecEDR.Command.cancel.message String Message explaining error code. Possible Values: -1 = Error 0 = Command cancel successfully requested 1 = Command cancel not supported for target command type 2 = Command cancel failed because the target command is already in terminal state 3 = Command cancel is already in progress for the target command.

Command example

!symantec-edr-endpoint-cancel-command command_id=bee3647b420f4e1bab822ca283fbeb00-2022-12-18

Context Example

{
    "SymantecEDR": {
        "Command": {
            "Cancel Endpoint": {
                "command_id": "bee3647b420f4e1bab822ca283fbeb00-2022-12-18",
                "error_code": 1,
                "message": "Command cancel_command not supported for target command type."
            }
        }
    }
}

Human Readable Output

Command Cancel Endpoint

Message CommandId
Command cancel_command not supported for target command type. bee3647b420f4e1bab822ca283fbeb00-2022-12-18

file


Issue a sandbox command of a specific SHA2.

Base Command

file

Input

Argument Name Description Required
polling Whether the command should poll until the result is ready. Possible values are: true, false. Default is True. Optional
file The file hash SHA256. Required
timeout_in_seconds Timeout for polling. Default is 600 seconds. Optional
interval_in_seconds Interval between polling. Default is 10 seconds. Must be 10 or higher. Optional

Context Output

Path Type Description
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.
DBotScore.Reliability String Reliability of the source providing the intelligence data.
File.MD5 String MD5 hash of the file submitted for analysis.
File.SHA1 String SHA1 hash of the file submitted for analysis.
File.SHA256 String SHA256 hash of the file submitted for analysis.

Configuration parameters

  • url — Server URL (i.e., https://host:port) (required)
  • credentials — Client ID (required)
  • isFetch — Fetch incidents
  • fetch_incidents_events_type — Incident data source
  • isIncidentsEvent — Fetch incidents alerts
  • isIncidentComment — Fetch incident comments
  • fetch_status — Incidents "Status" to filter out fetching as incidents. Comma-separated lists are supported, e.g., Open, In-Progress
  • fetch_priority — Incidents "Priority" to filter out fetching as incidents. Comma-separated lists are supported, e.g., Medium,High.
  • fetch_event_status — Events "Status" to filter out fetching as incidents. Comma-separated lists are supported, e.g., Unknown, Success
  • fetch_severity — Events "Severity" to filter out fetching as incidents. Comma-separated lists are supported, e.g., Info, Warning
  • fetch_incidents_query — Query string to fetch incidents/events. For example - "updated:[2017-01-01T00:00:00.000Z TO 2017-01-08T00:00:00.000Z]"
  • first_fetch — First fetch timestamp (<number> <time unit>, e.g., 10 minutes, 12 hours, 7 days)
  • max_fetch — Maximum number of incidents to fetch
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • integration_reliability — Source Reliability

Commands (21)

  • file

    Issue a sandbox command of a specific SHA2.

  • symantec-edr-allow-list-policy-get

    Get allow list policies.

  • symantec-edr-audit-event-list

    Get audit events.

  • symantec-edr-deny-list-policy-get

    Get deny list policies.

  • symantec-edr-domain-file-association-list

    List of domain and file association.

  • symantec-edr-domain-instance-list

    Get domain instances.

  • symantec-edr-endpoint-cancel-command

    Cancel a command that is already in progress. Cancel the command execution on all the endpoints where it is still in progress. \nOnly one command can be cancelled at a time.

  • symantec-edr-endpoint-delete-file

    Deletes a file, i.e., deletes all instances of the file, based on the file hash that you have specified from the endpoint using the device ID.

  • symantec-edr-endpoint-domain-association-list

    List of endpoint and domain association.

  • symantec-edr-endpoint-file-association-list

    List of domain and file association.

  • symantec-edr-endpoint-instance-list

    Get endpoint instances.

  • symantec-edr-endpoint-isolate

    Isolates or quarantines endpoints by cutting connections that the endpoint(s) has to internal networks and external networks, based on the endpoint device IDs.

  • symantec-edr-endpoint-rejoin

    Rejoins endpoints by re-establishing connections that the endpoint(s) has to internal networks and external networks, based on the endpoint IDs.

  • symantec-edr-endpoint-status

    Get the command status.

  • symantec-edr-event-list

    Get events or system alerts from EDR on-premise.

  • symantec-edr-file-instance-list

    Get File Instances.

  • symantec-edr-incident-comment-get

    Get incident comments based on incident UUID.

  • symantec-edr-incident-event-list

    Get events for incidents.

  • symantec-edr-incident-list

    Get incidents from Symantec EDR on-premise API.

  • symantec-edr-incident-update

    Incidents patch command to close an incident, update the resolution of a closed incident, or add comments to the incident.

  • symantec-edr-system-activity-list

    Get system activities or logs.

"""
Symantec Endpoint Detection and Response (EDR) On-Prem integration with Symantec-EDR
"""

import json

from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
import dateparser
import urllib3
import traceback
from collections.abc import Callable

# Disable insecure warnings
urllib3.disable_warnings()  # pylint: disable=no-member

""" CONSTANTS """

DEFAULT_INTERVAL = 30
DEFAULT_TIMEOUT = 600

# Symantec TOKEN timeout 60 minutes
SESSION_TIMEOUT_SEC = 3600
ISO8601_F_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
INTEGRATION_CONTEXT_NAME = "SymantecEDR"
DEFAULT_OFFSET = 0
DEFAULT_PAGE_SIZE = 50
PAGE_NUMBER_ERROR_MSG = (
    "Invalid Input Error: page number should be greater than zero. Note: Page must be used along with page_size"
)
PAGE_SIZE_ERROR_MSG = "Invalid Input Error: page size should be greater than zero. Note: Page must be used along with page_size"

INVALID_QUERY_ERROR_MSG = (
    'Invalid query arguments. Either use any optional filter in lieu of "query" or explicitly use only "query" argument'
)

INCIDENT_PATCH_ACTION = ["add_comment", "close_incident", "update_resolution"]
INCIDENT_PRIORITY_LEVEL: dict[str, str] = {"1": "Low", "2": "Medium", "3": "High"}

INCIDENT_STATUS: dict[str, str] = {
    "1": "Open",
    "2": "Waiting",
    "3": "In-Progress",
    "4": "Closed",
}

INCIDENT_RESOLUTION: dict[str, str] = {
    "0": "INSUFFICIENT_DATA. The incident does not have sufficient information to make a determination.",
    "1": "SECURITY_RISK. The incident indicates a true security threat.",
    "2": "FALSE_POSITIVE. The incident has been incorrectly reported as a security threat.",
    "3": "MANAGED_EXTERNALLY. The incident was exported to an external application and will be triaged there.",
    "4": "NOT_SET. The incident resolution was not set.",
    "5": "BENIGN. The incident detected the activity as expected but is not a security threat.",
    "6": "TEST. The incident was generated due to internal security testing.",
}

EVENT_SEVERITY: dict[str, str] = {
    "1": "Info",
    "2": "Warning",
    "3": "Minor",
    "4": "Major",
    "5": "Critical",
    "6": "Fatal",
}

# Status for Applicable events : 1, 20, 21, 1000
EVENT_STATUS: dict[str, str] = {"0": "Unknown", "1": "Success", "2": "Failure"}

EVENT_NODE_ROLE: dict[str, str] = {
    "0": "Pre-Bootstrap",
    "1": "Network Scanner",
    "2": "Management",
    "3": "StandaloneNetwork",
    "4": "Standalone Endpoint",
    "5": "All in One",
}

EVENT_SEVERITY_MAPPING = {
    "1": IncidentSeverity.INFO,
    "2": IncidentSeverity.LOW,
    "3": IncidentSeverity.MEDIUM,
    "4": IncidentSeverity.HIGH,
    "5": IncidentSeverity.CRITICAL,
    "6": IncidentSeverity.CRITICAL,
}

EVENT_TYPE: dict[str, str] = {
    "1": "Application Activity",
    "20": "Session Audit",
    "21": "Entity Audit",
    "1000": "System Health",
    "4096": "Reputation Request",
    "4098": "Intrusion Prevention",
    "4099": "Suspicious File",
    "4100": "SONAR",
    "4102": "Antivirus (endpoint detection)",
    "4112": "Blacklist (IP/URL/Domain)",
    "4113": "Vantage",
    "4115": "Insight",
    "4116": "Mobile Insight",
    "4117": "Sandbox",
    "4118": "Blacklist (file)",
    "4123": "Endpoint File Detection",
    "4124": "Endpoint Detection",
    "4125": "Email",
    "4353": "Antivirus | Network Detection",
    "8000": "Session Events",
    "8001": "Process Events",
    "8002": "Module Event",
    "8003": "File Events",
    "8004": "Directory Events",
    "8005": "Registry Events",
    "8006": "Registry Events",
    "8007": "Network Events",
    "8009": "Kernel Events",
    "8015": "Monitored Source Events",
    "8016": "Startup Application Configuration Change Events",
    "8018": "AMSI Activity Events",
    "8080": "EOC Session Query Result Events",
    "8081": "EOC Process Query Result Events",
    "8082": "EOC Module Query Result Events",
    "8083": "EOC File Query Result Events",
    "8084": "EOC Directory Query Result Events",
    "8085": "EOC Registry Key Query Result Events",
    "8086": "EOC Registry Value Query Result Events",
    "8089": "EOC Kernel Query Result Events",
    "8090": "EOC Service Query Result Events",
}

SANDBOX_STATE: dict[str, str] = {"0": "Completed", "1": "In Progress", "2": "Unknown"}

DOMAIN_DISPOSITION_STATUS: dict[str, str] = {
    "0": "Healthy",
    "1": "unknown",
    "2": "Suspicious",
    "3": "Bad",
}

HTTP_ERRORS = {
    400: "400 Bad Request - Incorrect or invalid parameters",
    401: "401 Authentication error - Incorrect or invalid username or password",
    403: "403 Forbidden - please provide valid username and password.",
    404: "404 Resource not found - invalid endpoint was called.",
    408: "408 Timeout - Check Server URl/Port",
    410: "410 Gone - Access to the target resource is no longer available at the origin server",
    500: "500 Internal Server Error - please try again after some time.",
    502: "502 Bad Gateway - Could not connect to the origin server",
    503: "503 Service Unavailable",
}

VERDICT_TO_SCORE_DICT = {
    "clean": Common.DBotScore.GOOD,
    "file_type_unrecognized": Common.DBotScore.SUSPICIOUS,
    "malware": Common.DBotScore.BAD,
}

# Map severity to Demisto severity for incident creation
XSOAR_SEVERITY_MAP = {"High": 3, "Medium": 2, "Low": 1}

# Reverse Incident Priority and State mapping
REVERSE_INCIDENT_PRIORITY = {v: k for k, v in INCIDENT_PRIORITY_LEVEL.items()}
REVERSE_INCIDENT_STATE = {v: k for k, v in INCIDENT_STATUS.items()}
REVERSE_EVENT_SEVERITY = {v.lower(): k for k, v in EVENT_SEVERITY.items()}
REVERSE_EVENT_STATUS = {v: k for k, v in EVENT_STATUS.items()}

""" CLIENT CLASS """


class Client(BaseClient):
    """
    This Client implements API calls, and does not contain any XSOAR logic.
    Should only do requests and return data.
    It inherits from BaseClient defined in CommonServer Python.
    Most calls use _http_request() that handles proxy, SSL verification, etc.
    For this implementation, no special attributes defined
    """

    def __init__(
        self,
        base_url: str,
        verify: bool,
        proxy: bool,
        client_id: str,
        client_secret: str,
        fetch_incidents_type: str,
        first_fetch: str = "3 days",
        fetch_limit: Optional[int] = 50,
        is_incident_event: bool = False,
        is_fetch_comment: bool = False,
        fetch_status: list = None,
        fetch_priority: list = None,
        fetch_event_status: list = None,
        fetch_event_severity: list = None,
        fetch_query: str = None,
    ):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy)

        self.client_key = client_id
        self.secret_key = client_secret
        self.fetch_incidents_type = fetch_incidents_type
        self.first_fetch = first_fetch
        self.fetch_limit = fetch_limit
        self.is_incident_event = is_incident_event
        self.is_fetch_comment = is_fetch_comment
        self.fetch_status = fetch_status
        self.fetch_priority = fetch_priority
        self.fetch_event_status = fetch_event_status
        self.fetch_event_severity = fetch_event_severity
        self.fetch_query = fetch_query
        self.access_token = self.get_access_token_or_login()

    @property
    def headers(self) -> dict:
        """
        Client headers method
        Returns:
            self.headers
        """
        if self.access_token is None:  # for logging in, before self.access_token is set
            return {"Content-Type": "application/json"}

        return {
            "Authorization": f"Bearer {self.access_token}",
            "Content-Type": "application/json",
        }

    @staticmethod
    def get_access_token_from_context() -> str | None:
        """
        Symantec EDR on-premise get previous access token from global integration context
        Args:

        Returns:
            return token from context or None
        """
        global_context = demisto.getIntegrationContext()
        if access_token_timestamp := global_context.get("access_token_timestamp"):
            now_timestamp = int(time.time())
            if token := global_context.get("access_token"):
                time_diff = int(now_timestamp - access_token_timestamp)
                if time_diff <= SESSION_TIMEOUT_SEC:
                    return token
                else:
                    LOG("Access token expired")
        return None

    def get_access_token_or_login(self) -> str:
        """
        Check Access Token from Context if that still valid then using the same token
         else Generate new Access token
        Return:
            return access_token
        """
        if last_access_token := self.get_access_token_from_context():
            LOG("Last access token in context is still active, reusing it")
            access_token = last_access_token
        else:
            try:
                response = self._http_request(
                    method="POST",
                    url_suffix="/atpapi/oauth2/tokens",
                    auth=(self.client_key, self.secret_key),
                    data={"grant_type": "client_credentials"},
                    resp_type="response",
                )
                response.raise_for_status()
            except requests.exceptions.HTTPError as exc:
                if error_msg := HTTP_ERRORS.get(exc.response.status_code):
                    raise DemistoException(f"{error_msg}", res=exc.response) from exc

                raise

            LOG("Generated Access token.")
            access_token = response.json().get("access_token")
            demisto.setIntegrationContext(
                demisto.getIntegrationContext()
                | {
                    "access_token": access_token,
                    "access_token_timestamp": int(time.time()),
                }
            )

        return access_token

    def http_request(
        self,
        method: str,
        endpoint: str,
        params: dict[str, Any] = None,
        json_data: Union[dict[str, Any], list] = None,
        ignore_empty_response: bool = False,
    ) -> requests.Response:
        """
        Call Symantec EDR On-prem POST and GET Request API
        Args:
            method (str): Request Method support POST and GET
            endpoint (str): API endpoint
            params (dict): URL parameters to specify the query for GET.
            json_data (dict): The dictionary to send in a request for POST.
            ignore_empty_response (bool): Default is False, For PATCH method provide this argument as True
        Returns:
            Return the raw api response from Symantec EDR on-premise API.
        """
        try:
            response = self._http_request(
                method=method.upper(),
                url_suffix=endpoint,
                headers=self.headers,
                json_data=json_data,
                params=params,
                resp_type="response",
                return_empty_response=ignore_empty_response,
            )
            response.raise_for_status()
        except requests.exceptions.HTTPError as exc:
            if error_msg := HTTP_ERRORS.get(exc.response.status_code):
                raise DemistoException(f"{error_msg}", res=exc.response) from exc

            raise

        return response

    def list_domain_file(self, payload: dict) -> dict[str, Any]:
        """
        Client method for domain file association list
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/associations/entities/domains-files",
            params={},
            json_data=payload,
        ).json()

    def list_endpoint_domain(self, payload: dict) -> dict[str, Any]:
        """
        Client method for endpoint domain association list
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/associations/entities/endpoints-domains",
            params={},
            json_data=payload,
        ).json()

    def list_endpoint_file(self, payload: dict) -> dict[str, Any]:
        """
        Client method for endpoint file association list
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/associations/entities/endpoints-files",
            params={},
            json_data=payload,
        ).json()

    def get_audit_event(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get Audit Events
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/auditevents",
            params={},
            json_data=payload,
        ).json()

    def get_event_list(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get Events List
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(method="POST", endpoint="/atpapi/v2/events", params={}, json_data=payload).json()

    def get_system_activity(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get System Activity
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/systemactivities",
            params={},
            json_data=payload,
        ).json()

    def get_event_for_incident(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get event for Incident
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/incidentevents",
            params={},
            json_data=payload,
        ).json()

    def get_incident(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get Incident
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(method="POST", endpoint="/atpapi/v2/incidents", params={}, json_data=payload).json()

    def get_incident_comment(self, payload: dict, uuid: str) -> dict[str, Any]:
        """
        Client method for get Incident
        Args:
            payload (dict): request json body
            uuid (str): Incident Unique ID
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint=f"/atpapi/v2/incidents/{uuid}/comments",
            params={},
            json_data=payload,
        ).json()

    def add_incident_comment(self, uuid: str, value: str) -> requests.Response:
        """
        Client method for add Incident comment

        Args:
            uuid : Incident Unique ID
            value : Incident Comment
        Returns:
            return response json
        """
        if len(value) > 512:
            raise ValueError("The maximum allowed length of a comment is 512 characters")

        request_data: list[dict[str, Any]] = [{"op": "add", "path": f"/{uuid}/comments", "value": value}]
        return self.http_request(
            method="PATCH",
            endpoint="/atpapi/v2/incidents",
            params={},
            json_data=request_data,
            ignore_empty_response=True,
        )

    def close_incident(self, uuid: str, value: int) -> requests.Response:
        """
        Client method for close incident

        Args:
            uuid : Incident Unique ID
            value : Incident Comment
        Returns:
            return response json
        """
        request_data: list[dict[str, Any]] = [{"op": "replace", "path": f"/{uuid}/state", "value": value}]
        return self.http_request(
            method="PATCH",
            endpoint="/atpapi/v2/incidents",
            params={},
            json_data=request_data,
            ignore_empty_response=True,
        )

    def update_incident(self, uuid: str, value: int) -> requests.Response:
        """
        Client method for update incident Resolution

        Args:
            uuid : Incident Unique ID
            value : Incident Comment
        Returns:
            return response json
        """
        request_data: list[dict[str, Any]] = [{"op": "replace", "path": f"/{uuid}/resolution", "value": value}]
        return self.http_request(
            method="PATCH",
            endpoint="/atpapi/v2/incidents",
            params={},
            json_data=request_data,
            ignore_empty_response=True,
        )

    def get_file_instance(self, payload: dict, sha2: str | None) -> dict[str, Any]:
        """
        Client method for get file instance
        Args:
            payload (dict): request json body
            sha2 (str): file sha2 value
        Returns:
            return response json
        """
        endpoint = f"/atpapi/v2/entities/files/{sha2}/instances" if sha2 else "/atpapi/v2/entities/files/instances"

        return self.http_request(method="POST", endpoint=endpoint, params={}, json_data=payload).json()

    def get_domain_instance(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get domain instance
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/entities/domains/instances",
            params={},
            json_data=payload,
        ).json()

    def get_endpoint_instance(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get endpoint instance
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/entities/endpoints/instances",
            params={},
            json_data=payload,
        ).json()

    def get_allow_list(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get allow list
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="GET",
            endpoint="/atpapi/v2/policies/allow_list",
            params=payload,
            json_data={},
        ).json()

    def get_deny_list(self, payload: dict) -> dict[str, Any]:
        """
        Client method for get deny list
        Args:
            payload (dict): request json body
        Returns:
            return response json
        """
        return self.http_request(
            method="GET",
            endpoint="/atpapi/v2/policies/deny_list",
            params=payload,
            json_data={},
        ).json()

    def get_cancel_endpoint(self, command_id: str) -> dict[str, Any]:
        """
        Client method for cancel endpoint
        Args:
            command_id (str): command_id separate by commas
        Returns:
            return response json
        """
        payload = {"action": "cancel_command", "targets": argToList(command_id)}
        return self.http_request(method="POST", endpoint="/atpapi/v2/commands", params={}, json_data=payload).json()

    def get_delete_endpoint(self, device_uid: str, file_sha2: str) -> dict[str, Any]:
        """
        Client method for delete endpoint
        Args:
            device_uid (str): Endpoint device id
            file_sha2 (str): Endpoint file sha2
        Returns:
            return response json
        """
        payload = {
            "action": "delete_endpoint_file",
            "targets": [{"device_uid": device_uid, "hash": file_sha2}],
        }
        return self.http_request(method="POST", endpoint="/atpapi/v2/commands", params={}, json_data=payload).json()

    def get_isolate_endpoint(self, device_uid: str) -> dict[str, Any]:
        """
        Client method for Isolate endpoint
        Args:
            device_uid (str): Endpoint Device UUID
        Returns:
            return response json
        """
        payload = {"action": "isolate_endpoint", "targets": argToList(device_uid)}
        return self.http_request(method="POST", endpoint="/atpapi/v2/commands", params={}, json_data=payload).json()

    def get_rejoin_endpoint(self, device_uid: str) -> dict[str, Any]:
        """
        Client method for Rejoin endpoint
        Args:
            device_uid (str): Endpoint Device UUID
        Returns:
            return response json
        """
        payload = {"action": "rejoin_endpoint", "targets": argToList(device_uid)}
        return self.http_request(method="POST", endpoint="/atpapi/v2/commands", params={}, json_data=payload).json()

    def get_status_endpoint(self, command_id: str, payload: dict) -> dict[str, Any]:
        """
        Client method for get endpoint status
        Args:
            command_id (str): Endpoint command_id
            payload: request body

        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint=f"/atpapi/v2/commands/{command_id}",
            params={},
            json_data=payload,
        ).json()

    def get_sandbox_verdict_for_file(self, sha2: str) -> dict[str, Any]:
        """
        Client method for get sandbox verdict for specific file sha256
        Args:
            sha2 (str): File SHA256
        Returns:
            return response json
        """
        return self.http_request(
            method="GET",
            endpoint=f"/atpapi/v2/sandbox/results/{sha2}/verdict",
            params={},
            json_data={},
        ).json()

    def get_file_entity(self, sha2: str) -> dict[str, Any]:
        """
        Client method for Get File Entity for specific SHA2
        Args:
            sha2 (str): File SHA256
        Returns:
            return response json
        """
        return self.http_request(
            method="GET",
            endpoint=f"/atpapi/v2/entities/files/{sha2}",
            params={},
            json_data={},
        ).json()

    def get_sandbox_status(self, command_id: str) -> dict[str, Any]:
        """
        Client method to Get Sanbox Status for specific file
        Args:
            command_id (str): sandbox command ID
        Returns:
            return response json
        """
        return self.http_request(
            method="GET",
            endpoint=f"/atpapi/v2/sandbox/commands/{command_id}",
            params={},
            json_data={},
        ).json()

    def submit_file_to_sandbox_analyze(self, payload: dict) -> dict[str, Any]:
        """
        Client method for sandbox analyzes
        Args:
            payload: request body

        Returns:
            return response json
        """
        return self.http_request(
            method="POST",
            endpoint="/atpapi/v2/sandbox/commands",
            params={},
            json_data=payload,
        ).json()

    def test_module(self) -> str:
        """
        Returns ok on a successful connection to the Symantec EDR API.
        Otherwise, an exception should be raised by self._http_request()
        """
        incident_type = self.fetch_incidents_type
        query = self.fetch_query or ""
        fetch_args = {"verb": "query", "limit": 1, "query": query}
        self.get_incident(fetch_args) if incident_type == "incident" else self.get_event_list(fetch_args)
        return "ok"


""" HELPER FUNCTIONS """


def convert_to_iso8601(timestamp: str) -> str:
    """Convert timestamp from an iso8601 format

    Args:
        timestamp: Any valid timestamp or provide timedelta e.g. now, "<n> days", "<n> weeks",
        "<n> months", "1 months ago"

    Returns: return timestamp in an iso 8601 format.
    """
    if datetime_from_timestamp := dateparser.parse(timestamp, settings={"TIMEZONE": "UTC"}):
        return f"{datetime_from_timestamp.strftime(ISO8601_F_FORMAT)[:-3]}Z"
    else:
        raise ValueError(f"{timestamp=} could not be parsed")


def extract_headers_for_readable_output(summary_data: list[dict]) -> list:
    """
    Symantec EDR formatting Readable output Header
    Args:
        summary_data (list[dict]): Human readable output summary data

    Returns:
        Return string headers to a camel case.

    """
    if not summary_data:
        return []

    headers = summary_data[0] if summary_data else {}
    headers = list(headers.keys())
    return [camelize_string(column) for column in headers]


def get_data_of_current_page(response_data: list[dict[str, Any]], offset: int = 0, limit: int = 0) -> list:
    """
    Retrieve a list element based on offset and limit
    Args:
        response_data (list): Raw API result list
        offset (int) : Offset
        limit (int) : Page Limit

    Returns:
        Return List of an object from the response according to the limit, page and page_size.

    """

    if offset >= 0 and limit >= 0:
        return response_data[offset : (offset + limit)]
    return response_data[:limit]


def compile_command_title_string(context_name: str, args: dict, record: int) -> str:
    """
    Symantec EDR on-premise display title and pagination
        If page/page_size is input, then the limit should be ignored.
        If only page or page_size were input,
        then the default for the other that is missing will be added in the code.
        limit can work by itself independently, without page and page_size
    Args:
        context_name (str): Commands sub context name
        args (dict): demisto.args()
        record (int): Total Number of Records
    Returns:
        Return the title for the readable output

    """
    page = arg_to_number(args.get("page"))
    page_size = arg_to_number(args.get("page_size"))

    if page is None and page_size:
        page = 1

    if page_size is None and page:
        page_size = DEFAULT_PAGE_SIZE
        if record < DEFAULT_PAGE_SIZE:
            page_size = record

    if (page and page_size) and (page > 0 and page_size > 0):
        return f"{context_name} List\nShowing page {page}\nShowing {page_size} out of {record} Record(s) Found."

    return f"{context_name} List"


def parse_process_sub_object(data: dict) -> dict:
    """
    Retrieve event process sub object data
    Args:
        data (dict): Event process data
    Returns:
        return process data
    """
    ignore_key_list: list[str] = ["file", "user"]
    return extract_raw_data(data, ignore_key_list)


def parse_attacks_sub_object(data: list[dict]) -> dict:
    """
    Retrieve event attacks sub object data
    Args:
        data (dict): Event attacks data

    Returns:
        return attacks data
    """
    ignore_key_list: list[str] = ["tactic_ids", "tactic_uids"]
    attacks_dict = extract_raw_data(data, ignore_key_list, prefix="attacks")

    for attack in data:
        cnt = 0
        # tactic_ids
        if tactic_ids_list := attack.get("tactic_ids", []):
            tactic_ids_dict = {f"attacks_tactic_ids_{cnt}": convert_list_to_str(tactic_ids_list)}
            attacks_dict |= tactic_ids_dict

        # tactic uids
        if tactic_uids_list := attack.get("tactic_uids", []):
            tactic_uids_dict = {f"attacks_tactic_uids_{cnt}": convert_list_to_str(tactic_uids_list)}
            attacks_dict |= tactic_uids_dict

        cnt += 1
    return attacks_dict


def parse_event_data_sub_object(data: dict[str, Any]) -> dict:
    """
    Retrieve event data sub object data
    Args:
        data (dict): Event data
    Returns:
        return event data
    """
    result: dict = {}
    for key in (
        "sepm_server",
        "search_config",
        "atp_service",
    ):
        if values := data.get(key):
            result |= extract_raw_data(values, [], key)

    return result


def parse_enriched_data_sub_object(data: dict[str, Any]) -> dict:
    """
    Retrieve event enriched sub object data
    Args:
        data (dict): Event enriched data
    Returns:
        return enriched data
    """
    return extract_raw_data(data, [], "enriched_data")


def parse_user_sub_object(data: dict[str, Any], obj_prefix: Optional[str]) -> dict:
    """
    Retrieve event user sub object data
    Args:
        data (dict): Event user data
        obj_prefix (optional) : Object prefix name
    Returns:
        return user data
    """
    prefix = f"{obj_prefix}_user" if obj_prefix else "user"
    return extract_raw_data(data, [], prefix)


def parse_xattributes_sub_object(data: dict[str, Any], obj_prefix: Optional[str]) -> dict:
    """
    Retrieve event xattributes sub object data
    Args:
        data (dict): Event xattribute data
        obj_prefix (optional) : Object prefix name
    Returns:
        return event data
    """
    prefix = f"{obj_prefix}_xattributes" if obj_prefix else "xattributes"
    return extract_raw_data(data, [], prefix)


def parse_event_actor_sub_object(data: dict[str, Any]) -> dict:
    """
    Retrieve event actor object data
    Args:
        data (dict): Event actor data
    Returns:
        return event actor data
    """
    # Sub Object will be fetched separately
    ignore_key: list[str] = ["file", "user", "xattributes"]

    result = extract_raw_data(data, ignore_key, "event_actor")

    for key, func in (
        ("file", parse_file_sub_object),
        ("user", parse_user_sub_object),
        ("xattributes", parse_xattributes_sub_object),
    ):
        if values := data.get(key):
            result |= func(values, key)  # type: ignore[operator]
    return result


def parse_file_sub_object(data: dict[str, Any], obj_prefix: Optional[str]) -> dict:
    """
    Retrieve event file object data
    Args:
        data (dict): Event monitor data
        obj_prefix (optional) : added object prefix
    Returns:
        return to event dict
    """
    prefix = f"{obj_prefix}_file" if obj_prefix else "file"
    return extract_raw_data(data, ["signature_value_ids"], prefix)


def parse_monitor_source_sub_object(data: dict[str, Any]) -> dict:
    """
    Retrieve event monitor object data
    Args:
        data (dict): Event monitor data
    Returns:
        return to event dict
    """
    return extract_raw_data(data, [], prefix="monitor_source")


def parse_connection_sub_object(data: dict[str, Any]) -> dict:
    """
    Retrieve event connection object data and return Event dict
    Args:
        data (dict): Event connection data
    Returns:
        return Data dict
    """
    return extract_raw_data(data, [], prefix="connection")


def convert_list_to_str(data: Optional[list] = None) -> str:
    """
    Convert list value to string with comma seperator
    Args:
        data (list): values
    Returns:
        return string
    """
    seperator = ","
    return seperator.join(map(str, data)) if isinstance(data, list) else ""


def parse_event_object_data(data: dict[str, Any]) -> dict:
    """
    Retrieve event object data and return Event dict
    Args:
        data (dict): Event Object data
    Returns:
        event_dict: Event Json Data
    """
    if not data:
        # Return empty dictionary
        return {}

    # Ignore to retrieve Sub Object which will be fetched subsequently based on the command requirement
    ignore_list = [
        "attacks",
        "av",
        "bash",
        "connection",
        "data",
        "directory",
        "enriched_data",
        "entity",
        "entity_result",
        "event_actor",
        "file",
        "intrusion",
        "kernel",
        "link_following",
        "receivers",
        "process",
        "reg_key",
        "reg_value",
        "sandbox",
        "scan",
        "sender",
        "service",
        "session",
        "monitor_source",
    ]

    result: dict[str, Any] = extract_raw_data(data, ignore_list)

    for key, func in (
        ("attacks", parse_attacks_sub_object),
        ("data", parse_event_data_sub_object),
        ("enriched_data", parse_enriched_data_sub_object),
        ("event_actor", parse_event_actor_sub_object),
        ("monitor_source", parse_monitor_source_sub_object),
        ("process", parse_process_sub_object),
        ("connection", parse_connection_sub_object),
        ("edr_data_protocols", convert_list_to_str),
    ):
        if values := data.get(key):
            result |= func(values)  # type: ignore[operator, arg-type]

    for item in ("edr_data_protocols", "edr_files", "source_port", "target_port"):
        if values := data.get(item):
            result |= {f"{item}": values}

    return result


def domain_instance_readable_output(results: list[dict], title: str) -> tuple[str, list]:
    """
    Convert to XSOAR Readable output for entity Domains instance
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
    Returns:
        A string representation of the Markdown table
    """
    summary_data = []
    for data in results:
        disposition_val = data.get("disposition", "")
        domain_instance = {
            "data_source_url_domain": data.get("data_source_url_domain", ""),
            "first_seen": data.get("first_seen", ""),
            "last_seen": data.get("last_seen", ""),
            "external_ip": data.get("external_ip", ""),
            "disposition": DOMAIN_DISPOSITION_STATUS.get(str(disposition_val), ""),
            "data_source_url": data.get("data_source_url", ""),
        }
        summary_data.append(domain_instance)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data, "_"), headers=headers, removeNull=True)
    return markdown, summary_data


def system_activity_readable_output(results: list[dict], title: str) -> tuple[str, list]:
    """
    Convert to User-Readable output for System Activity resources
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
    Returns:
        Human Readable table
        Content output
    """
    # Applicable events : 1, 20, 21, 1000
    summary_data = []
    context_data = []

    for data in results:
        event_data = parse_event_object_data(data)
        event_data["severity_id"] = EVENT_SEVERITY.get(str(event_data.get("severity_id")))
        event_data["atp_node_role"] = EVENT_NODE_ROLE.get(str(event_data.get("atp_node_role")))
        event_data["status_id"] = EVENT_STATUS.get(str(event_data.get("status_id")))
        # Symantec EDR Console logging System Activity
        system_activity = {
            "time": event_data.get("device_time", ""),
            "type_id": event_data.get("type_id", ""),
            "severity_id": event_data.get("severity_id", ""),
            "message": event_data.get("message", ""),
            "device_ip": event_data.get("device_ip", ""),
            "atp_node_role": event_data.get("atp_node_role", ""),
            "status_id": event_data.get("status_id", ""),
        }
        summary_data.append(system_activity)
        context_data.append(event_data)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data, "_"), headers=headers, removeNull=True)
    return markdown, context_data


def endpoint_instance_readable_output(results: list[dict], title: str) -> tuple[str, list]:
    """
    Convert to XSOAR Readable output for entities endpoints instance
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
    Returns:
        Human Readable table
        Content output
    """

    summary_data = []
    for data in results:
        ip_addresses = data.get("ip_addresses", [])
        endpoint_instance = {
            "device_uid": data.get("device_uid", ""),
            "device_name": data.get("device_name", ""),
            "device_ip": data.get("device_ip", ""),
            "domain_or_workgroup": data.get("domain_or_workgroup", ""),
            "time": data.get("time", ""),
            "ip_addresses": ip_addresses,
        }
        summary_data.append(endpoint_instance)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data, "_"), headers=headers, removeNull=True)
    return markdown, summary_data


def incident_readable_output(results: list[dict], title: str) -> tuple[str, list]:
    """
    Convert to User-Readable output for Incident resources
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
    Returns:
        markdown: A string representation of the Markdown table
        summary_data: Formatting response data
    """
    summary_data: list[dict[str, Any]] = []
    for data in results:
        priority = data.get("priority_level", "")
        state = data.get("state", "")
        resolution = data.get("resolution", "")
        incident = {
            "incident_id": data.get("atp_incident_id", ""),
            "description": data.get("summary", ""),
            "incident_created": data.get("device_time", ""),
            "detection_type": data.get("detection_type", ""),
            "last_updated": data.get("updated", ""),
            "priority": INCIDENT_PRIORITY_LEVEL.get(str(priority), ""),
            "incident_state": INCIDENT_STATUS.get(str(state), ""),
            "atp_rule_id": data.get("atp_rule_id"),
            "rule_name": data.get("rule_name"),
            "incident_uuid": data.get("uuid"),
            "log_name": data.get("log_name"),
            "recommended_action": data.get("recommended_action"),
            "resolution": INCIDENT_RESOLUTION.get(str(resolution), ""),
            "first_seen": data.get("first_event_seen"),
            "last_seen": data.get("last_event_seen"),
        }
        summary_data.append(incident)
    summary_data_sorted = sorted(summary_data, key=lambda d: d["incident_id"], reverse=True)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data_sorted, "_"), headers=headers, removeNull=True)
    return markdown, summary_data


def audit_event_readable_output(results: list[dict], title: str) -> tuple[str, list]:
    """
    Convert to User-Readable output for Audit Event
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
    Returns:
        markdown: A string representation of the Markdown table
        summary_data: Formatting response data
    """
    context_data: list[dict[str, Any]] = []
    summary_data: list[dict[str, Any]] = []
    for data in results:
        event_dict = parse_event_object_data(data)
        event_dict["severity_id"] = EVENT_SEVERITY.get(str(event_dict.get("severity_id")))
        event_dict["status_id"] = EVENT_STATUS.get(str(event_dict.get("status_id")))
        # ---- Display Data ----
        event = {
            "time": event_dict.get("device_time", ""),
            "type_id": event_dict.get("type_id", ""),
            "feature_name": event_dict.get("feature_name", ""),
            "message": event_dict.get("message", ""),
            "user_agent_ip": event_dict.get("user_agent_ip", ""),
            "user_name": event_dict.get("user_name", ""),
            "severity": event_dict.get("severity_id", ""),
            "device_name": event_dict.get("device_name", ""),
            "device_ip": event_dict.get("device_ip", ""),
            "uuid": event_dict.get("uuid", ""),
            "status_id": event_dict.get("status_id", ""),
        }
        summary_data.append(event)
        context_data.append(event_dict)

    summary_data_sorted = sorted(summary_data, key=lambda d: d["time"], reverse=True)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data_sorted, "_"), headers=headers, removeNull=True)
    return markdown, context_data


def incident_event_readable_output(results: list[dict], title: str) -> tuple[str, list]:
    """
    Convert to User-Readable output for Event for Incident resources
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
    Returns:
        A string representation of the Markdown table and context Data
        summary_data: Formatting response data
    """
    context_data: list[dict[str, Any]] = []
    summary_data: list[dict[str, Any]] = []
    for data in results:
        event_dict = parse_event_object_data(data)
        severity_id = event_dict.get("severity_id", "")
        event_dict["severity_id"] = EVENT_SEVERITY.get(str(severity_id), "")
        # ---- Display Data ----
        incident_for_event = {
            "time": event_dict.get("device_time", ""),
            "type_id": event_dict.get("type_id", ""),
            "description": f'{event_dict.get("event_actor_file_name", "")} '
            f'logged: {event_dict.get("enriched_data_rule_description", "")}',
            "device_name": event_dict.get("device_name", ""),
            "severity_id": event_dict.get("severity_id"),
            "device_ip": event_dict.get("device_ip", ""),
            "event_uuid": event_dict.get("event_uuid", ""),
            "incident": event_dict.get("incident", ""),
            "operation": event_dict.get("operation", ""),
            "device_domain": event_dict.get("device_domain", ""),
            "user_name": event_dict.get("user_name", ""),
        }
        summary_data.append(incident_for_event)
        context_data.append(event_dict)

    summary_data_sorted = sorted(summary_data, key=lambda d: d["time"], reverse=True)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data_sorted, "_"), headers=headers, removeNull=True)
    return markdown, context_data


def incident_comment_readable_output(results: list[dict], title: str, incident_id: str) -> tuple[str, list]:
    """
    Convert to XSOAR Readable output for incident comment
    Args:
        results (list): Symantec Association Results data
        title (str): Title string
        incident_id (str): Incident Id
    Returns:
        markdown: A string representation of the Markdown table
        summary_data: Formatted data set
    """

    summary_data: list[dict[str, Any]] = []
    for data in results:
        incident_comment = {
            "incident_id": incident_id,
            "comment": data.get("comment", ""),
            "time": data.get("time", ""),
            "user_id": data.get("user_id", ""),
            "incident_responder_name": data.get("incident_responder_name", ""),
        }
        summary_data.append(incident_comment)

    headers = extract_headers_for_readable_output(summary_data)
    markdown = tableToMarkdown(title, camelize(summary_data, "_"), headers=headers, removeNull=True)
    return markdown, summary_data


def generic_readable_output(results: list[dict], title: str) -> str:
    """
    Generic Readable output data for markdown
    Args:
        results (list): Generic Endpoint Response results data
        title (str): Title string
    Returns:
        A string representation of the Markdown table
    """
    readable_output = []
    for data in results:
        row = extract_raw_data(data, [])
        readable_output.append(row)

    headers = extract_headers_for_readable_output(readable_output)
    return tableToMarkdown(title, camelize(readable_output, "_"), headers=headers, removeNull=True)


def extract_raw_data(result: list | dict, ignore_key: list, prefix: str = None) -> dict:
    """
    Retrieve response result data
    Args:
        result (dict or list): Data ``dict`` or ``list``
        ignore_key (List): Ignore Key List
        prefix (str): Optional Added prefix in field name
    Returns:
        Return dict according to table field name and value
    """
    dataset: dict = {}
    if not isinstance(result, dict | list):
        raise ValueError(f"Unexpected data type {type(result)}:: must be either a list or dict.\ndata={result}")

    raw_data = {k: v for attribute in result for (k, v) in attribute.items()} if isinstance(result, list) else result

    for key, value in raw_data.items():
        if key not in ignore_key:
            field_name = f"{prefix}_{key}" if prefix else f"{key}"
            dataset[field_name] = value

    return dataset


def query_search_condition(q_type: str, q_value: str, ignore_validation: bool = False) -> str:
    """
    This function makes a query condition based on single or multiple search values .
    Args:
        q_type (str): search query Type
        q_value (str): search query value
        ignore_validation (bool): A boolean which ignores value Validation, Default false
    Returns:
        Return search condition.
    """
    condition: str = ""
    if not q_type or not q_value:
        return condition

    list_value = argToList(q_value, ",")
    for value in list_value:
        if not ignore_validation:
            check_valid_indicator_value(q_type, value)
        condition = f"{condition} OR {value}" if condition else value

    return condition


def get_incident_filter_query(args: dict[str, Any]) -> str:
    """
    This function validate the incident filter search query and return the query condition
    Args:
        args: demisto.args()
    Returns:
        Return string.
    """
    # Incident Parameters
    ids = arg_to_number(args.get("incident_id", None))
    priority = REVERSE_INCIDENT_PRIORITY.get(args.get("priority", None))
    status = REVERSE_INCIDENT_STATE.get(args.get("status", None))
    query = args.get("query", "")

    if query and (ids or priority or status):
        raise DemistoException(INVALID_QUERY_ERROR_MSG)

    condition: str = ""
    if ids is not None:
        condition = f"atp_incident_id: {ids}"

    if priority is not None:
        condition = f"{condition} AND priority_level: {priority} " if condition else f"priority_level: {priority}"

    if status is not None:
        condition = f"{condition} AND state: {status}" if condition else f"state: {status}"

    if query:
        condition = query

    return condition


def get_event_filter_query(args: dict[str, Any]) -> str:
    """
    This function creates the query for search condition.
    Args:
        args: demisto.args()
    Returns:
        Return string.
    """
    event_type_id = arg_to_number(args.get("type_id"))
    severity = REVERSE_EVENT_SEVERITY.get(args.get("severity", ""))
    status = REVERSE_EVENT_STATUS.get(args.get("status", ""))
    query = args.get("query")

    if query and (event_type_id or severity):
        raise DemistoException(INVALID_QUERY_ERROR_MSG)

    condition = ""
    if event_type_id:
        condition = f"type_id: {event_type_id}"

    if severity:
        condition = f"{condition} AND severity_id: {severity}" if condition else f"severity_id: {severity}"

    if status:
        condition = f"{condition} AND status_id: {status}" if condition else f"status_id: {status}"

    if query:
        condition = query

    return condition


def get_association_filter_query(args: dict) -> str:
    """
    This function validate the association filter search query and create the query search condition a
    payload based on the demisto.args().
    Args:
        args: demisto.args()
    Returns:
        Return string.
    """
    query_type = args.get("search_object", "")
    query_value = args.get("search_value", "")
    query = args.get("query", "")

    if query and (query_type or query_value):
        raise DemistoException(INVALID_QUERY_ERROR_MSG)

    if query_type == "sha256":
        condition = query_search_condition("sha256", query_value)
        return f"sha2: ({condition})"
    elif query_type == "device_uid":
        condition = query_search_condition("device_uid", query_value, ignore_validation=True)
        return f"device_uid: ({condition})"
    elif query_type == "domain":
        condition = query_search_condition("domain", query_value, ignore_validation=True)
        return f"data_source_url_domain: ({condition})"
    else:
        return query


def create_content_query(args: dict) -> dict[str, Any]:
    """
    This function creates content body based on the demisto.args().
    Args:
        args: demisto.args()
    Returns:
        Return request body payload.
    """
    # Default payload
    limit, offset = get_query_limit(args)
    payload: dict[str, Any] = {"verb": "query", "limit": limit, "offset": offset}

    if (raw_start_time := args.get("start_time")) and (start_time := convert_to_iso8601(raw_start_time)):
        payload["start_time"] = start_time

    if (raw_end_time := args.get("end_time")) and (end_time := convert_to_iso8601(raw_end_time)):
        payload["end_time"] = end_time

    return payload


def pagination(page: int | None, page_size: int | None) -> tuple[int, int]:
    """
    Define pagination.
    Args:
        # page: The page number.
        # page_size: The number of requested results per page.
    Returns:
        limit (int): Records per page.
        offset (int): The number of records to be skipped.
    """
    if page is None:
        # Default OFFSET value is 0 Or Page = 0
        page = DEFAULT_OFFSET
    elif page <= 0:
        raise DemistoException(PAGE_NUMBER_ERROR_MSG)

    if page_size is None:
        page_size = DEFAULT_PAGE_SIZE
    elif page_size <= 0:
        raise DemistoException(PAGE_SIZE_ERROR_MSG)

    limit = (page * page_size) if page > 0 else page_size
    offset = (page - 1) * page_size if page > 0 else page

    return limit, offset


def get_query_limit(args: dict) -> tuple[int, int]:
    """
    This function determines the query limit based on the demisto.args().

    Scenarios:
        If page/page_size is input, then the limit should be ignored.
        If only page or page_size were input,
        then the default for the other that is missing will be added in the code.
        Limit can work by itself independently, without page and page_size

    Args:
        args: demisto.args()
    Returns:
        limit (int)
        offset (int)
    """
    # Set default value to page, page_limit and page_size
    page = arg_to_number(args.get("page"), arg_name="page")
    page_size = arg_to_number(args.get("page_size"), arg_name="page_size")

    if page or page_size:
        page_limit, offset = pagination(page, page_size)
        return page_limit, offset

    limit: int = args.get("limit", DEFAULT_PAGE_SIZE)
    return limit, DEFAULT_OFFSET


def create_params_query(args: dict) -> dict:
    """
    This function creates a query param based on the demisto.args().
    Args:
        args: demisto.args()
    Returns:
        Return arguments dict.
    """
    limit, offset = get_query_limit(args)
    query_params: dict = {"limit": limit, "offset": offset}

    if denylist_id := args.get("denylist_id"):
        query_params["id"] = arg_to_number(denylist_id)

    if allowlist_id := args.get("allowlist_id"):
        query_params["id"] = arg_to_number(allowlist_id)

    if ip := args.get("ip"):
        check_valid_indicator_value("ip", ip)
        query_params["ip"] = ip

    if url := args.get("url"):
        query_params["url"] = url

    if domain := args.get("domain"):
        query_params["domain"] = domain

    if md5 := args.get("md5"):
        check_valid_indicator_value("md5", md5)
        query_params["md5"] = md5

    if sha256 := args.get("sha256"):
        check_valid_indicator_value("sha256", sha256)
        query_params["sha256"] = sha256

    return query_params


def check_valid_indicator_value(indicator_type: str, indicator_value: str) -> bool:
    """
    Check the validity of indicator values
    Args:
        indicator_type: Indicator type provided in the command
            Possible Indicator type are: sha256, urls, ip, md5
        indicator_value: Indicator value provided in the command
    Returns:
        True if the provided indicator values are valid
    """
    hash_to_regex: dict[str, Any] = {
        "sha256": sha256Regex,
        "urls": urlRegex,
        "md5": md5Regex,
    }

    if indicator_type == "ip":
        if not is_ip_valid(indicator_value):
            raise ValueError(f"{indicator_value} is not a valid IP")
    else:
        if indicator_type not in hash_to_regex:
            raise ValueError(f"Indicator type {indicator_type} is not supported")

        if not re.match(hash_to_regex[indicator_type], indicator_value):
            raise ValueError(f"{indicator_value} is not a valid {indicator_type}")

    return True


def get_incident_uuid(client: Client, args: dict[str, Any]) -> str | None:
    """
    Get the incident UUID
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
      Return Incident UUID
    """
    payload = create_content_query(args)

    # offset does not support by API, therefore, need to be removed
    payload.pop("offset")

    # search query as Lucene query string
    if search_query := get_incident_filter_query(args):
        payload["query"] = search_query

    result = client.get_incident(payload).get("result")
    return result[0].get("uuid") if result else None


def create_payload_for_query(args: dict[str, Any], query_type: Optional[str] = None) -> dict:
    """
    Create payload for request the endpoints
    Args:
        args: all command arguments, usually passed from ``demisto.args()``.
        query_type: query type : association, event, incident, allow_list, deny_list
    Returns:
        payload (dict): Return payload for request body
    """
    if query_type in ("allow_list", "deny_list"):
        limit = arg_to_number(args.get("limit"))
        page_size = arg_to_number(args.get("page_size"))
        if (limit and limit < 10) or (page_size and page_size < 10):
            raise ValueError(
                "Invalid input limit or page_size. "
                "For the Deny and Allow list specify the limit/page_size range "
                "The value must be >= 10 and <= 1000."
            )
        payload = create_params_query(args)
    else:
        payload = create_content_query(args)

    # search query as Lucene query string
    if query_type == "association":
        search_query = get_association_filter_query(args)
    elif query_type == "event":
        search_query = get_event_filter_query(args)
    elif query_type == "incident":
        search_query = get_incident_filter_query(args)
    else:
        # default
        search_query = args.get("query", "")

    if search_query:
        payload["query"] = search_query

    return payload


def validate_command_argument(args: dict[str, Any], cmd_type: str, expected_values: list) -> None:
    """
    Validate command arguments based on user input value and expected value.

    Args:
        - args (dict): Usually passed from ``demisto.args()``.
        - cmd_type (str): Command argument type.
        - expected_values (list): An acceptable list of value
    Raises:
     ValueError: Raise error if invalid argument is found.
    """
    arg_value = args.get(cmd_type)
    if arg_value and arg_value not in expected_values:
        raise ValueError(f"Invalid {cmd_type}! Only supported types are : {expected_values}")


""" COMMAND FUNCTIONS """


def common_wrapper_command(
    client_func: Callable,
    cmd_args: dict,
    readable_title: str,
    context_path: str,
    output_key_field: str,
    command_type: str = None,
    func_readable_output: Callable = None,
    **kwargs,
) -> CommandResults:
    """
    Common Wrapper Command for different endpoints
    Args:
        client_func: Call client method e.g. client.list_domain_file
        cmd_args: Command arguments, usually passed from ``demisto.args()``.
        readable_title: Readable Output title
        context_path: Readable Context Output path
        output_key_field: Outputs key field
        command_type: Load the specific payload
        func_readable_output: Optional, call in case of readable output method is different for specific command
        kwargs: In case required other arguments

    Returns:
        CommandResults: A ``CommandResults`` object
    """
    context_data: list = []
    payload = create_payload_for_query(cmd_args, command_type)
    offset = int(payload.pop("offset", ""))
    limit = int(payload.get("limit", ""))

    if "uuid" in kwargs:
        raw_response = client_func(payload, kwargs["uuid"])
    elif "sha2" in kwargs:
        raw_response = client_func(payload, kwargs["sha2"])
    else:
        raw_response = client_func(payload)

    title = compile_command_title_string(readable_title, cmd_args, int(raw_response.get("total", 0)))

    if printable_result := get_data_of_current_page(raw_response.get("result", []), offset, limit):
        if func_readable_output is None:
            readable_output = generic_readable_output(printable_result, title)
            context_data = printable_result
        elif "incident_id" in kwargs:
            readable_output, context_data = func_readable_output(printable_result, title, kwargs["incident_id"])
        else:
            readable_output, context_data = func_readable_output(printable_result, title)
    else:
        readable_output = f"No {readable_title} data to present."

    return CommandResults(
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.{context_path}",
        outputs_key_field=output_key_field,
        readable_output=readable_output,
        outputs=context_data,
        raw_response=raw_response,
        ignore_auto_extract=True,
    )


def get_domain_file_association_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    List of Domain and File association
    Args:
        client: Symantec EDR on-premise client object.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object
    """
    validate_command_argument(args, "search_object", ["sha256", "domain"])
    return common_wrapper_command(
        client_func=client.list_domain_file,
        cmd_args=args,
        readable_title="Domain File Association",
        context_path="DomainFileAssociation",
        output_key_field="sha2",
        command_type="association",
    )


def get_endpoint_domain_association_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    endpoint_domain_association_command: List of endpoint domain association
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    validate_command_argument(args, "search_object", ["device_uid", "domain"])
    return common_wrapper_command(
        client_func=client.list_endpoint_domain,
        cmd_args=args,
        readable_title="Endpoint Domain Association",
        context_path="EndpointDomainAssociation",
        output_key_field="device_uid",
        command_type="association",
    )


def get_endpoint_file_association_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    endpoint_file_association_command: List of Endpoint File association
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    validate_command_argument(args, "search_object", ["device_uid", "sha256"])

    return common_wrapper_command(
        client_func=client.list_endpoint_file,
        cmd_args=args,
        readable_title="Endpoint File Association",
        context_path="EndpointFileAssociation",
        output_key_field="sha2",
        command_type="association",
    )


def get_audit_event_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Audit Event
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    return common_wrapper_command(
        client_func=client.get_audit_event,
        cmd_args=args,
        readable_title="Audit Event",
        context_path="AuditEvent",
        output_key_field="event_uuid",
        command_type="event",
        is_call_diff_readable_output=True,
        func_readable_output=audit_event_readable_output,
    )


def get_event_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get all events
    Args:
        client: Symantec EDR on-premise client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    validate_command_argument(args, "severity", list(REVERSE_EVENT_SEVERITY.keys()))
    return common_wrapper_command(
        client_func=client.get_event_list,
        cmd_args=args,
        readable_title="Event",
        context_path="Event",
        output_key_field="event_uuid",
        command_type="event",
        is_call_diff_readable_output=True,
        func_readable_output=incident_event_readable_output,
    )


def get_system_activity_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get System Activity log events
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    validate_command_argument(args, "severity", list(REVERSE_EVENT_SEVERITY.keys()))

    return common_wrapper_command(
        client_func=client.get_system_activity,
        cmd_args=args,
        readable_title="System Activity",
        context_path="SystemActivity",
        output_key_field="uuid",
        command_type="event",
        is_call_diff_readable_output=True,
        func_readable_output=system_activity_readable_output,
    )


def get_event_for_incident_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Event for Incident List
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    validate_command_argument(args, "severity", list(REVERSE_EVENT_SEVERITY.keys()))
    return common_wrapper_command(
        client_func=client.get_event_for_incident,
        cmd_args=args,
        readable_title="Event for Incident",
        context_path="IncidentEvent",
        output_key_field="event_uuid",
        command_type="event",
        is_call_diff_readable_output=True,
        func_readable_output=incident_event_readable_output,
    )


def get_incident_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Incident List
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    validate_command_argument(args, "priority", list(REVERSE_INCIDENT_PRIORITY.keys()))
    validate_command_argument(args, "status", list(REVERSE_INCIDENT_STATE.keys()))
    return common_wrapper_command(
        client_func=client.get_incident,
        cmd_args=args,
        readable_title="Incident",
        context_path="Incident",
        output_key_field="apt_incident_id",
        command_type="incident",
        is_call_diff_readable_output=True,
        func_readable_output=incident_readable_output,
    )


def get_incident_comments_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get all comments based on Incident ID
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    # Get UUID based on incident_id
    uuid = get_incident_uuid(client, args)
    incident_id = args.pop("incident_id", None)
    if uuid is None:
        raise ValueError(
            f"Incident ID {incident_id} was not found. If it's older than 30 days, try increasing the time range arguments"
        )

    return common_wrapper_command(
        client_func=client.get_incident_comment,
        cmd_args=args,
        readable_title="Domain Instances",
        context_path="DomainInstances",
        output_key_field="data_source_url_domain",
        command_type="incident",
        is_call_diff_readable_output=True,
        func_readable_output=incident_comment_readable_output,
        uuid=uuid,
        incident_id=incident_id,
    )


def patch_incident_update_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Incident Update command is used to Add, close or update incident resolution
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an updated
            result.
    """
    action = args.get("operation")
    update_value = args.get("value", "")

    if action not in INCIDENT_PATCH_ACTION:
        raise ValueError(f"Invalid Incident Patch Operation: Supported values are : {INCIDENT_PATCH_ACTION}")

    # Get UUID based on incident_id
    if not (uuid := get_incident_uuid(client, args)):
        raise ValueError(
            f'Incident ID {args.get("incident_id")} was not found. '
            f"If it's older than 30 days, try increasing the time range arguments"
        )
    # Incident Add Comment
    if action == "add_comment":
        if not update_value:
            raise ValueError("Comment is missing.")

        action_desc = "Add Comment"
        response = client.add_incident_comment(uuid, update_value)
        status = response.status_code

        # Incident Close Incident
    elif action == "close_incident":
        action_desc = "Close Incident"
        response = client.close_incident(uuid, 4)
        status = response.status_code

        # Incident Update Resolution
    elif action == "update_resolution":
        action_desc = "Update Status"
        if not update_value or INCIDENT_RESOLUTION.get(str(update_value)) is None:
            raise ValueError("Incident Resolution value is missing or invalid.")
        response = client.update_incident(uuid, int(args.get("value", 0)))
        status = response.status_code
    else:
        raise DemistoException(f"Operation {action} is not supported; it must be one of {INCIDENT_PATCH_ACTION}")

    if status != 204:
        raise DemistoException(f"Failure of incident {action} operation")

    summary_data = {
        "incident_id": args.get("incident_id"),
        "Message": "Finished updating",
    }
    headers = list(summary_data.keys())
    readable_output = tableToMarkdown(f"Incident {action_desc}", summary_data, headers=headers, removeNull=True)

    return CommandResults(readable_output=readable_output, ignore_auto_extract=True)


def get_file_instance_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get File Instance
    Args:
        client: Symantec EDR on-premise client objectd to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    if sha2 := args.get("file_sha2"):
        check_valid_indicator_value("sha256", sha2)

    return common_wrapper_command(
        client_func=client.get_file_instance,
        cmd_args=args,
        readable_title="File Instances",
        context_path="FileInstance",
        output_key_field="sha2",
        sha2=sha2,
    )


def get_domain_instance_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Domain Instance
    Args:
        client: Symantec EDR on-premise client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    return common_wrapper_command(
        client_func=client.get_domain_instance,
        cmd_args=args,
        readable_title="Domain Instances",
        context_path="DomainInstances",
        output_key_field="data_source_url_domain",
        is_call_diff_readable_output=True,
        func_readable_output=domain_instance_readable_output,
    )


def get_endpoint_instance_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Endpoint Instance
    Args:
        client: Symantec EDR on-premise client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    return common_wrapper_command(
        client_func=client.get_endpoint_instance,
        cmd_args=args,
        readable_title="Endpoint Instances",
        context_path="EndpointInstances",
        output_key_field="device_uid",
        is_call_diff_readable_output=True,
        func_readable_output=endpoint_instance_readable_output,
    )


def get_allow_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Allow List Policies
     Args:
         client: Symantec EDR on-premise client object to use.
         args: all command arguments, usually passed from ``demisto.args()``.
     Returns:
         CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
             result.
    """
    return common_wrapper_command(
        client_func=client.get_allow_list,
        cmd_args=args,
        readable_title="Allow List Policy",
        context_path="AllowListPolicy",
        output_key_field="id",
        command_type="allow_list",
    )


def get_deny_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get deny List Policies
    Args:
        client: Symantec EDR on-premise client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    return common_wrapper_command(
        client_func=client.get_deny_list,
        cmd_args=args,
        readable_title="Deny List Policy",
        context_path="DenyListPolicy",
        output_key_field="id",
        command_type="deny_list",
    )


def get_endpoint_command(client: Client, args: dict[str, Any], command: str) -> CommandResults:
    """
    Issue a Command Action to the SEDR On-Prem networks with the following action:
        isolate - Isolates endpoint by cutting connections that the endpoint(s) has to internal networks and external
                  networks, based on the endpoint IDs
        rejoin  - Rejoins endpoints by re-establishing connections that the endpoint(s) has to internal networks
                  and external networks, based on the endpoint IDs
        delete-file - Deletes a file, i.e. deletes all instances of the file, based on the file hash that you have
                        specified from the endpoint using the Device ID
        cancel command - When you cancel a command that is already in progress, you cancel the command execution on all
                        the endpoints where it is still in progress. Only one command can be cancelled at a time
                        to the infecting devices.
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
        command: Demisto.command

    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    device_uid = args.get("device_id", "")
    file_sha2 = args.get("sha2", "")
    command_id = args.get("command_id", "")

    if command == "symantec-edr-endpoint-cancel-command":
        raw_response = client.get_cancel_endpoint(command_id)
        action_type = "Cancel Endpoint"
    elif command == "symantec-edr-endpoint-delete-file":
        if device_uid and file_sha2:
            raw_response = client.get_delete_endpoint(device_uid, file_sha2)
            action_type = "Delete Endpoint"
        else:
            raise DemistoException(
                'Invalid Arguments. Both "device_id" and "sha2" arguments are required for endpoint delete action'
            )
    elif command == "symantec-edr-endpoint-isolate":
        action_type = "Isolate Endpoint"
        raw_response = client.get_isolate_endpoint(device_uid)
    elif command == "symantec-edr-endpoint-rejoin":
        action_type = "Rejoin Endpoint"
        raw_response = client.get_rejoin_endpoint(device_uid)
    else:
        raise DemistoException("Endpoint Command action not found.")

    title = f"Command {action_type}"

    summary_data = {
        "Message": raw_response.get("message"),
        "CommandId": raw_response.get("command_id"),
    }

    headers = list(summary_data.keys())
    return CommandResults(
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Command.{action_type}",
        outputs_key_field="command_id",
        outputs=raw_response,
        readable_output=tableToMarkdown(title, summary_data, headers=headers, removeNull=True),
        raw_response=raw_response,
        ignore_auto_extract=True,
    )


def get_endpoint_status_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Check the command status of isolate endpoint.
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    command_id = args.get("command_id", "")
    readable_data = []
    payload = create_content_query(args)
    payload.pop("offset", 0)

    raw_response = client.get_status_endpoint(command_id, payload)

    summary_data = {
        "state": raw_response.get("state"),
        "Command Issuer Name": raw_response.get("command_issuer_name"),
    }

    if result := raw_response.get("status", ()):
        for status in result:
            summary_data["state"] = status.get("state", "")
            summary_data["message"] = status.get("message", "")
            summary_data["error_code"] = status.get("error_code", "")

    if summary_data:
        title = "Command Status"
        readable_data.append(summary_data)
        readable_output = generic_readable_output(readable_data, title)
    else:
        readable_output = "No command status data to present."

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.CommandStatus",
        outputs_key_field="",
        outputs=summary_data,
        raw_response=raw_response,
        ignore_auto_extract=True,
    )


""" FETCHES INCIDENTS """


def fetch_incidents(client: Client, query_start_time: str) -> list:
    """
    Fetching Incidents pulls incidents and events from third party tools and convert then into incidents.
    Args:
        client: Client Object
        query_start_time: Start Time
    Returns:
        Incident Tuple
    """
    seperator = " OR "
    priority_list = [REVERSE_INCIDENT_PRIORITY.get(i) for i in client.fetch_priority]  # type: ignore[union-attr]
    priority = priority_list[0] if len(priority_list) == 1 else seperator.join(map(str, priority_list))

    state_list = [REVERSE_INCIDENT_STATE.get(i) for i in client.fetch_status]  # type: ignore[union-attr]
    state = state_list[0] if len(state_list) == 1 else seperator.join(map(str, state_list))

    fetch_query = client.fetch_query or f"priority_level: ({priority}) AND state: ({state})"
    incident_payload = {
        "verb": "query",
        "limit": client.fetch_limit,
        "query": fetch_query,
        "start_time": query_start_time,
    }
    demisto.debug(f"Incident query with {incident_payload}")
    result = client.get_incident(incident_payload).get("result", [])

    incidents, events_result, comments_result = [], [], []
    if result:
        _, incidents_context = incident_readable_output(result, "Incident")

        for incident in incidents_context:
            incident_id = incident.get("incident_id")
            incident_uuid = incident.get("incident_uuid")

            # Get Incident Comments if set as true
            if client.is_fetch_comment:
                comment_payload = {"verb": "query", "start_time": query_start_time}
                comments_result = client.get_incident_comment(comment_payload, incident_uuid).get("result", [])

            # Fetch incident for event if set as true
            if client.is_incident_event:
                event_payload = {
                    "verb": "query",
                    "query": f"incident: {incident_uuid}",
                    "start_time": query_start_time,
                }
                events_result = client.get_event_for_incident(event_payload).get("result", [])

            # Incidents Data
            incidents.append(
                {
                    "name": f"SEDR Incident {incident_id}",
                    "details": incident.get("description"),
                    "severity": XSOAR_SEVERITY_MAP.get(str(incident["priority"])),
                    "occurred": incident.get("incident_created"),
                    "dbotMirrorId": str(incident_id),
                    "rawJSON": json.dumps(
                        {
                            "incident": incident,
                            "comments": comments_result,
                            "events": events_result,
                        }
                    ),
                }
            )

    return incidents


def fetch_events(client: Client, query_start_time: str) -> list:
    """
    Fetching Events pulls events from third party tools and convert then into incidents.
    Args:
        client: Client Object
        query_start_time: Start Time
    Returns:
        Incident list
    """
    seperator = "OR"

    severity_list = [REVERSE_EVENT_SEVERITY.get(i.lower()) for i in client.fetch_event_severity]  # type: ignore[union-attr]
    severity = severity_list[0] if len(severity_list) == 1 else seperator.join(map(str, severity_list))
    status_list = [REVERSE_EVENT_STATUS.get(i) for i in client.fetch_event_status]  # type: ignore[union-attr]
    status = status_list[0] if len(status_list) == 1 else seperator.join(map(str, status_list))
    fetch_query = client.fetch_query or f"severity_id: ({severity}) and status_id: ({status})"
    event_payload = {
        "verb": "query",
        "limit": client.fetch_limit,
        "query": fetch_query,
        "start_time": query_start_time,
    }
    demisto.debug(f"Event query with {event_payload}")
    results = client.get_event_list(event_payload).get("result", [])
    demisto.debug(f"Fetched {len(results)}")
    incidents = []
    for result in results:
        event_type = EVENT_TYPE.get(str(result.get("type_id")))
        result["event_type"] = event_type
        incidents.append(
            {
                "name": f'SEDR Event {result.get("type_id")}: {event_type} - {result.get("device_name")}',
                "severity": EVENT_SEVERITY_MAPPING.get(str(result.get("severity_id"))),
                "rawJSON": json.dumps(result),
                "occurred": result.get("device_time"),
                "uuid": result.get("uuid"),
            }
        )
    return incidents


def fetch_xsaor_incidents(client: Client, fetch_incident_type: str) -> list:
    """
    Common function for fetch incidents and events.
    Args:
        client: Client Object
        fetch_incident_type: Fetch Type
    Returns:
        Incident list
    """
    function_mapping = {
        "incidents": (fetch_incidents, "name"),
        "events": (fetch_events, "uuid"),
    }
    # demisto.getLastRun() will return an obj with the previous run in it.
    last_run = demisto.getLastRun()
    demisto.debug(f"Last Run Object : {last_run}")
    # set First Fetch starting time in case running first time or reset
    previous_start_time, previous_end_time = get_fetch_run_time_range(last_run=last_run, first_fetch=client.first_fetch)

    query_start_time = (
        convert_to_iso8601(last_run.get("time")) if last_run and "time" in last_run else convert_to_iso8601(previous_start_time)
    )
    fetch_function, id_field = function_mapping[fetch_incident_type]
    incidents = fetch_function(client, query_start_time)
    # remove duplicate incidents which were already fetched
    incidents_insert = filter_incidents_by_duplicates_and_limit(
        incidents_res=incidents,
        last_run=last_run,
        fetch_limit=client.fetch_limit,
        id_field=id_field,
    )
    current_end_time = convert_to_iso8601("now")
    last_run = update_last_run_object(
        last_run=last_run,
        incidents=incidents_insert,
        fetch_limit=client.fetch_limit,
        start_fetch_time=query_start_time,
        end_fetch_time=current_end_time,
        look_back=30,
        created_time_field="occurred",
        id_field=id_field,
        date_format=f"{ISO8601_F_FORMAT}Z",
    )

    demisto.debug(f"Incident insert: {len(incidents_insert)}")
    demisto.setLastRun(last_run)
    demisto.debug(f"length of incident {len(incidents_insert)}")
    return incidents_insert


""" POLLING CODE """


def get_sandbox_verdict(client: Client, args: Dict[str, Any]) -> CommandResults:
    """
    Get file Sandbox Verdict of specific SHA2
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    sha2 = args.get("file", "")
    reliability = args.get("integration_reliability", "B - Usually reliable")
    response_verdict = client.get_sandbox_verdict_for_file(sha2) | client.get_file_entity(sha2)
    # Sandbox verdict
    title = "Sandbox Verdict"
    indicator = None
    if response_verdict:
        readable_output = generic_readable_output([response_verdict], title)
        score = VERDICT_TO_SCORE_DICT.get(response_verdict.get("verdict", "").lower(), Common.DBotScore.NONE)
        dbot_score = Common.DBotScore(
            indicator=sha2,
            indicator_type=DBotScoreType.FILE,
            integration_name=INTEGRATION_CONTEXT_NAME,
            score=score,
            malicious_description=response_verdict.get("verdict", ""),
            reliability=DBotScoreReliability.get_dbot_score_reliability_from_str(reliability),
        )
        indicator = Common.File(sha256=sha2, dbot_score=dbot_score)
    else:
        readable_output = f"{title} does not have data to present."

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.SandboxVerdict",
        outputs_key_field="sha2",
        outputs=response_verdict,
        raw_response=response_verdict,
        indicator=indicator,
    )


def check_sandbox_status(client: Client, args: Dict[str, Any]) -> CommandResults:
    """
    Query file Sandbox command status,
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """
    readable_data = []
    title = "File Sandbox Status"
    if not (command_id := args.get("command_id")):
        raise DemistoException("Command ID missing.")

    response = client.get_sandbox_status(command_id)
    # Query Sandbox Command Status
    summary_data = {}
    if sandbox_status := response.get("status"):
        for status in sandbox_status:
            summary_data = {
                "command_id": command_id,
                "status": SANDBOX_STATE.get(str(status.get("state", ""))),
                "message": status.get("message", ""),
                "target": status.get("target", ""),
                "error_code": status.get("error_code", ""),
            }

    if summary_data:
        readable_data.append(summary_data)
        readable_output = generic_readable_output(readable_data, title)
    else:
        readable_output = f"{title} does not have data to present."

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.SandboxStatus",
        outputs_key_field="command_id",
        outputs=summary_data,
        raw_response=response,
    )


def issue_sandbox_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """
    Issue File Sandbox command,
    Args:
        client: client object to use.
        args: all command arguments, usually passed from ``demisto.args()``.
    Returns:
        CommandResults: A ``CommandResults`` object that is then passed to ``return_results``, that contains an updated
            result.
    """

    sha2 = args.get("file", "")
    if get_hash_type(sha2) != "sha256":
        raise ValueError(f"SHA256 value:{sha2} is invalid")

    payload = {"action": "analyze", "targets": argToList(sha2)}
    response = client.submit_file_to_sandbox_analyze(payload)

    # Get Issue Sandbox Command
    title = "Issue Sandbox Command"
    summary_data = {
        "sha2": sha2,
        "command_id": response.get("command_id"),
        "command_type": "Issue Sandbox Command",
    }
    headers = list(summary_data.keys())
    column_order = [camelize_string(column) for column in headers]
    return CommandResults(
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.SandboxIssue",
        outputs_key_field="command_id",
        outputs=summary_data,
        readable_output=tableToMarkdown(title, camelize(summary_data, "_"), headers=column_order, removeNull=True),
        raw_response=response,
    )


# ScheduledCommand
def run_polling_command(client: Client, args: dict, cmd: str, status_func: Callable, results_func: Callable):
    """
    This function can handle the polling flow.
    After the first run, progress will be shown through the status command.
    The run_polling_command function checks the file scan status and will run until status is not 'Completed'.
    It returns a ScheduledCommand object that schedules the next 'results' function until the polling is complete.
    Args:
        client: Symantec EDR client object
        args: the arguments required to the command being called
        cmd: the command to schedule by after the current command
        status_func : The function that checks the file scan status and returns either completed or error status
        results_func: the function that retrieves the verdict based on file sandbox status

    Returns:
        return CommandResults
    """
    demisto.debug(f"-- Polling Command --\nArguments : {args}")
    ScheduledCommand.raise_error_if_not_supported()
    interval_in_secs: int = int(args.get("interval_in_seconds", DEFAULT_INTERVAL))
    timeout_in_seconds: int = int(args.get("timeout_in_seconds", DEFAULT_TIMEOUT))

    # Check for ongoing file scanning command_id if exist
    if pre_cmd_id := demisto.getIntegrationContext().get("command_id"):
        args["command_id"] = pre_cmd_id

    # first run ...
    if "command_id" not in args:
        outputs: Any[object] = issue_sandbox_command(client, args).outputs
        command_id = outputs.get("command_id")

        if command_id is not None:
            if global_integration_context := demisto.getIntegrationContext():
                global_integration_context["command_id"] = command_id
                demisto.setIntegrationContext(global_integration_context)
            else:
                demisto.setIntegrationContext({"command_id": command_id})

            args["command_id"] = command_id
            polling_args = {
                "interval_in_seconds": interval_in_secs,
                "polling": True,
                **args,
            }

            scheduled_command = ScheduledCommand(
                command=cmd,
                next_run_in_seconds=interval_in_secs,
                args=polling_args,
                timeout_in_seconds=timeout_in_seconds,
            )

            return CommandResults(
                scheduled_command=scheduled_command,
                readable_output=f"Waiting for the polling execution..Command id {command_id}",
                ignore_auto_extract=True,
            )

    # not a first run
    command_result = status_func(client, args)
    outputs = status_func(client, args).outputs
    status = outputs.get("status")
    if status == "Completed":
        # action completed
        if global_integration_context := demisto.getIntegrationContext():
            global_integration_context.pop("command_id")
            demisto.setIntegrationContext(global_integration_context)
        return results_func(client, args)
    elif status == "Error":
        if global_integration_context := demisto.getIntegrationContext():
            global_integration_context.pop("command_id")
            demisto.setIntegrationContext(global_integration_context)

        return command_result
    else:
        # in case of In progress
        polling_args = {
            "interval_in_seconds": interval_in_secs,
            "polling": True,
            **args,
        }
        scheduled_command = ScheduledCommand(
            command=cmd,
            next_run_in_seconds=interval_in_secs,
            args=polling_args,
            timeout_in_seconds=timeout_in_seconds,
        )

        # result with scheduled_command only - no update to the war room
        return CommandResults(scheduled_command=scheduled_command, ignore_auto_extract=True)


def file_scheduled_polling_command(client: Client, args: Dict[str, Any]):
    """
    File Scheduled Polling file command
    Returns:
        return polling CommandResults
    """
    return run_polling_command(client, args, "file", check_sandbox_status, get_sandbox_verdict)


""" MAIN FUNCTION """


def main() -> None:  # pragma: no cover
    """
    main function, parses params and runs command functions
    """
    try:
        params = demisto.params()
        args = demisto.args()
        command = demisto.command()

        # OAuth parameters
        server_url = params.get("url", "")
        client_id = params.get("credentials", {}).get("identifier", "")
        client_secret = params.get("credentials", {}).get("password", "")
        verify_certificate = params.get("insecure", False)
        proxy = params.get("proxy", False)

        # Fetches Incident Parameters
        fetch_incidents_type = params.get("fetch_incidents_events_type", "incidents")
        first_fetch_time = params.get("first_fetch", "3 days").strip()
        fetch_limit = arg_to_number(params.get("max_fetch", 50))
        fetch_incident_event = params.get("isIncidentsEvent", False)
        fetch_comments = params.get("isIncidentComment", False)
        fetch_status = argToList(params.get("fetch_status", "New"))
        fetch_priority = argToList(params.get("fetch_priority", "High,Medium"))
        fetch_event_status = argToList(params.get("fetch_event_status", "Success"))
        fetch_event_severity = argToList(params.get("fetch_severity", "Info"))
        fetch_query = params.get("fetch_incidents_query", "")
        reliability = params.get("integration_reliability", "")
        args["integration_reliability"] = reliability

        client = Client(
            base_url=server_url,
            verify=verify_certificate,
            proxy=proxy,
            client_id=client_id,
            client_secret=client_secret,
            fetch_incidents_type=fetch_incidents_type,
            first_fetch=first_fetch_time,
            fetch_limit=fetch_limit,
            is_incident_event=fetch_incident_event,
            is_fetch_comment=fetch_comments,
            fetch_status=fetch_status,
            fetch_priority=fetch_priority,
            fetch_event_status=fetch_event_status,
            fetch_event_severity=fetch_event_severity,
            fetch_query=fetch_query,
        )

        demisto.info(f"Command being called is {demisto.command()}")
        commands = {
            # Command Status
            "symantec-edr-endpoint-status": get_endpoint_status_command,
            # Domain File Associations
            "symantec-edr-domain-file-association-list": get_domain_file_association_list_command,
            # Endpoint Domain Associations
            "symantec-edr-endpoint-domain-association-list": get_endpoint_domain_association_list_command,
            # Endpoint File Associations
            "symantec-edr-endpoint-file-association-list": get_endpoint_file_association_list_command,
            # Get Incidents
            "symantec-edr-incident-list": get_incident_list_command,
            # Events For Incidents
            "symantec-edr-incident-event-list": get_event_for_incident_list_command,
            # Get Incident Comments
            "symantec-edr-incident-comment-get": get_incident_comments_command,
            # Patch Incidents Command to (Close Incidents, Update Resolution or Add Comments)
            "symantec-edr-incident-update": patch_incident_update_command,
            # System Activities
            "symantec-edr-system-activity-list": get_system_activity_command,
            # Audit Events
            "symantec-edr-audit-event-list": get_audit_event_command,
            # Allow List Policies
            "symantec-edr-allow-list-policy-get": get_allow_list_command,
            # Deny List Policies
            "symantec-edr-deny-list-policy-get": get_deny_list_command,
            # Domain Instances
            "symantec-edr-domain-instance-list": get_domain_instance_command,
            # Endpoint Instances
            "symantec-edr-endpoint-instance-list": get_endpoint_instance_command,
            # File Instances
            "symantec-edr-file-instance-list": get_file_instance_command,
            # Events
            "symantec-edr-event-list": get_event_list_command,
            # file Sandbox (Reputation command)
            "file": file_scheduled_polling_command,
        }
        command_output: CommandResults | str
        if command == "test-module":
            command_output = client.test_module()
        elif command == "fetch-incidents":
            incidents = fetch_xsaor_incidents(client, fetch_incidents_type)
            demisto.incidents(incidents)
            command_output = "OK"
        elif command in [
            "symantec-edr-endpoint-isolate",
            "symantec-edr-endpoint-rejoin",
            "symantec-edr-endpoint-delete-file",
            "symantec-edr-endpoint-cancel-command",
        ]:
            # isolate_endpoint, re-join, delete_endpoint_file, cancel_command
            command_output = get_endpoint_command(client, args, command)
        elif command in commands:
            command_output = commands[command](client, args)
        else:
            raise NotImplementedError(f"Command {command} is not supported")

        return_results(command_output)

    # Log exceptions and return errors
    except Exception as e:
        demisto.error(traceback.format_exc())
        return_error(f"Failed to execute {demisto.command()} command.\nError: {e}")


""" ENTRY POINT """

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