McAfee ESM-v10 Deprecated

Deprecated. Use the McAfee ESM v2 integration instead.

Analytics & SIEM · McAfee ESM v10 and v11 (Deprecated)

Details

IDMcAfee ESM-v10
ProviderTrellix
CategoryAnalytics & SIEM
From Version5.0.0
Docker Imagedemisto/python:2.7.18.20958
Supported ModulesAgentix

README

Deprecated. Use the McAfee ESM v2 integration instead.

Configure McAfee ESM-v10 on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for McAfee ESM-v10.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • Port
    • ESM IP (e.g. 78.125.0.209)
    • Username
    • Fetch incidents
    • Incident type
    • Fetch Types: cases, alarms, both (relevant only for fetch incident mode)
    • Start fetch after Case ID: (relevant only for fetch incident mode)
    • Trust any certificate (not secure)
    • Version: (one of 10.0, 10.1, 10.2, 10.3)
    • ESM time format, e.g., %Y/%m/%d %H:%M:%S. Select “auto-discovery” to extract the format automatically.
    • __McAfee ESM Timezone in hours (e.g if ESM timezone is +0300 => then insert 3) __
  4. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR 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.

1. Get list of all fields


Returns a list of all fields (and the field type) that can be used in query filters.

Base Command

esm-fetch-fields

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example
esm-fetch-fields
Human Readable

This output is truncated.

Result:

name types
AppID STRING
CommandID STRING
DomainID STRING
HostID STRING
ObjectID STRING
UserIDDst STRING
UserIDSrc STRING
URL SSTRING
Database_Name STRING
Message_Text SSTRING
Response_Time UINT32,UINT32
Application_Protocol STRING
Object_Type STRING
Filename SSTRING
From SSTRING
To SSTRING
Cc SSTRING
Bcc SSTRING
Subject SSTRING
Method STRING
User_Agent SSTRING
Cookie SSTRING
Referer SSTRING
File_Operation STRING
File_Operation_Succeeded STRING

 

2. Perform a search in McAfee ESM


Performs a query against McAfee ESM.

Base Command

esm-search

Input
Argument Name Description Required
timeRange The time period for the search Required
filters Filter on the query results in the format EsmFilter. Should be a JSON string. Required
queryType Query type to preform, by default EVENT (other possible values are : FLOW/ASSET) Optional
maxWait Maximum time to wait (in minutes), default is 30 Optional
customStart if timeRange is CUSTOM, start time for the time range (e.g. 2017-06-01T12:48:16.734Z) Optional
customEnd if timeRange is CUSTOM, end time for the time range (e.g. 2017-06-01T12:48:16.734Z) Optional
fields The fields that will be selected when this query is executed. Optional

 

Context Output

There is no context output for this command.

Command Example
!esm-search timeRange=LAST_10_MINUTES filters=`[{"type": "EsmFieldFilter", "field": {"name": "SrcIP"}, "operator": "EQUALS", "values": [{"type": "EsmBasicValue", "value": "52.15.91.198"}]}]
Context Example
{
    "SearchResults": [
        {
            "AlertIPSIDAlertID": "144115188075855872|10201"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 14:57:38"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10202"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 14:58:37"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10203"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 14:59:35"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10204"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:00:36"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10208"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:01:37"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10209"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:02:38"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10210"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:03:36"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10211"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:04:36"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10212"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:05:37"
        },
        {
            "ActionName": "success"
        },
        {
            "AlertIPSIDAlertID": "144115188075855872|10213"
        },
        {
            "AlertSrcIP": "52.15.91.198"
        },
        {
            "AlertSrcPort": "0"
        },
        {
            "AlertDstIP": "192.168.1.25"
        },
        {
            "AlertDstPort": "0"
        },
        {
            "AlertProtocol": "n/a"
        },
        {
            "AlertLastTime": "03/11/2019 15:06:38"
        },
        {
            "ActionName": "success"
        }
    ]
}
Human Readable Output

results:

Alert.IPSIDAlertID Alert.SrcIP Alert.SrcPort Alert.DstIP Alert.DstPort Alert.Protocol Alert.LastTime Action.Name
144115188075855872|10201 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 14:57:38 success
144115188075855872|10202 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 14:58:37 success
144115188075855872|10203 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 14:59:35 success
144115188075855872|10204 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:00:36 success
144115188075855872|10208 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:01:37 success
144115188075855872|10209 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:02:38 success
144115188075855872|10210 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:03:36 success
144115188075855872|10211 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:04:36 success
144115188075855872|10212 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:05:37 success
144115188075855872|10213 52.15.91.198 0 192.168.1.25 0 n/a 03/11/2019 15:06:38 success

 

3. Get a list of triggered alarms


Retrieves a list of triggered alarms.

Base Command

esm-fetch-alarms

Input
Argument Name Description Required
timeRange The time period for the fetch. Required
customStart if timeRange is CUSTOM, start time for the time range (e.g. 2017-06-01T12:48:16.734Z) Optional
customEnd if timeRange is CUSTOM, end time for the time range (e.g. 2017-06-01T12:48:16.734Z) Optional
assignedUser user assigned to handle this triggered alarm (use ‘ME’ option to use instance user, or use format EsmUser (read more on that here - https://:/rs/esm/help/types/EsmUser) Optional

 

Context Output
Path Type Description
Alarm.ID number Alarm ID
Alarm.summary string Alarm summary
Alarm.assignee string Alarm assignee
Alarm.severity number Alarm severity
Alarm.triggeredDate date Alarm triggered date
Alarm.acknowledgedDate date Alarm acknowledged date
Alarm.acknowledgedUsername string Alarm acknowledged username
Alarm.alarmName string Alarm name
Alarm.conditionType number Alarm condition type

 

Command Example
!esm-fetch-alarms timeRange="LAST_3_DAYS"
Context Example
{
    "Alarm": [
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:48:10",
            "acknowledgedDate": "03/11/2019 08:16:19",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "NGCP",
            "ID": 25
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:44:40",
            "acknowledgedDate": "03/11/2019 08:16:20",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "NGCP",
            "ID": 24
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:41:10",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 23
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:27:39",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 22
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:24:39",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 21
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:21:39",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 20
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:19:09",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 19
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:14:09",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 18
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:07:09",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 17
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:06:09",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 16
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/11/2019 01:01:39",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 15
        },
        {
            "conditionType": 13,
            "severity": 50,
            "triggeredDate": "03/10/2019 17:01:30",
            "acknowledgedDate": "",
            "summary": "408944640 - Failed Login Attempts - 306-31",
            "assignee": "NGCP",
            "alarmName": "Failed Login Attempts",
            "acknowledgedUsername": "",
            "ID": 14
        }
    ]
}
Human Readable Output

Result:

conditionType severity triggeredDate acknowledgedDate summary assignee alarmName ID acknowledgedUsername
13 50 03/11/2019 01:48:10 03/11/2019 08:16:19 408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 25 NGCP
13 50 03/11/2019 01:44:40 03/11/2019 08:16:20 408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 24 NGCP
13 50 03/11/2019 01:41:10   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 23  
13 50 03/11/2019 01:27:39   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 22  
13 50 03/11/2019 01:24:39   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 21  
13 50 03/11/2019 01:21:39   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 20  
13 50 03/11/2019 01:19:09   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 19  
13 50 03/11/2019 01:14:09   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 18  
13 50 03/11/2019 01:07:09   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 17  
13 50 03/11/2019 01:06:09   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 16  
13 50 03/11/2019 01:01:39   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 15  
13 50 03/10/2019 17:01:30   408944640 - Failed Login Attempts - 306-31 NGCP Failed Login Attempts 14  

 

4. Get a list of cases


Returns a list of cases from the McAfee ESM.

Base Command

esm-get-case-list

Input
Argument Name Description Required
since Filter for a case opened before this date. Given in format " ",e.g. 1 day,30 minutes,2 weeks,6 months,1 year Optional

 

Context Output
Path Type Description
Case.ID number The ID of the case
Case.Summary string The summary of the case
Case.Status string The status of the case
Case.OpenTime date The open time of the case
Case.Severity number The severity of the case
Case.Assignee string The Assignee of the case
Case.Organization string The organization of the case
Case.EventList unknown List of case’s events
Case.Notes unknown List of case’s notes

 

Command Example
!esm-get-case-list
Context Example
{
    "Case": [
        {
            "Status": "Open",
            "Summary": "case to be deleted",
            "OpenTime": "03/11/2019 08:15:02",
            "ID": 1,
            "Severity": 1
        },
        {
            "Status": "Open",
            "Summary": "New Virus Detected",
            "OpenTime": "03/11/2019 11:39:18",
            "ID": 2,
            "Severity": 1
        },
        {
            "Status": "Open",
            "Summary": "408944640 - Failed Login Attempts - 306-31",
            "OpenTime": "03/11/2019 11:41:02",
            "ID": 3,
            "Severity": 1
        },
        {
            "Status": "Open",
            "Summary": "this is the first case",
            "OpenTime": "03/11/2019 12:54:50",
            "ID": 4,
            "Severity": 1
        },
        {
            "Status": "Open",
            "Summary": "this is the first case",
            "OpenTime": "03/11/2019 13:27:22",
            "ID": 5,
            "Severity": 1
        },
        {
            "Status": "Open",
            "Summary": "this is the first case",
            "OpenTime": "03/11/2019 13:29:47",
            "ID": 6,
            "Severity": 1
        },
        {
            "Status": "Open",
            "Summary": "this is the first case",
            "OpenTime": "03/11/2019 13:33:13",
            "ID": 7,
            "Severity": 1
        }
    ]
}
Human Readable Output

All cases:

ID Summary Status Severity OpenTime
1 case to be deleted Open 1 03/11/2019 08:15:02
2 New Virus Detected Open 1 03/11/2019 11:39:18
3 408944640 - Failed Login Attempts - 306-31 Open 1 03/11/2019 11:41:02
4 this is the first case Open 1 03/11/2019 12:54:50
5 this is the first case Open 1 03/11/2019 13:27:22
6 this is the first case Open 1 03/11/2019 13:29:47
7 this is the first case Open 1 03/11/2019 13:33:13

 

5. Add a case


Adds a case to McAfee ESM.

Base Command

esm-add-case

Input
Argument Name Description Required
summary The name of the case Required
status The status of the case (use esm-get-case-statuses to view all statuses) Optional
assignee The user the case is assigned to Optional
severity The severity of the case (1 - 100) Optional
organization The organization assigned to the case (use esm-get-organization-list to view all organizations) Optional

 

Context Output
Path Type Description
Case.ID number The ID of the case
Case.Summary string The summary of the case
Case.Status string The status of the case
Case.OpenTime date The open time of the case
Case.Severity number The severity of the case
Case.Assignee string The assignee of the case
Case.Organization string The organization of the case
Case.EventList unknown List of case’s events
Case.Notes unknown List of case’s notes

 

Command Example
!esm-add-case summary="this is the first case"
Context Example
{
    "Case": [
        {
            "Status": "Open",
            "OpenTime": "03/11/2019 15:07:22",
            "Severity": 1,
            "EventList": [],
            "Notes": [
                {
                    "action": "Open",
                    "content": "",
                    "username": "NGCP",
                    "changes": [],
                    "timestamp": "03/11/2019 15:07:22(GMT)"
                }
            ],
            "Summary": "this is the first case",
            "Assignee": "NGCP",
            "Organization": "None",
            "ID": 8
        }
    ]
}
Human Readable Output

New Case:

ID Summary Status Severity OpenTime Assignee Organization Event List Notes
8 this is the first case Open 1 03/11/2019 15:07:22 NGCP None [] [{“action”: “Open”, “timestamp”: “03/11/2019 15:07:22(GMT)”, “username”: “NGCP”, “content”: “”, “changes”: []}]

 

6. Edit a case


Modifies an existing case.

Base Command

esm-edit-case

Input
Argument Name Description Required
id The ID of the case Required
summary The name of the case Optional
severity The new severity of the case (1 - 100) Optional
assignee The user that the case should be assigned to Optional
status The new status of the case (use the esm-get-case-statuses command to view all statuses) Optional
organization The organization assigned to the case (use the esm-get-organization-list command to view all organizations) Optional

 

Context Output
Path Type Description
Case.ID number The ID of the case
Case.Summary string The summary of the case
Case.Status string The status of the case
Case.OpenTime date The open time of the case
Case.Severity number The severity of the case
Case.Assignee string The Assignee of the case
Case.Organization string The organization of the case
Case.EventList unknown List of case’s events
Case.Notes unknown List of case’s notes

 

Command Example
!esm-edit-case id="2" summary="editing first case" severity="50" organization="LuthorCorp"
Context Example
{
    "Case": [
        {
            "Status": "Open",
            "OpenTime": "03/11/2019 11:39:18",
            "Severity": 50,
            "EventList": [],
            "Notes": [
                {
                    "action": "Changes",
                    "content": "Summary\n    old: New Virus Detected\n    new: editing first case\n\n  Severity\n    old: 1\n    new: 50",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Summary",
                            "changes": [
                                "old: New Virus Detected",
                                "new: editing first case"
                            ]
                        },
                        {
                            "changeType": "Severity",
                            "changes": [
                                "old: 1",
                                "new: 50"
                            ]
                        }
                    ],
                    "timestamp": "03/11/2019 15:07:26(GMT)"
                },
                {
                    "action": "Changes",
                    "content": "Summary\n    old: editing first case\n    new: New Virus Detected\n\n  Severity\n    old: 50\n    new: 1",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Summary",
                            "changes": [
                                "old: editing first case",
                                "new: New Virus Detected"
                            ]
                        },
                        {
                            "changeType": "Severity",
                            "changes": [
                                "old: 50",
                                "new: 1"
                            ]
                        }
                    ],
                    "timestamp": "03/11/2019 15:01:28(GMT)"
                },
                {
                    "action": "Changes",
                    "content": "Summary\n    old: New Virus Detection\n    new: editing first case\n\n  Severity\n    old: 1\n    new: 50",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Summary",
                            "changes": [
                                "old: New Virus Detection",
                                "new: editing first case"
                            ]
                        },
                        {
                            "changeType": "Severity",
                            "changes": [
                                "old: 1",
                                "new: 50"
                            ]
                        }
                    ],
                    "timestamp": "03/11/2019 13:33:16(GMT)"
                },
                {
                    "action": "Changes",
                    "content": "Summary\n    old: editing first New Virus Detection\n    new: New Virus Detection",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Summary",
                            "changes": [
                                "old: editing first New Virus Detection",
                                "new: New Virus Detection"
                            ]
                        }
                    ],
                    "timestamp": "03/11/2019 13:31:59(GMT)"
                },
                {
                    "action": "Changes",
                    "content": "Summary\n    old: editing first case\n    new: editing first New Virus Detection\n\n  Severity\n    old: 50\n    new: 1",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Summary",
                            "changes": [
                                "old: editing first case",
                                "new: editing first New Virus Detection"
                            ]
                        },
                        {
                            "changeType": "Severity",
                            "changes": [
                                "old: 50",
                                "new: 1"
                            ]
                        }
                    ],
                    "timestamp": "03/11/2019 13:31:45(GMT)"
                },
                {
                    "action": "Changes",
                    "content": "Summary\n    old: New Virus Detection\n    new: editing first case\n\n  Severity\n    old: 1\n    new: 50",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Summary",
                            "changes": [
                                "old: New Virus Detection",
                                "new: editing first case"
                            ]
                        },
                        {
                            "changeType": "Severity",
                            "changes": [
                                "old: 1",
                                "new: 50"
                            ]
                        }
                    ],
                    "timestamp": "03/11/2019 13:27:25(GMT)"
                },
                {
                    "action": "Open",
                    "content": "",
                    "username": "NGCP",
                    "changes": [],
                    "timestamp": "03/11/2019 11:39:18(GMT)"
                }
            ],
            "Summary": "editing first case",
            "Assignee": "NGCP",
            "Organization": "None",
            "ID": 2
        }
    ]
}
Human Readable Output

Edited Case:

ID Summary Status Severity OpenTime Assignee Organization Event List Notes
2 editing first case Open 50 03/11/2019 11:39:18 NGCP None [] [{“action”: “Changes”, “timestamp”: “03/11/2019 15:07:26(GMT)”, “username”: “NGCP”, “content”: “Summary\n old: New Virus Detected\n new: editing first case\n\n Severity\n old: 1\n new: 50”, “changes”: [{“changeType”: “Summary”, “changes”: [“old: New Virus Detected”, “new: editing first case”]}, {“changeType”: “Severity”, “changes”: [“old: 1”, “new: 50”]}]}, {“action”: “Changes”, “timestamp”: “03/11/2019 15:01:28(GMT)”, “username”: “NGCP”, “content”: “Summary\n old: editing first case\n new: New Virus Detected\n\n Severity\n old: 50\n new: 1”, “changes”: [{“changeType”: “Summary”, “changes”: [“old: editing first case”, “new: New Virus Detected”]}, {“changeType”: “Severity”, “changes”: [“old: 50”, “new: 1”]}]}, {“action”: “Changes”, “timestamp”: “03/11/2019 13:33:16(GMT)”, “username”: “NGCP”, “content”: “Summary\n old: New Virus Detection\n new: editing first case\n\n Severity\n old: 1\n new: 50”, “changes”: [{“changeType”: “Summary”, “changes”: [“old: New Virus Detection”, “new: editing first case”]}, {“changeType”: “Severity”, “changes”: [“old: 1”, “new: 50”]}]}, {“action”: “Changes”, “timestamp”: “03/11/2019 13:31:59(GMT)”, “username”: “NGCP”, “content”: “Summary\n old: editing first New Virus Detection\n new: New Virus Detection”, “changes”: [{“changeType”: “Summary”, “changes”: [“old: editing first New Virus Detection”, “new: New Virus Detection”]}]}, {“action”: “Changes”, “timestamp”: “03/11/2019 13:31:45(GMT)”, “username”: “NGCP”, “content”: “Summary\n old: editing first case\n new: editing first New Virus Detection\n\n Severity\n old: 50\n new: 1”, “changes”: [{“changeType”: “Summary”, “changes”: [“old: editing first case”, “new: editing first New Virus Detection”]}, {“changeType”: “Severity”, “changes”: [“old: 50”, “new: 1”]}]}, {“action”: “Changes”, “timestamp”: “03/11/2019 13:27:25(GMT)”, “username”: “NGCP”, “content”: “Summary\n old: New Virus Detection\n new: editing first case\n\n Severity\n old: 1\n new: 50”, “changes”: [{“changeType”: “Summary”, “changes”: [“old: New Virus Detection”, “new: editing first case”]}, {“changeType”: “Severity”, “changes”: [“old: 1”, “new: 50”]}]}, {“action”: “Open”, “timestamp”: “03/11/2019 11:39:18(GMT)”, “username”: “NGCP”, “content”: “”, “changes”: []}]

 

7. Get a list of case statuses


Returns a list of valid case statuses from McAfee ESM.

Base Command

esm-get-case-statuses

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example
!esm-get-case-statuses
Human Readable Output

Result:

ID Name Is Default Show In Case Pane
2 Closed false false
1 Open true true
8 Pending false true
4 Research false false

 

8. Edit the status of a case


Modifies a case status.

Base Command

esm-edit-case-status

Input
Argument Name Description Required
original_name The name of the case status to edit Required
new_name The new name for the case status Required
show_in_case_pane Whether the status will be shown in the case pane Optional

 

Context Output

There is no context output for this command.

Command Example
!esm-edit-case-status original_name=Research new_name=RnD
Human Readable Output

Edit case status with ID: 4

9. Get details of a case


Returns details about an existing case.

Base Command

esm-get-case-detail

Input
Argument Name Description Required
id The ID of the case Required

 

Context Output
Path Type Description
Case.ID number The ID of the case
Case.Summary string The summary of the case
Case.Status string The status of the case
Case.OpenTime date The open time of the case
Case.Severity number The severity of the case
Case.Assignee string The assignee of the case
Case.Organization string The organization of the case
Case.EventList unknown List of case’s events
Case.Notes unknown List of case’s notes

 

Command Example
!esm-get-case-detail id=3
Context Example
{
    "Case": [
        {
            "Status": "Open",
            "OpenTime": "03/11/2019 11:41:02",
            "Severity": 1,
            "EventList": [
                {
                    "message": "Failed User Logon",
                    "lastTime": "03/11/2019 01:01:13",
                    "id": {
                        "value": "144115188075855872|8850"
                    }
                }
            ],
            "Notes": [
                {
                    "action": "Open",
                    "content": "Events Added: 144115188075855872|8850\n    Events Removed:",
                    "username": "NGCP",
                    "changes": [
                        {
                            "changeType": "Events Added",
                            "changes": [
                                "144115188075855872|8850"
                            ]
                        },
                        {
                            "changeType": "Events Removed",
                            "changes": []
                        }
                    ],
                    "timestamp": "03/11/2019 11:41:02(GMT)"
                }
            ],
            "Summary": "408944640 - Failed Login Attempts - 306-31",
            "Assignee": "NGCP",
            "Organization": "None",
            "ID": 3
        }
    ]
}
Human Readable Output

Case 3:

ID Summary Status Severity OpenTime Assignee Organization Event List Notes
3 408944640 - Failed Login Attempts - 306-31 Open 1 03/11/2019 11:41:02 NGCP None [{“message”: “Failed User Logon”, “lastTime”: “03/11/2019 01:01:13”, “id”: {“value”: “144115188075855872|8850”}}] [{“action”: “Open”, “timestamp”: “03/11/2019 11:41:02(GMT)”, “username”: “NGCP”, “content”: “Events Added: 144115188075855872|8850\n Events Removed:”, “changes”: [{“changeType”: “Events Added”, “changes”: [“144115188075855872|8850”]}, {“changeType”: “Events Removed”, “changes”: []}]}]

 

10. Get details of a case event


Returns case event details.

Base Command

esm-get-case-event-list

Input
Argument Name Description Required
ids CSV list of event IDs Required

 

Context Output
Path Type Description
CaseEvents.ID string The ID of the event
CaseEvents.LastTime date The last updated time of the event
CaseEvents.Message string The message of the event

 

Command Example
!esm-get-case-event-list ids=144115188075855872|8850,144115188075855872|9718
Context Example
{
    "CaseEvents": [
        {
            "Message": "Failed User Logon",
            "LastTime": "03/11/2019 01:01:13",
            "ID": "144115188075855872|8850"
        },
        {
            "Message": "User Logon",
            "LastTime": "03/11/2019 11:09:37",
            "ID": "144115188075855872|9718"
        }
    ]
}
Human Readable Output

Case Events:

ID LastTime Message
144115188075855872|8850 03/11/2019 01:01:13 Failed User Logon
144115188075855872|9718 03/11/2019 11:09:37 User Logon

 

11. Add a status to a case


Adds a case status to a case.

Base Command

esm-add-case-status

Input
Argument Name Description Required
name The name of the case status Required
show_in_case_pane Whether the status will be shown in case pane Optional

 

Context Output

There is no context output for this command.

Command Example
!esm-add-case-status name=Deprecated
Human Readable Output

Added case status : Deprecated

12. Remove a status from a case


Deletes a case status from a case.

Base Command

esm-delete-case-status

Input
Argument Name Description Required
name The name of the case status to delete Required

 

Context Output

There is no context output for this command.

Command Example
esm-delete-case-status name=Pending
Human Readable Output

Deleted case status with ID: 8

13. Get a list of case organizations


Returns a list case organizations.

Base Command

esm-get-organization-list

Input

There are no input arguments for this command.

Context Output
Path Type Description
Organizations.ID number Organization ID
Organizations.Name string Organization Name

 

Command Example
!esm-get-organization-list
Context Example
{
    "Organizations": [
        {
            "ID": 1,
            "Name": "None"
        }
    ]
}
Human Readable Output

Organizations:

ID Name
1 None

 

14. Get a list of all users


Returns a list of all users.

Base Command

esm-get-user-list

Input

There are no input arguments for this command.

Context Output
Path Type Description
EsmUser.ID number The ID of the user
EsmUser.Name string The ESM user name
EsmUser.Email string The email address of the user
EsmUser.SMS string The SMS details of the user
EsmUser.IsMaster boolean Whether the user is a master user
EsmUser.IsAdmin boolean Whether the user is an admin

 

Command Example
!esm-get-user-list
Context Example
{
    "EsmUser": [
        {
            "IsMaster": true,
            "Name": "NGCP",
            "SMS": "",
            "ID": 1,
            "IsAdmin": false,
            "Email": ""
        },
        {
            "IsMaster": false,
            "Name": "POLICY",
            "SMS": "",
            "ID": 3,
            "IsAdmin": false,
            "Email": ""
        },
        {
            "IsMaster": false,
            "Name": "REPORT",
            "SMS": "",
            "ID": 2,
            "IsAdmin": false,
            "Email": ""
        }
    ]
}
Human Readable Output

Users:

ID Name Email SMS IsMaster IsAdmin
1 NGCP     true false
3 POLICY     false false
2 REPORT     false false

 

15. Mark triggered alarms as acknowledged


Marks triggered alarms as acknowledged.

Base Command

esm-acknowledge-alarms

Input
Argument Name Description Required
alarmIds A CSV list of triggered alarm IDs to be marked acknowledged  Required

 

Context Output

There is no context output for this command.

Command Example
!esm-acknowledge-alarms alarmIds=2,5,6
Human Readable Output

Alarms has been Acknowledged.

16. Mark triggered alarms as unacknowledged


Marks triggered alarms as unacknowledged.

Base Command

esm-unacknowledge-alarms

Input
Argument Name Description Required
alarmIds A CSV list of triggered alarm IDs to be marked unacknowledged Required

 

Context Output

There is no context output for this command.

Command Example
!esm-unacknowledge-alarms alarmIds="1,8,7"
Human Readable Output

Alarms has been Unacknowledged.

17. Delete triggered alarms


Deletes triggered alarms.

Base Command

esm-delete-alarms

Input
Argument Name Description Required
alarmIds A CSV list of triggered alarm IDs to be deleted Required

 

Context Output

There is no context output for this command.

Command Example
!esm-delete-alarms alarmIds=26
Human Readable Output

Alarms has been Deleted.

18. Get details for a triggered alarm


Returns details for a triggered alarm.

Base Command

esm-get-alarm-event-details

Input
Argument Name Description Required
eventId The event to get the details for. The ID can be retrieved from the esm-list-alarm-events command. Required

 

Context Output
Path Type Description
EsmAlarmEvent.ID string Event ID
EsmAlarmEvent.SubType string Event type
EsmAlarmEvent.Severity number Event severity
EsmAlarmEvent.Message string Event message
EsmAlarmEvent.LastTime date Event time
EsmAlarmEvent.SrcIP string Source IP of the event
EsmAlarmEvent.DstIP string Destination IP of the event
EsmAlarmEvent.Cases unknown A list of related cases to the event
EsmAlarmEvent.Cases.ID string Case ID
EsmAlarmEvent.Cases.OpenTime date Case creation time
EsmAlarmEvent.Cases.Severity number Case severity
EsmAlarmEvent.Cases.Status string Case status
EsmAlarmEvent.Cases.Summary string Case summary
EsmAlarmEvent.DstMac string Destination MAC of the event
EsmAlarmEvent.SrcMac string Source MAC of the event
EsmAlarmEvent.DstPort string Destination port of the event
EsmAlarmEvent.SrcPort string Source port of the event
EsmAlarmEvent.FirstTime date The first time for the event
EsmAlarmEvent.NormalizedDescription string Normalized description of the event

 

Command Example
!esm-get-alarm-event-details eventId="144115188075855872|9718"
Context Example
{
    "EsmAlarmEvent": [
        {
            "DstIP": "192.168.1.25",
            "FirstTime": "03/11/2019 11:09:37",
            "Severity": 19,
            "DstPort": "0",
            "SrcPort": "0",
            "DstMac": "00:00:00:00:00:00",
            "SubType": "success",
            "SrcIP": "52.15.91.198",
            "Message": "User Logon",
            "LastTime": "03/11/2019 11:09:37",
            "ID": "144115188075855872|9718",
            "NormalizedDescription": "The Login category indicates events related to logging in to hosts or services.  Belongs to Authentication: The authentication category indicates events relating to system access.",
            "SrcMac": "00:00:00:00:00:00"
        }
    ]
}
Human Readable Output

Alarm Events:

ID SubType Severity Message LastTime SrcIP SrcPort DstIP DstPort
144115188075855872|9718 success 19 User Logon 03/11/2019 11:09:37 52.15.91.198 0 192.168.1.25 0

 

19. Get an event list related to an alarm


Returns an event list related to an alarm.

Base Command

esm-list-alarm-events

Input
Argument Name Description Required
alarmId The alarm to get the details for. The ID can be retrieved from the esm-fetch-alarms command. Required

 

Context Output
Path Type Description
EsmAlarmEvent.ID string Event ID
EsmAlarmEvent.SubType string Event type
EsmAlarmEvent.Severity number Event severity
EsmAlarmEvent.Message string Event message
EsmAlarmEvent.LastTime date Event time
EsmAlarmEvent.SrcIP string Source IP of the event
EsmAlarmEvent.DstIP string Destination IP of the event
EsmAlarmEvent.Cases unknown A list of related cases to the event
EsmAlarmEvent.Cases.ID string Case ID
EsmAlarmEvent.Cases.OpenTime date Case creation time
EsmAlarmEvent.Cases.Severity number Case severity
EsmAlarmEvent.Cases.Status string Case status
EsmAlarmEvent.Cases.Summary string Case summary

 

Command Example
!esm-list-alarm-events alarmId="24"
Context Example
{
    "EsmAlarmEvent": [
        {
            "DstIP": "192.168.1.25",
            "Severity": 25,
            "SubType": "failure",
            "SrcIP": "186.29.149.40",
            "Message": "Failed User Logon",
            "LastTime": "03/11/2019 01:44:27",
            "ID": "144115188075855872|8919"
        }
    ]
}
Human Readable Output

Alarm Events:

ID SubType Severity Message LastTime SrcIP SrcPort DstIP DstPort
144115188075855872|8919 failure 25 Failed User Logon 03/11/2019 01:44:27 186.29.149.40   192.168.1.25  

Configuration parameters

  • ip — ESM IP (e.g. 78.125.0.209) (required)
  • port — Port (required)
  • credentials — Username (required)
  • version — Version: (one of 10.0, 10.1, 10.2, 10.3, 11.1) (required)
  • insecure — Trust any certificate (not secure)
  • time_format — ESM time format, e.g., %Y/%m/%d %H:%M:%S. Select "auto-discovery" to attempt to determine the format automatically.
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • fetchTypes — Fetch Types: cases, alarms, both (relevant only for fetch incident mode)
  • startingCaseID — Start fetch after Case ID: (relevant only for fetch incident mode)
  • alarm_fetch_time — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)
  • timezone — McAfee ESM Timezone in hours (e.g if ESM timezone is +0300 => then insert 3)

Commands (19)

  • esm-acknowledge-alarms

    Mark triggered alarms as acknowledged

  • esm-add-case

    Add a case to the system

  • esm-add-case-status

    Add a case status

  • esm-delete-alarms

    Delete triggered alarms

  • esm-delete-case-status

    Delete a case status

  • esm-edit-case

    Edit an existing case

  • esm-edit-case-status

    Edit a case status

  • esm-fetch-alarms

    Retrieves a list of alarms that have been triggered

  • esm-fetch-fields

    Get all fields that can be used in query filters, with type information for each field

  • esm-get-alarm-event-details

    Gets the details for the triggered alarm

  • esm-get-case-detail

    Get detail on an existing case

  • esm-get-case-event-list

    Get case events details

  • esm-get-case-list

    Get a list of cases from the system

  • esm-get-case-statuses

    Get a list of valid case statuses from the system

  • esm-get-organization-list

    Get case organization

  • esm-get-user-list

    Get a list of all users.

  • esm-list-alarm-events

    Gets an event list related to the alarm

  • esm-search

    Perform a query against Mcafee ESM SIEM

  • esm-unacknowledge-alarms

    Mark triggered alarms as unacknowledged

import demistomock as demisto
from CommonServerPython import *
from CommonServerUserPython import *

""" IMPORTS """
import base64
import json
import re
import sys
import requests
import time
import traceback
from datetime import datetime, timedelta
from distutils.util import strtobool

MAX_CASES_PER_FETCH = 30

# by default filters only "Closed" cases
FILTERED_OUT_STATUSES = [2, ]
VERIFY = demisto.params()['insecure'] is False
requests.packages.urllib3.disable_warnings()
VERSION = demisto.params()['version']
IS_V2_API = VERSION in ['10.2', '10.3', '11.1']

ESM_URL = demisto.params()['ip'] + ":" + demisto.params()['port']
USERNAME = demisto.params()['credentials']['identifier']
PASSWORD = demisto.params()['credentials']['password']
ESM_DATE_FORMAT = demisto.params()['time_format']
TIMEZONE = float(demisto.params().get('timezone'))


@logger
def parse_time(time_str):
    if ESM_DATE_FORMAT != 'auto-discovery':
        return ESM_DATE_FORMAT

    regex_to_format = {
        r'\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}': '%Y/%m/%d %H:%M:%S',  # '2018-12-31 16:54:32'
        r'\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}': '%d/%m/%Y %H:%M:%S',  # '31-12-2018 16:54:32'
    }

    selected_format = '%Y/%m/%d %H:%M:%S'
    for regex, time_format in regex_to_format.items():
        if re.match(regex, time_str):
            selected_format = time_format
            break

    return selected_format


@logger
def search_result_to_md(res):
    columns = res['columns']
    rows = res['rows']

    md = "### results:\n"

    if len(rows) == 0:
        return md + "No matching search result were found"

    # headers
    column_names = list(map(lambda column: column['name'], columns))
    md = md + ' | '.join(column_names) + '\n'
    md = md + ' | '.join(list(map(lambda column: "---", columns))) + '\n'

    # body
    for row in rows:
        md = md + ' | '.join(r.replace('|', '\\|') for r in row['values']) + '\n'

    return md


@logger
def search_results_to_context(res):
    columns = res['columns']
    rows = res['rows']
    fixed_searches = []
    for row in rows:
        values = row['values']
        i = 0
        for column in columns:
            if len(values[i]) != 0:
                column_string = column['name'].replace(".", "")
                column_string = column_string.replace(")", "")
                column_string = column_string.replace("(", "")
                fixed_searches.append({
                    column_string: values[i]
                })
            i += 1
    context = {'SearchResults(val.ID && val.ID == obj.ID)': fixed_searches}
    return context


def severity_to_level(severity):
    if severity > 65:
        return 3
    elif severity > 32:
        return 2
    else:
        return 1


class NitroESM(object):
    def __init__(self, esmhost, user, passwd):
        """ Init instance attributes """
        self.esmhost = esmhost
        self.user = user
        self.passwd = passwd
        self.url = 'https://{}/rs/esm/'.format(self.esmhost)
        self.session_headers = {'Content-Type': 'application/json'}
        self.is_logged_in = False
        self._case_statuses = None

    def __repr__(self):
        return 'NitroESM("{}", "{}")'.format(self.url, self.user)

    def login(self):
        b64_user = base64.b64encode(self.user.encode('utf-8')).decode()
        b64_passwd = base64.b64encode(self.passwd.encode('utf-8')).decode()
        params = {
            "username": b64_user,
            "password": b64_passwd,
            "locale": "en_US",
            "os": "Win32"
        }
        login_response = requests.post(self.url + 'login',
                                       json=params,
                                       headers=self.session_headers,
                                       verify=VERIFY)
        jwttoken = login_response.cookies.get('JWTToken')
        xsrf_token = login_response.headers.get('Xsrf-Token')
        if jwttoken is None or xsrf_token is None:
            raise Exception("Failed login\nurl: {}\n response status: {}\nresponse: {}\n".format(
                self.url + 'login',
                login_response.status_code,
                login_response.text))

        self.session_headers = {
            'Cookie': 'JWTToken=' + jwttoken,
            'X-Xsrf-Token': xsrf_token,
            'Content-Type': 'application/json'
        }
        self.is_logged_in = True

    def logout(self):
        if self.is_logged_in:
            try:
                url = self.url + ('v2/logout' if IS_V2_API else 'logout')
                requests.delete(url,
                                headers=self.session_headers,
                                data=json.dumps(''),
                                verify=VERIFY
                                )
                self.is_logged_in = False
            except Exception as e:
                demisto.error('McAfee ESM logout failed with the following error: %s' % (str(e),))

    @logger
    def cmdquery(self, cmd, query=None, params=None, no_answer=False, no_validation=False):
        """ Send query to ESM, return JSON result """
        LOG('querying endpoint: {}'.format(cmd))
        result = requests.post(self.url + cmd,
                               headers=self.session_headers,
                               params=params,
                               data=query, verify=VERIFY)
        if not no_validation:
            if no_answer:
                if result.status_code != 200:
                    raise ValueError(
                        'Error - ESM replied with:\n - status code: {} \n - body: {}'.format(result.status_code,
                                                                                             result.text))
            else:
                try:
                    res = result.json()
                    if VERSION != '10.0' and not cmd.startswith('v2'):
                        res = res['return']
                    return res
                except Exception as e:  # noqa: E722
                    LOG(str(e))
                    raise ValueError(
                        'Error - ESM replied with:\n - status code: {} \n - body: {}'.format(result.status_code,
                                                                                             result.text))

    @logger
    def execute_query(self, time_range, custom_start, custom_end, filters, fields, query_type):
        if time_range == 'CUSTOM' and (not custom_start or not custom_end):
            raise ValueError('you must specify customStart and customEnd when timeRange is CUSTOM')

        cmd = '%sqryExecuteDetail?reverse=false&type=%s' % ('v2/' if IS_V2_API else '', query_type,)

        if time_range == 'CUSTOM':
            cmd = cmd + '&customStart=' + custom_start + '&customEnd=' + custom_end

        q = {
            'config': {
                'timeRange': time_range,
                'filters': filters,
            }
        }
        if fields is not None:
            q['config']['fields'] = [{'name': v} for v in argToList(fields)]
        query = json.dumps(q)

        res = self.cmdquery(cmd, query)
        return res['resultID']

    @logger
    def get_query_result(self, result_id):
        cmd = '%sqryGetStatus' % ('v2/' if IS_V2_API else '',)
        query = json.dumps({'resultID': result_id})

        res = self.cmdquery(cmd, query)
        return res['complete']

    def wait_for_results(self, result_id, max_wait):

        # initial back off, sleep 3 sec between each time
        for i in range(5):
            ready = self.get_query_result(result_id)
            if ready:
                return
            else:
                time.sleep(3)

        # wait for response - 1 min between each try
        for i in range(max_wait):
            ready = self.get_query_result(result_id)
            if ready:
                return
            else:
                time.sleep(60)  # pylint: disable=sleep-exists

        raise ValueError('Waited more than {} min for query results : {}'.format(max_wait, result_id))

    @logger
    def fetch_results(self, result_id):
        cmd = '%sqryGetResults?startPos=0&reverse=false&numRows=10000' % ('v2/' if IS_V2_API else '',)

        query = json.dumps({'resultID': result_id})

        res = self.cmdquery(cmd, query)
        return res

    @logger
    def search(self, time_range, custom_start, custom_end, filters, fields, query_type, max_wait):
        # execute command
        result_id = self.execute_query(time_range, custom_start, custom_end, filters, fields, query_type)

        # wait for result to be ready
        self.wait_for_results(result_id, max_wait)

        # fetch result
        res = self.fetch_results(result_id)

        table = search_result_to_md(res)
        context = search_results_to_context(res)

        return {
            'ContentsFormat': formats['json'],
            'Type': entryTypes['note'],
            'Contents': res,
            'ReadableContentsFormat': formats['markdown'],
            'HumanReadable': table,
            'EntryContext': context
        }

    @logger
    def fetch_all_fields(self):
        res = self.cmdquery('%sqryGetFilterFields' % ('v2/' if IS_V2_API else '',))

        # convert to an appropriate table
        for x in res:
            x['types'] = ','.join(x['types'])

        return {
            'ContentsFormat': formats['json'],
            'Type': entryTypes['note'],
            'Contents': res,
            'ReadableContentsFormat': formats['markdown'],
            'HumanReadable': tblToMd('Result:', res),
        }

    # alarms commands
    @logger
    def fetch_alarms(self, time_range, custom_start, custom_end, assigned_user):
        if time_range == 'CUSTOM' and (not custom_start or not custom_end):
            raise ValueError('you must specify customStart and customEnd when timeRange is CUSTOM')

        params = {
            'pageSize': 50,
            'pageNumber': 1,
            'triggeredTimeRange': time_range
        }

        if VERSION.startswith('11.'):
            cmd = 'alarmGetTriggeredAlarms'
        else:
            cmd = 'alarmGetTriggeredAlarmsPaged'
        if time_range == 'CUSTOM':
            params.update({
                'customStart': custom_start,
                'customEnd': custom_end,
            })

        query = ''
        if assigned_user == 'ME':
            user = self.get_user_obj(self.user)
            query = json.dumps({'assignedUser': user})
        elif assigned_user:
            query = json.dumps({'assignedUser': self.get_user_obj(assigned_user)})

        res = self.cmdquery(cmd, query, params=params)

        for alarm in res:
            alarm['ID'] = alarm['id']['value']
            del alarm["id"]

        return res

    @logger
    def update_alarms_status(self, action, alarm_ids):
        cmd = 'alarm%(action)sTriggeredAlarm' % {'action': action}
        query = json.dumps({'triggeredIds': [{'value': int(i)} for i in alarm_ids]})

        # the command return an error for a list of alarm ids however is execute the update successfully.
        self.cmdquery(cmd, query, no_validation=True)
        return 'Alarms has been %sd.' % (action,)

    @logger
    def acknowledge_alarms(self, alarm_ids):
        return self.update_alarms_status('Acknowledge', alarm_ids)

    @logger
    def unacknowledge_alarms(self, alarm_ids):
        return self.update_alarms_status('Unacknowledge', alarm_ids)

    @logger
    def delete_alarms(self, alarm_ids):
        return self.update_alarms_status('Delete', alarm_ids)

    @logger
    def get_alarm_event_details(self, event_id):
        cmd = ('%sipsGetAlertData' % ('v2/' if IS_V2_API else '',))
        query = json.dumps({'id': event_id})

        res = self.cmdquery(cmd, query)

        return res

    @logger
    def list_alarm_events(self, alarm_id):
        cmd = 'notifyGetTriggeredNotificationDetail'
        query = json.dumps({'id': alarm_id})

        res = self.cmdquery(cmd, query)

        return res

    # case statuses commands
    @logger
    def add_case_status(self, name, should_show_in_case_pane):
        """add a new type of case status with given parameters"""

        status_details = {
            'name': name,
            'default': False,
            'showInCasePane': should_show_in_case_pane
        }

        cmd = 'caseAddCaseStatus'
        query = json.dumps({'status': status_details})
        self.cmdquery(cmd, query)
        return 'Added case status : %s' % (name,)

    @logger
    def edit_case_status(self, original_name, new_name, show_in_case_pane):
        """edit a  case status with given id"""

        status_id = self.case_status_name_to_id(original_name)
        status_details = {
            'id': status_id,
            'name': new_name
        }

        if show_in_case_pane is not None:
            status_details['showInCasePane'] = show_in_case_pane

        cmd = 'caseEditCaseStatus'
        query = json.dumps({'status': status_details})
        self.cmdquery(cmd, query, no_answer=True)

        return 'Edit case status with ID: %d' % (status_id,)

    @logger
    def delete_case_status(self, name):
        """delete a new type of case status with given name"""

        status_id = self.case_status_name_to_id(name)
        status_id = {'value': status_id}

        cmd = 'caseDeleteCaseStatus'
        query = json.dumps({'statusId': status_id})
        self.cmdquery(cmd, query, no_answer=True)

        return 'Deleted case status with ID: %d' % (status_id['value'],)

    @logger
    def get_case_statuses(self):
        """get all case statuses"""

        cmd = 'caseGetCaseStatusList'
        query = json.dumps({"authPW": {"value": self.passwd}})

        return self.cmdquery(cmd, query)

    @logger
    def case_status_id_to_name(self, status_id, use_cache=True):
        """convert case status id to name"""
        if self._case_statuses is None:
            self._case_statuses = demisto.getIntegrationContext().get('case_statuses', None)

        if self._case_statuses is None or not use_cache or not any([s['id'] == status_id for s in self._case_statuses]):
            self._case_statuses = self.get_case_statuses()
            demisto.setIntegrationContext({
                'case_statuses': self._case_statuses
            })

        matches = [status['name'] for status in self._case_statuses if status['id'] == status_id]

        return matches[0] if matches else 'Unknown - %d' % (status_id,)

    @logger
    def case_status_name_to_id(self, status_name, use_cache=True):
        """convert case status name to id"""
        if self._case_statuses is None or not use_cache or not any(
                [s['name'].lower() == status_name.lower() for s in self._case_statuses]):
            self._case_statuses = self.get_case_statuses()

        matches = [status['id'] for status in self._case_statuses if status['name'].lower() == status_name.lower()]

        return matches[0] if matches else 0  # 0 is not a valid value

    # user commands
    @logger
    def get_users(self):
        """get all user's names"""

        cmd = 'userGetUserList'
        query = json.dumps({"authPW": {"value": self.passwd}})
        return self.cmdquery(cmd, query)

    @logger
    def get_user_obj(self, user_name):
        """get user object"""
        if user_name.lower() == 'me':
            user_name = self.user

        res = self.get_users()
        matches = [user for user in res if user['username'] == user_name]
        self_matches = [user['id']['value'] for user in res if user['username'] == self.user]

        # the login user must appear in the user list
        return matches[0] if matches else self_matches[0]

    @logger
    def user_name_to_id(self, user_name):
        """convert user name to id"""
        if user_name is None or user_name.lower() == 'me':
            user_name = self.user
        res = self.get_users()
        matches = [user['id']['value'] for user in res if user['username'] == user_name]
        self_matches = [user['id']['value'] for user in res if user['username'] == self.user]

        # the login user must appear in the user list
        return matches[0] if matches else self_matches[0]

    @logger
    def user_id_to_name(self, user_id):
        """convert user id to name"""
        res = self.get_users()
        matches = [user['username'] for user in res if user['id']['value'] == user_id]

        return matches[0] if matches else self.user

    # organization commands
    @logger
    def get_organizations(self):
        """get all organization names"""

        cmd = 'caseGetOrganizationList'
        return self.cmdquery(cmd, '')

    @logger
    def organization_name_to_id(self, organization_name):
        """convert organization name to id"""
        if organization_name is None:
            organization_name = ''
        res = self.get_organizations()
        matches = [org['id'] for org in res if org['name'].lower() == organization_name.lower()]

        return matches[0] if matches else 1

    @logger
    def organization_id_to_name(self, organization_id):
        """convert organization name to id"""
        res = self.get_organizations()
        matches = [org['name'] for org in res if org['id'] == organization_id]

        return matches[0] if matches else 'None'

    # cases commands
    @logger
    def get_cases(self, since_date_range=None):
        """get all cases associated with current user"""
        cmd = 'caseGetCaseList'
        res = self.cmdquery(cmd)
        cases = []
        if since_date_range:
            start_time, _ = parse_date_range(since_date_range, '%Y/%m/%d %H:%M:%S')
            for case in res:
                if case.get('openTime') > start_time:
                    cases.append(case)

        else:
            cases = res

        return cases

    def get_case_detail(self, case_id):
        cmd = 'caseGetCaseDetail'
        case_id = {'id': {'value': case_id}}
        query = json.dumps(case_id)
        res = self.cmdquery(cmd, query)

        return res

    def add_case(self, summary, severity, status, assignee, organization):
        if severity < 1:
            severity = 1
        elif severity > 100:
            severity = 100

        if status is None:
            status = 'Open'

        assignee = self.user_name_to_id(assignee)
        org_id = self.organization_name_to_id(organization)

        cmd = 'caseAddCase'
        case_details = {
            'summary': summary,
            'assignedTo': assignee,
            'severity': severity,
            'orgId': org_id,
            'statusId': {'value': self.case_status_name_to_id(status)},
        }

        query = json.dumps({'caseDetail': case_details})
        res = self.cmdquery(cmd, query)
        return res['value']

    def edit_case(self, case_id, summary, severity, status, assignee, organization):

        case = self.get_case_detail(case_id)

        if summary is not None:
            case['summary'] = summary

        if severity is not None:
            if severity < 1:
                severity = 1
            elif severity > 100:
                severity = 100
            case['severity'] = severity

        if status is not None:
            case['statusId'] = self.case_status_name_to_id(status)

        if assignee is not None:
            case['assignedTo'] = self.user_name_to_id(assignee)

        if organization is not None:
            case['orgId'] = self.organization_name_to_id(organization)

        # due to error 400 from api - java.util.ArrayList` out of VALUE_STRING
        del case['notes']
        del case['history']

        cmd = 'caseEditCase'
        query = json.dumps({'caseDetail': case})
        self.cmdquery(cmd, query, no_answer=True)

        return

    def get_case_event_list(self, event_ids):

        event_ids = {'list': event_ids}

        cmd = 'caseGetCaseEventsDetail'
        query = json.dumps({'eventIds': event_ids})
        res = self.cmdquery(cmd, query)

        return res


@logger
def alarms_to_entry(alarms):
    if not alarms:
        return "No alarms were found"

    context = {'Alarm(val.ID && val.ID == obj.ID)': alarms}
    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': alarms,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd('Result:', alarms),
        'EntryContext': context
    }


@logger
def alarms_to_incidents(alarms):
    incidents = []
    for alarm in alarms:
        incidents.append({
            'name': alarm['summary'].encode('utf-8'),
            'details': 'Alarm {} , ID : {} , was triggered by condition type: {}'.format(
                alarm['alarmName'].encode('utf-8'),
                alarm['ID'],
                alarm['conditionType']),
            'severity': severity_to_level(alarm['severity']),
            'rawJSON': json.dumps(alarm)
        })
    return incidents


@logger
def cases_to_entry(esm, title, cases):
    if not cases:
        return 'No cases were found'

    headers = ['ID', 'Summary', 'Status', 'Severity', 'OpenTime']
    fixed_cases = []
    context_cases = []
    for case in cases:
        fixed_case = {
            'ID': case['id']['value'],
            'Summary': case['summary'],
            'Status': esm.case_status_id_to_name(
                case['statusId']['value'] if type(case['statusId']) is dict else case['statusId']),
            'OpenTime': case['openTime'],
            'Severity': case['severity']
        }

        if 'assignedTo' in case:
            fixed_case['Assignee'] = esm.user_id_to_name(case['assignedTo'])
            headers.append('Assignee')

        if 'orgId' in case:
            fixed_case['Organization'] = esm.organization_id_to_name(case['orgId'])
            headers.append('Organization')

        context_case = fixed_case.copy()
        if 'eventList' in case:
            fixed_case['Event List'] = json.dumps(case['eventList'])
            context_case['EventList'] = case['eventList']
            headers.append('Event List')

        if 'notes' in case:
            fixed_case['Notes'] = json.dumps(case['notes'])
            context_case['Notes'] = case['notes']
            headers.append('Notes')

        fixed_cases.append(fixed_case)
        context_cases.append(context_case)

    context = {'Case(val.ID && val.ID == obj.ID)': context_cases}

    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': cases,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd(title, fixed_cases, headers),
        'EntryContext': context
    }


@logger
def cases_to_incidents(cases):
    incidents = []
    for case in cases:
        incident = {
            'name': case['summary'].encode('utf-8'),
            'details': 'Case %s with ID %s was triggerred' % (case['summary'].encode('utf-8'), case['id']['value']),
            'severity': severity_to_level(case['severity']),
            'rawJSON': json.dumps(case),
        }
        incidents.append(incident)

    return incidents


@logger
def case_statuses_to_entry(case_statuses):
    if not case_statuses:
        return 'No case statuses were found'

    headers = ['ID', 'Name', 'Is Default', 'Show In Case Pane']
    fixed_statuses = []
    for status in case_statuses:
        fixed_statuses.append({
            'ID': status['id'],
            'Name': status['name'],
            'Is Default': status['default'],
            'Show In Case Pane': status['showInCasePane']
        })

    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': fixed_statuses,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd('Result:', fixed_statuses, headers),
        'EntryContext': {}
    }


def organizations_to_entry(organizations):
    if not organizations:
        return 'No organizations were found'

    headers = ['ID', 'Name']
    fixed_organizations = []
    for organization in organizations:
        fixed_organizations.append({
            'ID': organization['id'],
            'Name': organization['name'],
        })

    context = {'Organizations(val.ID && val.ID == obj.ID)': fixed_organizations}
    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': fixed_organizations,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd('Organizations:', fixed_organizations, headers),
        'EntryContext': context
    }


def case_events_to_entry(events):
    if not events:
        return 'No events were found'

    headers = ['ID', 'LastTime', 'Message']
    fixed_events = []
    for event in events:
        fixed_events.append({
            'ID': event['id']['value'],
            'LastTime': event['lastTime'],
            'Message': event['message'],
        })

    context = {'CaseEvents(val.ID && val.ID == obj.ID)': fixed_events}
    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': fixed_events,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd('Case Events:', fixed_events, headers),
        'EntryContext': context
    }


def alarm_events_to_entry(esm, events):
    headers = ['ID', 'SubType', 'Severity', 'Message', 'LastTime', 'SrcIP', 'SrcPort', 'DstIP', 'DstPort', ]
    fixed_events = []
    for raw_event in events:
        # there are two type of event objects representation:
        # 1) the result of esm-list-alarm-events
        # 2) the result of esm-get-alarm-event-details
        # therefore, first try to get the field of the first option and fallback to the second option.
        cases = [{
            'ID': case['id'],
            'OpenTime': case['openTime'],
            'Severity': case['severity'],
            'Summary': case['summary'],
            'Status': esm.case_status_id_to_name(case['statusId']['value'])
        } for case in raw_event.get('cases', [])]
        event = {
            'ID': raw_event.get('eventId', '%s|%s' % (raw_event.get('ipsId', ''), raw_event.get('alertId', ''))),
            'SubType': raw_event.get('eventSubType', raw_event.get('subtype')),
            'Severity': raw_event['severity'],
            'Cases': cases,
            'Message': raw_event.get('ruleMessage', raw_event.get('ruleName')),
            'NormalizedDescription': raw_event.get('normDesc'),
            'FirstTime': raw_event.get('firstTime'),
            'LastTime': raw_event['lastTime'],

            'SrcMac': raw_event.get('srcMac'),
            'SrcIP': raw_event.get('sourceIp', raw_event.get('srcIp')),
            'SrcPort': raw_event.get('srcPort'),
            'DstMac': raw_event.get('destMac'),
            'DstIP': raw_event['destIp'],
            'DstPort': raw_event.get('destPort'),

            'Raw': raw_event,
        }

        fixed_events.append(event)

    context = {'EsmAlarmEvent(val.ID && val.ID == obj.ID)': createContext(fixed_events, removeNull=True)}
    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': fixed_events,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd('Alarm Events:', fixed_events, headers=headers),
        'EntryContext': context
    }


@logger
def users_to_entry(users):
    # unreachable code - in order to send command, one must be logged in. therefore there is at least one user.
    if not users:
        return 'No users were found'

    headers = ['ID', 'Name', 'Email', 'SMS', 'IsMaster', 'IsAdmin']
    fixed_users = []
    for user in users:
        fixed_users.append({
            'ID': user['id']['value'],
            'Name': user['username'],
            'Email': user['email'],
            'SMS': user['sms'],
            'IsMaster': user['master'],
            'IsAdmin': user['admin'],
        })

    context = {'EsmUser(val.ID && val.ID == obj.ID)': fixed_users}
    return {
        'ContentsFormat': formats['json'],
        'Type': entryTypes['note'],
        'Contents': fixed_users,
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tblToMd('Users:', fixed_users, headers),
        'EntryContext': context
    }


def main():
    esm = NitroESM(ESM_URL, USERNAME, PASSWORD)
    try:
        esm.login()
        final_result = 'No result set'

        if demisto.command() == 'fetch-incidents':
            last_run = demisto.getLastRun()
            demisto.debug('\n\nlast run:\n{}\n'.format(last_run))
            # for backward compatibility uses
            if 'value' in last_run and 'alarms' not in last_run:
                last_run['alarms'] = last_run['value']
            configuration_last_case = int(demisto.params().get('startingCaseID', 0))

            start_alarms = last_run.get('alarms')
            if start_alarms is None:
                start_alarms, _ = parse_date_range(demisto.params()['alarm_fetch_time'],
                                                   date_format='%Y-%m-%dT%H:%M:%S.%f', timezone=TIMEZONE)

            last_case = last_run.get('cases', 0)
            # if last_case < configuration_last_case:
            last_case = max(last_case, configuration_last_case)

            incidents = []  # type: list
            mode = demisto.params().get('fetchTypes', 'alarms').lower()  # alarms is default for backward compatibility

            next_run = None
            if mode in ('alarms', 'both'):
                end = (datetime.now() + timedelta(hours=TIMEZONE)).isoformat()

                demisto.debug("alarms: start - {} , end - {}".format(start_alarms, end))

                alarms = esm.fetch_alarms(
                    'CUSTOM',
                    start_alarms,
                    end,
                    ''
                )
                demisto.debug('alarms found:\n{}\n'.format(alarms))

                incidents = []
                for alarm in alarms:
                    triggered_date = alarm['triggeredDate']
                    if next_run is None or next_run < triggered_date:
                        next_run = triggered_date
                    alarm['events'] = esm.list_alarm_events(alarm['ID'])
                    incidents.append({
                        'name': alarm['summary'],
                        'details': 'Alarm {} , ID : {} , was triggered by condition type: {}'.format(
                            alarm['alarmName'],
                            alarm['ID'],
                            alarm['conditionType']),
                        'severity': severity_to_level(alarm['severity']),
                        'rawJSON': json.dumps(alarm)
                    })

            if mode in ('cases', 'both'):
                # get new cases
                cases = [case for case in esm.get_cases() if case['id']['value'] > last_case]
                cases.sort(key=lambda c: c['id']['value'])
                cases = cases[:MAX_CASES_PER_FETCH]

                if cases:
                    last_case = cases[-1]['id']['value']

                # update last run info
                last_run['cases'] = last_case

                demisto.debug('adding %d more cases, last id is: %d' % (len(cases), last_run['cases'],))
                if cases:
                    incidents.extend(cases_to_incidents(cases))

            if next_run is not None:
                next_run_datetime = datetime.strptime(next_run, parse_time(next_run))
                next_run = (next_run_datetime + timedelta(seconds=1)).isoformat()
            else:
                next_run = start_alarms

            last_run['value'] = next_run
            last_run['alarms'] = next_run

            demisto.incidents(incidents)
            demisto.setLastRun(last_run)
            sys.exit(0)

        elif demisto.command() == 'test-module':
            if VERSION not in ['10.0', '10.1', '10.2', '10.3', '11.1']:
                final_result = 'version must be one of 10.x, got %s' % (VERSION,)
            else:
                esm.fetch_all_fields()
                final_result = 'ok'

        elif demisto.command() == 'esm-fetch-fields':
            res = esm.fetch_all_fields()
            final_result = res

        elif demisto.command() == 'esm-search':
            args = demisto.args()
            res = esm.search(
                demisto.get(args, 'timeRange'),
                demisto.get(args, 'customStart'),
                demisto.get(args, 'customEnd'),
                json.loads(args.get('filters')),
                args.get('fields'),
                demisto.get(args, 'queryType') or 'EVENT',
                demisto.get(args, 'maxWait') or 30
            )
            final_result = res

        elif demisto.command() == 'esm-get-case-list':
            since_date_range = demisto.args().get('since')
            res = esm.get_cases(since_date_range)
            final_result = cases_to_entry(esm, 'All cases:', res)

        elif demisto.command() == 'esm-get-case-detail':
            args = demisto.args()
            case_id = int(demisto.get(args, 'id'))
            res = esm.get_case_detail(case_id)
            final_result = cases_to_entry(esm, 'Case %d:' % (case_id,), [res])

        elif demisto.command() == 'esm-add-case':
            args = demisto.args()
            res = esm.add_case(
                demisto.get(args, 'summary'),
                int(demisto.get(args, 'severity')),
                demisto.get(args, 'status'),
                demisto.get(args, 'assignee'),
                demisto.get(args, 'organization'),
            )
            case = esm.get_case_detail(res)
            final_result = cases_to_entry(esm, 'New Case:', [case])

        elif demisto.command() == 'esm-edit-case':
            args = demisto.args()
            case_id = int(demisto.get(args, 'id'))
            severity = demisto.get(args, 'severity')
            esm.edit_case(
                case_id,
                demisto.get(args, 'summary'),
                int(severity) if severity else None,
                demisto.get(args, 'status'),
                demisto.get(args, 'assignee'),
                demisto.get(args, 'organization'),
            )
            case = esm.get_case_detail(case_id)
            final_result = cases_to_entry(esm, 'Edited Case:', [case])

        elif demisto.command() == 'esm-get-case-statuses':
            res = esm.get_case_statuses()
            final_result = case_statuses_to_entry(res)

        elif demisto.command() == 'esm-add-case-status':
            args = demisto.args()
            res = esm.add_case_status(
                demisto.get(args, 'name'),
                bool(strtobool(demisto.get(args, 'show_in_case_pane'))),
            )
            final_result = res

        elif demisto.command() == 'esm-edit-case-status':
            args = demisto.args()
            should_show = demisto.get(args, 'show_in_case_pane')
            res = esm.edit_case_status(
                demisto.get(args, 'original_name'),
                demisto.get(args, 'new_name'),
                bool(strtobool(should_show)) if should_show else None,
            )
            final_result = res

        elif demisto.command() == 'esm-delete-case-status':
            args = demisto.args()
            res = esm.delete_case_status(
                demisto.get(args, 'name')
            )
            final_result = res

        elif demisto.command() == 'esm-get-case-event-list':
            args = demisto.args()
            event_ids = demisto.get(args, 'ids').split(',')
            res = esm.get_case_event_list(event_ids)
            final_result = case_events_to_entry(res)

        elif demisto.command() == 'esm-get-organization-list':
            res = esm.get_organizations()
            final_result = organizations_to_entry(res)

        elif demisto.command() == 'esm-get-user-list':
            res = esm.get_users()
            final_result = users_to_entry(res)

        elif demisto.command() == 'esm-fetch-alarms':
            args = demisto.args()
            res = esm.fetch_alarms(
                demisto.get(args, 'timeRange'),
                demisto.get(args, 'customStart'),
                demisto.get(args, 'customEnd'),
                demisto.get(args, 'assignedUser')
            )
            final_result = alarms_to_entry(res)

        elif demisto.command() == 'esm-acknowledge-alarms':
            args = demisto.args()
            res = esm.acknowledge_alarms(argToList(demisto.get(args, 'alarmIds')))
            final_result = res

        elif demisto.command() == 'esm-unacknowledge-alarms':
            args = demisto.args()
            res = esm.unacknowledge_alarms(argToList(demisto.get(args, 'alarmIds')))
            final_result = res

        elif demisto.command() == 'esm-delete-alarms':
            args = demisto.args()
            res = esm.delete_alarms(argToList(demisto.get(args, 'alarmIds')))
            final_result = res

        elif demisto.command() == 'esm-get-alarm-event-details':
            args = demisto.args()
            res = esm.get_alarm_event_details(demisto.get(args, 'eventId'))
            final_result = alarm_events_to_entry(esm, [res])

        elif demisto.command() == 'esm-list-alarm-events':
            args = demisto.args()
            res = esm.list_alarm_events(demisto.get(args, 'alarmId'))
            final_result = alarm_events_to_entry(esm, res['events'])
        demisto.results(final_result)

    except Exception as ex:
        demisto.error('#### error in McAfee ESM v10: ' + str(ex))
        if demisto.command() == 'fetch-incidents':
            LOG(traceback.format_exc())
            LOG.print_log()
            raise
        else:
            return_error(str(ex), error=traceback.format_exc())
    finally:
        esm.logout()


# python2 uses __builtin__ python3 uses builtins
if __name__ == "__builtin__" or __name__ == "builtins":
    main()