cymulate_v2

Multi-Vector Cyber Attack, Breach and Attack Simulation.

Deception & Breach Simulation · Cymulate

Details

IDcymulate_v2
ProviderCymulate
CategoryDeception & Breach Simulation
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Multi-Vector Cyber Attack, Breach and Attack Simulation.
This integration was integrated and tested with API version 1 of cymulate

This is the default integration for this content pack when configured by the Data Onboarder in Cortex XSIAM.

Configure cymulate_v2 in Cortex

Parameter Description Required
API token   True
Base URL   False
Fetch incidents   False
Fetch category Choose one or more categories to fetch. False
Incident type   False
First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)   False
Max Fetch Maximal number of incidents to fetch. Max value can be no grater than 35. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

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

Fetch Incidents command

Retrieves new incidents every interval (default is 1 minute).
The fetch incident command will retrieve incidents from all selected modules chosen in the configuration page by the user.
The next run will be calculated by the latest timestamp of all modules, to avoid duplications.
NOTE: We fetch only one module per fetch call.

cymulate-exfiltration-template-list


Retrieve the exfiltration template list.

Base Command

cymulate-exfiltration-template-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.Exfiltration.Template.id String Template ID.
Cymulate.Exfiltration.Template.name String Template name.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "Exfiltration": {
            "Templates": [
                {
                    "id": "5df0e79b85a00138dc648e75",
                    "name": "Cymulate Best Practice"
                },
                {
                    "id": "5df0e7d585a00138dc648e8f",
                    "name": "Cloud Services"
                },
                {
                    "id": "5df0e80885a00138dc648ea7",
                    "name": "Network Protocols"
                },
                {
                    "id": "5df0e82e85a00138dc648ebb",
                    "name": "Email"
                },
                {
                    "id": "5df25b3696fa2af420a379b9",
                    "name": "Physical"
                }
            ]
        }
    }
}

Human Readable Output

Exfiltration templates list

id name
5df0e79b85a00138dc648e75 Cymulate Best Practice
5df0e7d585a00138dc648e8f Cloud Services
5df0e80885a00138dc648ea7 Network Protocols
5df0e82e85a00138dc648ebb Email
5df25b3696fa2af420a379b9 Physical

cymulate-exfiltration-start


Create a new exfiltration assessment.

Base Command

cymulate-exfiltration-start

Input

Argument Name Description Required
template_id The ID of the template to run the exfiltration Assessment with. Can be retrieved using Cymulate’s UI, or using cymulate-exfiltration-template-list command. Required
agent_name agent name to run simulation attacks. Required
schedule whether to schedule the automated assessment periodically. Possible values are: true, false. Required
schedule_loop Loop size of the scheduled agent. For example: to run the agent only once, use the value ‘one-time’. Possible values are: one-time, daily, weekly, monthly. Required
agent_profile_name agent profile name to run simulation attacks. Optional

Context Output

Path Type Description
Cymulate.Exfiltration.id String New exfiltration assessment creation ID.
Cymulate.Exfiltration.success Boolean New exfiltration assessment creation success status.

Command Example

!cymulate-exfiltration-start template_id="5df0e79b85a00138dc648e75" agent_name="Cymulate_agent" schedule="false" schedule_loop="one-time"

Context Example

{
    "Cymulate": {
        "Exfiltration": {
            "id": "id_1",
            "success": true
        }
    }
}

Human Readable Output

Starting exfiltration assessment

id success
id_1 true

cymulate-exfiltration-stop


Stop a running exfiltration assessment.

Base Command

cymulate-exfiltration-stop

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.Exfiltration.data String New exfiltration assessment stopping data.
Cymulate.Exfiltration.success Boolean New exfiltration assessment stopping success status.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "Exfiltration": {
            "data": "ok",
            "success": true
        }
    }
}

Human Readable Output

Stopping exfiltration assessment

data success
ok true

cymulate-exfiltration-status


Get exfiltration assessment status.

Base Command

cymulate-exfiltration-status

Input

Argument Name Description Required
assessment_id Assessment ID. Required

Context Output

Path Type Description
Cymulate.Exfiltration.id String New exfiltration assessment stop ID.
Cymulate.Exfiltration.inProgress Boolean Whether the assessment is in progress.
Cymulate.Exfiltration.progress Number Percentage of the progress of the assessment.
Cymulate.Exfiltration.categories String Categories.

Command Example

!cymulate-exfiltration-status assessment_id="id_2"

Context Example

{
    "Cymulate": {
        "Exfiltration": {
            "categories": [
                "http",
                "https",
                "dns",
                "dns-tunneling",
                "icmp",
                "outlook",
                "device",
                "telnet",
                "sftp",
                "slack",
                "googledrive",
                "onedrive",
                "port_scanning",
                "msteams",
                "gmail",
                "gitlab",
                "azure_blob",
                "aws_s3_bucket",
                "github",
                "googlestorage",
                "browsinghttps",
                "browsinghttp"
            ],
            "id": "id_2",
            "inProgress": false,
            "progress": 0
        }
    }
}

Human Readable Output

Exfiltration assessment status

categories id inProgress progress
http,
https,
dns,
dns-tunneling,
icmp,
outlook,
device,
telnet,
sftp,
slack,
googledrive,
onedrive,
port_scanning,
msteams,
gmail,
gitlab,
azure_blob,
aws_s3_bucket,
github,
googlestorage,
browsinghttps,
browsinghttp
id_2 false 0

cymulate-email-gateway-template-list


Retrieve the email gateway template list.

Base Command

cymulate-email-gateway-template-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.EmailGateway.Template.id String Template ID.
Cymulate.EmailGateway.Template.name String Template name.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "EmailGateway": {
            "Templates": [
                {
                    "id": "5c6920853659191ccf6858fc",
                    "name": "free assessment"
                },
                {
                    "id": "5c6920853659191ccf6858fb",
                    "name": "cymulate best practice"
                },
                {
                    "id": "5db5ab6e79a0bf2feedaf9a7",
                    "name": "cymulate best practice - high risk"
                },
                {
                    "id": "5c6968e43659191ccf685929",
                    "name": "office payloads"
                },
                {
                    "id": "5c6968ec3659191ccf68592a",
                    "name": "executables payloads"
                },
                {
                    "id": "5c73b2ce3febfc300976c6e3",
                    "name": "exploits"
                },
                {
                    "id": "5c7f96963febfc300976c7be",
                    "name": "malwares"
                },
                {
                    "id": "5c7f977bc9545f79ea8b03c0",
                    "name": "ransomwares"
                }
            ]
        }
    }
}

Human Readable Output

Email gateway templates list

id name
5c6920853659191ccf6858fc free assessment
5c6920853659191ccf6858fb cymulate best practice
5db5ab6e79a0bf2feedaf9a7 cymulate best practice - high risk
5c6968e43659191ccf685929 office payloads
5c6968ec3659191ccf68592a executables payloads
5c73b2ce3febfc300976c6e3 exploits
5c7f96963febfc300976c7be malwares
5c7f977bc9545f79ea8b03c0 ransomwares

cymulate-email-gateway-start


Create a new email gateway assessment.

Base Command

cymulate-email-gateway-start

Input

Argument Name Description Required
template_id The ID of the template to run the email gateway assessment with. Can be retrieved using Cymulate’s UI, or using cymulate-email-gateway-template-list command. Required
agent_email agent email. Required
schedule whether to schedule the automated assessment periodically. Possible values are: true, false. Required
schedule_loop Loop size of the scheduled agent. For example: to run the agent only once, use the value ‘one-time’. Possible values are: one-time, daily, weekly, monthly. Required

Context Output

Path Type Description
Cymulate.EmailGateway.id String New email gateway assessment creation ID.
Cymulate.EmailGateway.success Boolean New email gateway assessment creation success status.

Command Example

!cymulate-email-gateway-start template_id="5c6920853659191ccf6858fc" agent_email="test@cymulate.com" schedule="false" schedule_loop="one-time"

Context Example

{
    "Cymulate": {
        "EmailGateway": {
            "id": "id_3",
            "success": true
        }
    }
}

Human Readable Output

Starting email gateway assessment

id success
id_3 true

cymulate-email-gateway-stop


Stop a running exfiltration assessment.

Base Command

cymulate-email-gateway-stop

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.EmailGateway.data String Email gateway assessment stopping data.
Cymulate.EmailGateway.success Boolean Email gateway assessment stopping success status.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "EmailGateway": {
            "data": "ok",
            "success": true
        }
    }
}

Human Readable Output

Stopping email gateway assessment

data success
ok true

cymulate-email-gateway-status


Get the email gateway assessment status.

Base Command

cymulate-email-gateway-status

Input

Argument Name Description Required
assessment_id Assessment ID. Required

Context Output

Path Type Description
Cymulate.EmailGateway.id String Email gateway assessment ID.
Cymulate.EmailGateway.success Boolean Whether the assessment was successful.
Cymulate.EmailGateway.inProgress Boolean Whether the assessment is in progress.
Cymulate.EmailGateway.progress Number Percentage of the progress of the assessment.
Cymulate.EmailGateway.addresses String Addresses connected to the assessment.

Command Example

!cymulate-email-gateway-status assessment_id="id_4"

Context Example

{
    "Cymulate": {
        "EmailGateway": {
            "addresses": [
                "test@cymulate.com"
            ],
            "categories": [
                "worm"
            ],
            "id": "id_4",
            "inProgress": false,
            "progress": 0
        }
    }
}

Human Readable Output

Email gateway assessment status

addresses categories id inProgress progress
test@cymulate.com worm id_4 false 0

cymulate-endpoint-security-template-list


Retrieve the endpoint security template list.

Base Command

cymulate-endpoint-security-template-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.EndpointSecurity.Template.id String Template ID.
Cymulate.EndpointSecurity.Template.name String Template name.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "EndpointSecurity": {
            "Templates": [
                {
                    "id": "5c87a26f548a3c7c4c184a5e",
                    "name": "Free Assessment"
                },
                {
                    "id": "5c97a50c5727c58a295d0459",
                    "name": "Cymulate Best Practice"
                },
                {
                    "id": "5e98461d312a740ee4839700",
                    "name": "DLL Side loading"
                },
                {
                    "id": "5c87a314548a3c7c4c184a5f",
                    "name": "Cymulate Behavior-based"
                },
                {
                    "id": "5c87a314548a3c7c4c184a60",
                    "name": "Cymulate Signature-based"
                }
            ]
        }
    }
}

Human Readable Output

Endpoint security templates list

id name
5c87a26f548a3c7c4c184a5e Free Assessment
5c97a50c5727c58a295d0459 Cymulate Best Practice
5e98461d312a740ee4839700 DLL Side loading
5c87a314548a3c7c4c184a5f Cymulate Behavior-based
5c87a314548a3c7c4c184a60 Cymulate Signature-based

cymulate-endpoint-security-start


Create a new endpoint security assessment.

Base Command

cymulate-endpoint-security-start

Input

Argument Name Description Required
template_id The ID of the template to run the endpoint security assessment with. Can be retrieved using Cymulate’s UI, or using cymulate-endpoint-security-template-list command. Required
agent_name agent name. Required
schedule whether to schedule the automated assessment periodically. Possible values are: true, false. Required
schedule_loop Loop size of the scheduled agent. For example: to run the agent only once, use the value ‘one-time’. Possible values are: one-time, daily, weekly, monthly. Required
agent_profile_name Agent profile name. Optional

Context Output

Path Type Description
Cymulate.EndpointSecurity.id String New endpoint security assessment creation ID.
Cymulate.EndpointSecurity.success Boolean New endpoint security assessment creation success status.

Command Example

!cymulate-endpoint-security-start template_id="5e98461d312a740ee4839700" agent_name="Cymulate_agent" schedule="false" schedule_loop="one-time"

Context Example

{
    "Cymulate": {
        "EndpointSecurity": {
            "id": "id_5",
            "success": true
        }
    }
}

Human Readable Output

Starting endpoint security assessment

id success
id_5 true

cymulate-endpoint-security-stop


Stop a running endpoint security assessment.

Base Command

cymulate-endpoint-security-stop

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.EndpointSecurity.data String Endpoint security assessment stopping data.
Cymulate.EndpointSecurity.success Boolean Endpoint Security assessment stopping success status.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "EndpointSecurity": {
            "data": "ok",
            "success": true
        }
    }
}

Human Readable Output

Stopping endpoint security assessment

data success
ok true

cymulate-endpoint-security-status


Get the endpoint security assessment status.

Base Command

cymulate-endpoint-security-status

Input

Argument Name Description Required
assessment_id Assessment ID. Required

Context Output

Path Type Description
Cymulate.EndpointSecurity.id String Endpoint security assessment ID.
Cymulate.EndpointSecurity.inProgress Boolean Whether the assessment is in progress.
Cymulate.EndpointSecurity.progress Number Percentage of the progress of the assessment.
Cymulate.EndpointSecurity.categories String Assessment categories.

Command Example

!cymulate-endpoint-security-status assessment_id="id_6"

Context Example

{
    "Cymulate": {
        "EndpointSecurity": {
            "categories": [
                "ransomware"
            ],
            "id": "id_6",
            "inProgress": true,
            "progress": 90
        }
    }
}

Human Readable Output

Endpoint security assessment status

categories id inProgress progress
ransomware id_6 false 0

cymulate-waf-template-list


Retrieve the WAF template list.

Base Command

cymulate-waf-template-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.WAF.Template.id String Template ID.
Cymulate.WAF.Template.name String Template name.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "WAF": {
            "Templates": [
                {
                    "id": "5edf7ddfef621bbc252498f3",
                    "name": "free assessment"
                },
                {
                    "id": "5edf7547ef621bbc25248d97",
                    "name": "Cymulate Best Practice"
                },
                {
                    "id": "5ee0726cef621bbc25251d7a",
                    "name": "SQL Injection"
                },
                {
                    "id": "5ee0726cef621bbc25251d7d",
                    "name": "XSS"
                },
                {
                    "id": "5eea1fb754b285889325b818",
                    "name": "Command Injection"
                },
                {
                    "id": "5eea1fb754b285889325b81b",
                    "name": "File Inclusion"
                },
                {
                    "id": "5fb65327f6ce656dbc7f9cf1",
                    "name": "SSRF"
                },
                {
                    "id": "600d258cbd15e73c5882b306",
                    "name": "david test"
                }
            ]
        }
    }
}

Human Readable Output

WAF templates list

id name
5edf7ddfef621bbc252498f3 free assessment
5edf7547ef621bbc25248d97 Cymulate Best Practice
5ee0726cef621bbc25251d7a SQL Injection
5ee0726cef621bbc25251d7d XSS
5eea1fb754b285889325b818 Command Injection
5eea1fb754b285889325b81b File Inclusion
5fb65327f6ce656dbc7f9cf1 SSRF
600d258cbd15e73c5882b306 david test

cymulate-waf-start


Create a new web application firewall assessment.

Base Command

cymulate-waf-start

Input

Argument Name Description Required
template_id The ID of the template to run the WAF assessment with. Can be retrieved using Cymulate’s UI, or using cymulate-waf-template-list command. Required
sites Websites to run the assessment on. Can be a single website URL or a list of URLs. Required
schedule whether to schedule the automated assessment periodically. Possible values are: true, false. Required
schedule_loop Loop size of the scheduled agent. For example: to run the agent only once, use the value ‘one-time’. Possible values are: one-time, daily, weekly, monthly. Required

Context Output

Path Type Description
Cymulate.WAF.id String Web application firewall assessment creation ID.
Cymulate.WAF.success Boolean Web application firewall assessment creation success status.

Command Example

!cymulate-waf-start template_id="5ee0726cef621bbc25251d7a" sites="http://cymulatelabs.com" schedule="false" schedule_loop="one-time"

Context Example

{
    "Cymulate": {
        "WAF": {
            "id": "604630cbb9eb930a0fa86ab5",
            "success": true
        }
    }
}

Human Readable Output

Starting WAF assessment

id success
604630cbb9eb930a0fa86ab5 true

cymulate-waf-stop


Stop a running web application firewall assessment.

Base Command

cymulate-waf-stop

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.WAF.data String Web application firewall assessment stopping data.
Cymulate.WAF.success Boolean Web application firewall assessment stopping success status.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "WAF": {
            "data": "no running attack",
            "success": true
        }
    }
}

Human Readable Output

Stopping WAF assessment

data success
no running attack true

cymulate-waf-status


Get the web application firewall assessment status.

Base Command

cymulate-waf-status

Input

Argument Name Description Required
assessment_id Assessment ID. Required

Context Output

Path Type Description
Cymulate.WAF.id String Web application firewall assessment ID.
Cymulate.WAF.inProgress Boolean Whether the assessment is in progress.
Cymulate.WAF.progress Number Percentage of the progress of the assessment.
Cymulate.WAF.categories String Assessment categories.

Command Example

!cymulate-waf-status assessment_id="5ff31ef451647c20338bd235"

Context Example

{
    "Cymulate": {
        "WAF": {
            "categories": [
                "XML Injection",
                "Command Injection",
                "File Inclusion",
                "XSS",
                "XML Injection",
                "SQL Injection"
            ],
            "id": "5ff31ef451647c20338bd235",
            "inProgress": false,
            "progress": 0
        }
    }
}

Human Readable Output

WAF assessment status

categories id inProgress progress
XML Injection,
Command Injection,
File Inclusion,
XSS,
XML Injection,
SQL Injection
5ff31ef451647c20338bd235 false 0

cymulate-immediate-threat-start


Create a new immediate threats assessment.

Base Command

cymulate-immediate-threat-start

Input

Argument Name Description Required
browsing_address Browsing address. Optional
mail_address Agent email address. Optional
edr_address EDR address. Optional
template_id The ID of the template to run the immediate threat assessment with. Can be retrieved using Cymulate’s UI. Required
browsing_address_profile_name Browsing Agent profile name to run the assessment with. Optional
edr_address_profile_name EDR Agent profile name to run the assessment with. Optional

Context Output

Path Type Description
Cymulate.ImmediateThreats.id String Immediate threats assessment creation ID.
Cymulate.ImmediateThreats.success String New exfiltration assessment creation success status.

Command Example

!cymulate-immediate-threat-start edr_address="Cymulate_agent" template_id="603270ce63aa15930631b938"

Context Example

{
    "Cymulate": {
        "ImmediateThreats": {
            "id": [
                "id_7"
            ],
            "success": true
        }
    }
}

Human Readable Output

Starting immediate-threats assessment

id success
id_7 true

cymulate-immediate-threat-stop


Stop a running immediate threats assessment.

Base Command

cymulate-immediate-threat-stop

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.ImmediateThreats.data String Immediate threats assessment stopping data.
Cymulate.ImmediateThreats.success String Immediate threats assessment stopping success status.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "ImmediateThreats": {
            "data": "ok",
            "success": true
        }
    }
}

Human Readable Output

Stop immediate-threats assessment

data success
ok true

cymulate-immediate-threat-status


Get immediate threats assessment status.

Base Command

cymulate-immediate-threat-status

Input

Argument Name Description Required
assessment_id Assessment ID. Required

Context Output

Path Type Description
Cymulate.ImmediateThreats.id String Web application firewall assessment ID.
Cymulate.ImmediateThreats.categories String Assessment categories.
Cymulate.ImmediateThreats.inProgress Boolean Whether the assessment is in progress.
Cymulate.ImmediateThreats.progress Number Percentage of the progress of the assessment.

Command Example

!cymulate-immediate-threat-status assessment_id="id_8"

Context Example

{
    "Cymulate": {
        "ImmediateThreats": {
            "categories": [
                "antivirus"
            ],
            "id": "id_8",
            "inProgress": true,
            "progress": 90
        }
    }
}

Human Readable Output

Immediate-threats assessment status

categories id inProgress progress
antivirus id_8 true 90

cymulate-phishing-awareness-contacts-group-list


Get a list of contact groups.

Base Command

cymulate-phishing-awareness-contacts-group-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.Phishing.Groups.id String The ID of the phishing content group.
Cymulate.Phishing.Groups.name String Name of the phishing content group.
Cymulate.Phishing.Groups.client String The client of the phishing content group.
Cymulate.Phishing.Groups.canDelete Boolean Whether this group can be deleted.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "Phishing": {
            "Groups": [
                {
                    "__v": 0,
                    "canDelete": true,
                    "client": "Cymulate",
                    "id": "id_a",
                    "name": "qmasters_01"
                },
                {
                    "__v": 0,
                    "canDelete": true,
                    "client": "Cymulate",
                    "id": "id_b",
                    "name": "qmasters_02"
                },
                {
                    "__v": 0,
                    "canDelete": true,
                    "client": "Cymulate",
                    "id": "id_c",
                    "name": "qmasters_03"
                },
                {
                    "__v": 0,
                    "canDelete": true,
                    "client": "Cymulate",
                    "id": "id_d",
                    "name": "new_group_01"
                },
                {
                    "__v": 0,
                    "canDelete": true,
                    "client": "Cymulate",
                    "id": "id_e",
                    "name": "test_group_02"
                }
            ]
        }
    }
}

Human Readable Output

Phishing awareness contact groups

__v canDelete client id name
0 true Cymulate id_a qmasters_01
0 true Cymulate id_b qmasters_02
0 true Cymulate id_c qmasters_03
0 true Cymulate id_d new_group_01
0 true Cymulate id_e test_group_02

cymulate-phishing-awareness-contacts-group-create


Create new contacts group.

Base Command

cymulate-phishing-awareness-contacts-group-create

Input

Argument Name Description Required
group_name The name of the new group to create. Required

Context Output

Path Type Description
Cymulate.Phishing.Groups.success Boolean Whether the creation of the new group was successful.
Cymulate.Phishing.Groups.id String The ID of the new phishing content group.

Command Example

!cymulate-phishing-awareness-contacts-group-create group_name="test_group_01"

Context Example

{
    "Cymulate": {
        "Phishing": {
            "Groups": {
                "id": "id_9",
                "success": true
            }
        }
    }
}

Human Readable Output

Phishing awareness contact group created

id success
id_9 true

cymulate-phishing-awareness-contacts-get


Get contacts group using a group ID.

Base Command

cymulate-phishing-awareness-contacts-get

Input

Argument Name Description Required
group_id Group ID. Required

Context Output

Path Type Description
Cymulate.Phishing.Groups.id String The ID of the phishing content group.
Cymulate.Phishing.Groups.name String Name of the phishing content group.
Cymulate.Phishing.Groups.client String The client of the phishing content group.
Cymulate.Phishing.Groups.canDelete Boolean Whether this group can be deleted.

Command Example

!cymulate-phishing-awareness-contacts-get group_id="id_abcd"

Context Example

{
    "Cymulate": {
        "Phishing": {
            "Groups": [
                {
                    "address": "jamesb@cymulate.com",
                    "color": "#ffbb00",
                    "firstName": "James",
                    "id": "id_1a",
                    "lastName": "Bond"
                },
                {
                    "address": "Billg@cymulate.com",
                    "color": "#34a853",
                    "firstName": "Bill",
                    "id": "id_a2",
                    "lastName": "Gates"
                },
                {
                    "address": "davidb@cymulate.com",
                    "color": "#00a1f1",
                    "firstName": "David ",
                    "id": "id_a3",
                    "lastName": "Ben-Gurion"
                }
            ]
        }
    }
}

Human Readable Output

Phishing awareness contact groups

address color firstName id lastName
jamesb@cymulate.com #ffbb00 James id_1a Bond
Billg@cymulate.com #34a853 Bill id_a2 Gates
davidb@cymulate.com #00a1f1 David id_a3 Ben-Gurion

cymulate-lateral-movement-template-list


Retrieve lateral movement template list.

Base Command

cymulate-lateral-movement-template-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.LateralMovement.Template.id String Template ID.
Cymulate.LateralMovement.Template.name String Template name.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "LateralMovement": {
            "Templates": [
                {
                    "id": "5e2f0c1054d53d6b115eefa7",
                    "name": "SMB Pass The Hash"
                },
                {
                    "id": "5e2f0c5f54d53d6b115ef0a1",
                    "name": "Kerberoasting and Cracking on DCOM and WMI"
                },
                {
                    "id": "5e2f0c9754d53d6b115ef190",
                    "name": "LLMNR Poisoning on SMB"
                },
                {
                    "id": "5e2f0d2c54d53d6b115ef345",
                    "name": "SMB And Credentials Harvesting"
                },
                {
                    "id": "5e41746171895006ef394607",
                    "name": "test1"
                },
                {
                    "id": "5e44020d3f46e106e9ec706c",
                    "name": "Prueba completa"
                },
                {
                    "id": "5e4a5792b1bdb606ed1f9407",
                    "name": "lab1"
                }
            ]
        }
    }
}

Human Readable Output

Lateral movement templates list

id name
5e2f0c1054d53d6b115eefa7 SMB Pass The Hash
5e2f0c5f54d53d6b115ef0a1 Kerberoasting and Cracking on DCOM and WMI
5e2f0c9754d53d6b115ef190 LLMNR Poisoning on SMB
5e2f0d2c54d53d6b115ef345 SMB And Credentials Harvesting
5e41746171895006ef394607 test1
5e44020d3f46e106e9ec706c Prueba completa
5e4a5792b1bdb606ed1f9407 lab1

cymulate-lateral-movement-start


Create a new lateral movement assessment.

Base Command

cymulate-lateral-movement-start

Input

Argument Name Description Required
agent_name Agent name to run the assessment with. Required
template_id The ID of the template to run the lateral movement with. Can be retrieved using Cymulate’s UI, or using cymulate-lateral-movement-template-list command. Required
upload_to_cymulate Whether to upload the result to Cymulate. Possible values are: true, false. Default is false. Required
schedule Whether to schedule the automated assessment periodically. Possible values are: true, false. Required
schedule_loop Loop size of the scheduled agent. For example: to run the agent only once, use the value ‘one-time’. Possible values are: one-time, daily, weekly, monthly. Required
agent_profile_name Agent profile name to run the assessment with. Optional

Context Output

Path Type Description
Cymulate.LateralMovement.id String Lateral movement assessment creation ID.
Cymulate.LateralMovement.success Boolean New exfiltration assessment creation success status.

Command Example

!cymulate-lateral-movement-start agent_name="Cymulate_agent" template_id="5e41746171895006ef394607" upload_to_cymulate="false" schedule="false" schedule_loop="one-time"

Context Example

{
    "Cymulate": {
        "LateralMovement": {
            "id": "id_987",
            "success": true
        }
    }
}

Human Readable Output

Starting lateral movement assessment

id success
id_987 true

cymulate-lateral-movement-stop


Stop a running lateral movement assessment.

Base Command

cymulate-lateral-movement-stop

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.LateralMovement.data String Lateral movement assessment stopping data.
Cymulate.LateralMovement.success Boolean Lateral Movement assessment creation success status.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "LateralMovement": {
            "data": "ok",
            "success": true
        }
    }
}

Human Readable Output

Stopping lateral movement assessment

data success
ok true

cymulate-lateral-movement-status


Get lateral movement assessment status.

Base Command

cymulate-lateral-movement-status

Input

Argument Name Description Required
assessment_id Assessment ID. Required

Context Output

Path Type Description
Cymulate.LateralMovement.id String Lateral movement assessment ID.
Cymulate.LateralMovement.inProgress Boolean Indicates whether the assessment is in progress.

Command Example

!cymulate-lateral-movement-status assessment_id="id_876"

Context Example

{
    "Cymulate": {
        "LateralMovement": {
            "id": "id_876",
            "inProgress": false
        }
    }
}

Human Readable Output

Lateral movement assessment status

id inProgress
id_876 false

cymulate-agent-list


Retrieve all agents.

Base Command

cymulate-agent-list

Input

There are no input arguments for this command.

Context Output

Path Type Description
Cymulate.Agent.agentAddress String The agent’s address.
Cymulate.Agent.agentMethod String The agent’s methods.
Cymulate.Agent.agentName String Agent name.
Cymulate.Agent.comment String Comments.

Command Example


#### Context Example

```json
{
    "Cymulate": {
        "Agent": [
            {
                "agentAddress": "test@cymulate.com",
                "agentMethod": "smtp",
                "comment": ""
            },
            {
                "agentAddress": "Cymulate_agent ",
                "agentMethod": "http",
                "agentName": "Cymulate_agent",
                "comment": ""
            }
        ]
    }
}

Human Readable Output

Agents list

agentAddress agentMethod agentName
test@cymulate.com smtp  
Cymulate_agent http Cymulate_agent

cymulate-simulations-list


Retrieve a list of all simulations by ID.

Base Command

cymulate-simulations-list

Input

Argument Name Description Required
module Module to retrieve events to. Possible values are: web-gateway, exfiltration, email-gateway, endpoint-security, waf, kill-chain, immediate-threats, phishing-awareness, lateral-movement. Required
attack_id Attack ID. Can be retrieved using cymulate-simulations-id-list command. Required

Context Output

Path Type Description
Cymulate.Simulations.Attack_Type String Attack payload
Cymulate.Simulations.Classification String Attack classification.
Cymulate.Simulations.Content_Type String Content type.
Cymulate.Simulations.Module String Event’s module.
Cymulate.Simulations.Phrase String Attack description.
Cymulate.Simulations.Phrase_Title String Attack name.
Cymulate.Simulations.Status String Attack status
Cymulate.Simulations.PrevStatus String Attack Previous status
Cymulate.Simulations.Risk String Attack risk level.
Cymulate.Simulations.Source String Attack Source
Cymulate.Simulations.User String User committed the attack ot was attacked.
Cymulate.Simulations.Attack_Vector String Attack vector
Cymulate.Simulations.Source_Email_Address String Source email address.
Cymulate.Simulations.Md5 String MD5 attached to the attack.
Cymulate.Simulations.Sha256 String Sha256 attached to the attack.
Cymulate.Simulations.Sha1 String Sha1 attached to the attack.
Cymulate.Simulations.Mitigation String Mitigation details.
Cymulate.Simulations.Mitigation_Details String Mitigation details.
Cymulate.Simulations.Description String Attack description
Cymulate.Simulations.Id String Attack ID.

Command Example

!cymulate-simulations-list module="waf" attack_id="id_001"

Context Example

{
    "Cymulate": {
        "Simulations": {
            "Action": " http://Google.com/",
            "Category": "SQL Injection",
            "Database": "DB Agnostic",
            "Display_Url": " http://Google.com/",
            "FullRequest": "N/A",
            "Id": "id_001",
            "Input": "password",
            "Method": "post",
            "Mitigation": "Create a WAF Security rule to block incoming requests that contains. Validate that the specific input url is protected with the MSSQL Blind signature pack (SQL Injection)",
            "Module": "Web Application Firewall",
            "Payload": "This is a payload",
            "Platform": "OS Agnostic",
            "PrevStatus": "blocked",
            "Risk": "high",
            "Source": " http://Google.com",
            "Status": "blocked",
            "SubCategoryType": "MSSQL Blind",
            "Timestamp": "2021-02-28 16:33:41",
            "Url": " http://Google.com/",
            "date": "2021-02-28T14:33:41.559Z"
        }
    }
}

Human Readable Output

Displaying 20/193 simulations

Action Category Database Display_Url FullRequest Id Input Method Mitigation Module Payload Platform PrevStatus Risk Source Status SubCategoryType Timestamp Url date
http://Google.com/signup SQL Injection DB Agnostic http://Google.com/signup N/A id_001 password post Create a WAF Security rule to block incoming requests that contains. Validate that the specific input/url is protected with the Oracle SQL Injection signature pack (SQL Injection) Web Application Firewall AND 1=utl_inaddr.get_host_address((SELECT DISTINCT(table_name) FROM (SELECT DISTINCT(table_name), ROWNUM AS LIMIT FROM sys.all_tables) WHERE LIMIT=3)) AND ‘i’=’i OS Agnostic blocked high http://Google.com blocked Oracle SQL Injection 2021-02-28 16:33:41 http://Google.com/signup 2021-02-28T14:33:41.475Z
http://Google.com/team/dudi SQL Injection DB Agnostic http://Google.com/team/dudi N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.. The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input url is protected with the Generic Blind Injection signature pack (SQL Injection) Web Application Firewall 1) or benchmark(10000000,MD5(1))# OS Agnostic blocked high http://Google.com blocked Generic Blind Injection 2021-02-28 16:33:41 http://Google.com/team/dudi 2021-02-28T14:33:41.476Z
http://Google.com/ SQL Injection DB Agnostic http://Google.com/ N/A id_001 tel post Create a WAF Security rule to block incoming requests that contains:1.The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the General SQL Injection signature pack (SQL Injection) Web Application Firewall 1’ and non_existant_table = ‘1 OS Agnostic blocked high http://Google.com blocked General SQL Injection 2021-02-28 16:33:41 http://Google.com/ 2021-02-28T14:33:41.478Z
http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 SQL Injection DB Agnostic http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 N/A id_001 password post Create a WAF Security rule to block incoming requests that contains:..The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the Passive SQL Injection signature pack (SQL Injection) Web Application Firewall ’ or 1=1 / OS Agnostic blocked high http://Google.com blocked Passive SQL Injection 2021-02-28 16:33:41 http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 2021-02-28T14:33:41.478Z
http://Google.com/contact SQL Injection DB Agnostic http://Google.com/contact N/A id_001 password post Create a WAF Security rule to block incoming requests that contains..The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the Passive SQL Injection signature pack (SQL Injection) Web Application Firewall ’ group by userid having 1=1– OS Agnostic blocked high http://Google.com blocked Passive SQL Injection 2021-02-28 16:33:41 http://Google.com/contact 2021-02-28T14:33:41.476Z
http://Google.com/ SQL Injection DB Agnostic http://Google.com/ N/A id_001 tel post Create a WAF Security rule to block incoming requests that contains.The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the MSSQL Injection signature pack (SQL Injection) Web Application Firewall ) waitfor delay ‘0:0:20’ OS Agnostic blocked high http://Google.com blocked MSSQL Injection 2021-02-28 16:33:41 http://Google.com/ 2021-02-28T14:33:41.479Z
http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 SQL Injection DB Agnostic http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the Passive SQL Injection signature pack (SQL Injection) Web Application Firewall ’ or 1=1– OS Agnostic blocked high http://Google.com blocked Passive SQL Injection 2021-02-28 16:33:41 http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 2021-02-28T14:33:41.481Z
http://Google.com/ SQL Injection DB Agnostic http://Google.com/ N/A id_001 hidden post Create a WAF Security rule to block incoming requests that contains:.Validate that the specific input/url is protected with the Passive SQL Injection signature pack (SQL Injection) Web Application Firewall ’ OR ‘something’ like ‘some%’ OS Agnostic blocked high http://Google.com blocked Passive SQL Injection 2021-02-28 16:33:41 http://Google.com/ 2021-02-28T14:33:41.480Z
http://Google.com/team/ruba SQL Injection DB Agnostic http://Google.com/team/ruba N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the Passive SQL Injection signature pack (SQL Injection) Web Application Firewall ’ union select from users where login = char(114,111,111,116); OS Agnostic blocked high http://Google.com blocked Passive SQL Injection 2021-02-28 16:33:41 http://Google.com/team/ruba 2021-02-28T14:33:41.481Z
http://Google.com/team/ruba SQL Injection DB Agnostic http://Google.com/team/ruba N/A id_001 password post Create a WAF Security rule to block incoming requests that contains. The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the General SQL Injection signature pack (SQL Injection) Web Application Firewall ’ AND 1=utl_inaddr.get_host_address((SELECT SYS.DATABASE_NAME FROM DUAL)) AND ‘i’=’i OS Agnostic blocked high http://Google.com blocked General SQL Injection 2021-02-28 16:33:41 http://Google.com/team/ruba 2021-02-28T14:33:41.479Z
http://Google.com/contact SQL Injection DB Agnostic http://Google.com/contact N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the Generic Blind Injection signature pack (SQL Injection) Web Application Firewall ;waitfor delay ‘0:0:TIME’– OS Agnostic blocked high http://Google.com blocked Generic Blind Injection 2021-02-28 16:33:41 http://Google.com/contact 2021-02-28T14:33:41.480Z
http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 SQL Injection DB Agnostic http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 N/A id_001 password post Create a WAF Security rule to block incoming requests that contains. The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the MSSQL Injection signature pack (SQL Injection) Web Application Firewall waitfor delay ‘0:0:20’ / OS Agnostic blocked high http://Google.com blocked MSSQL Injection 2021-02-28 16:33:41 http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 2021-02-28T14:33:41.481Z
http://Google.com/team/dudi SQL Injection DB Agnostic http://Google.com/team/dudi N/A id_001 password post Create a WAF Security rule to block incoming requests that contains:.Validate that the specific input/url is protected with the Mysql Injection signature pack (SQL Injection) Web Application Firewall 1or1=1 OS Agnostic blocked high http://Google.com blocked Mysql Injection 2021-02-28 16:33:41 http://Google.com/team/dudi 2021-02-28T14:33:41.483Z
http://Google.com/team/dudi SQL Injection DB Agnostic http://Google.com/team/dudi N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the Generic Blind Injection signature pack (SQL Injection) Web Application Firewall ) or sleep(TIME)=’ OS Agnostic blocked high http://Google.com blocked Generic Blind Injection 2021-02-28 16:33:41 http://Google.com/team/dudi 2021-02-28T14:33:41.483Z
http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 SQL Injection DB Agnostic http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the Passive SQL Injection signature pack (SQL Injection) Web Application Firewall @var select @var as var into temp end – OS Agnostic blocked high http://Google.com blocked Passive SQL Injection 2021-02-28 16:33:41 http://Google.com/team/%d7%94%d7%9e%d7%a8%d7%a4%d7%90%d7%95%d7%aa-%d7%a9%d7%9c%d7%a0%d7%95 2021-02-28T14:33:41.485Z
http://Google.com/team/ruba SQL Injection DB Agnostic http://Google.com/team/ruba N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the Oracle SQL Injection signature pack (SQL Injection) Web Application Firewall AND 1=utl_inaddr.get_host_address((SELECT DISTINCT(granted_role) FROM (SELECT DISTINCT(granted_role), ROWNUM AS LIMIT FROM dba_role_privs WHERE GRANTEE=SYS.LOGINUSER) WHERE LIMIT=1)) AND ‘i’=’i OS Agnostic blocked high http://Google.com blocked Oracle SQL Injection 2021-02-28 16:33:41 http://Google.com/team/ruba 2021-02-28T14:33:41.482Z
http://Google.com/signup SQL Injection DB Agnostic http://Google.com/signup N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the Mysql Injection signature pack (SQL Injection) Web Application Firewall create table myfile (input TEXT); load data infile filepath into table myfile OS Agnostic blocked high http://Google.com blocked Mysql Injection 2021-02-28 16:33:41 http://Google.com/signup 2021-02-28T14:33:41.484Z
http://Google.com/contact SQL Injection DB Agnostic http://Google.com/contact N/A id_001 password post Create a WAF Security rule to block incoming requests that contains. The rule could be a Regular expression that needs to be implemented or an update of your WAF.Validate that the specific input/url is protected with the MSSQL Injection signature pack (SQL Injection) Web Application Firewall and 0=benchmark OS Agnostic blocked high http://Google.com blocked MSSQL Injection 2021-02-28 16:33:41 http://Google.com/contact 2021-02-28T14:33:41.486Z
http://Google.com/ SQL Injection DB Agnostic http://Google.com/ N/A id_001 tel post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the MSSQL Injection signature pack (SQL Injection) Web Application Firewall ; exec master..xp_cmdshell ‘ping 1.2.3.4’– OS Agnostic blocked high http://Google.com blocked MSSQL Injection 2021-02-28 16:33:41 http://Google.com/ 2021-02-28T14:33:41.485Z
http://Google.com/signup SQL Injection DB Agnostic http://Google.com/signup N/A id_001 password post Create a WAF Security rule to block incoming requests that contains.Validate that the specific input/url is protected with the MSSQL Injection signature pack (SQL Injection) Web Application Firewall insert into mysql.user (user, host, password) values (‘name’, ‘localhost’, password(‘pass123’)) – OS Agnostic N/A high http://Google.com blocked MSSQL Injection 2021-02-28 16:33:41 http://Google.com/signup 2021-02-28T14:33:41.487Z

cymulate-simulations-id-list


Retrieve a list of all simulations IDs.

Base Command

cymulate-simulations-id-list

Input

Argument Name Description Required
module Module to retrieve simulations IDs to. Possible values are: web-gateway, exfiltration, email-gateway, endpoint-security, waf, kill-chain, immediate-threats, phishing-awareness, lateral-movement. Required
from_date From which date to fetch data. Format: YYYY-MM-DD, for example: March 1st 2021 should be written: 2021-03-01. . Required
to_date End date to fetch data. Format: YYYY-MM-DD, for example: March 1st 2021 should be written: 2021-03-01. If no argument is given, default is now. Optional

Context Output

Path Type Description
Cymulate.Simulations.ID String Attack ID.
Cymulate.Simulations.Timestamp String Attack timestamp
Cymulate.Simulations.Agent String Agent connected to the attack.
Cymulate.Simulations.Template String Attack template.

Command Example

!cymulate-simulations-id-list module="kill-chain" from_date="2021-01-01"

Context Example

{
    "Cymulate": {
        "Simulations": [
            {
                "Agent": "Cymulate_agent_2",
                "ID": "id_b1",
                "Template": "Cobalt Group",
                "Timestamp": "2021-03-01 10:15:58.230000"
            },
            {
                "Agent": "Cymulate_agent_2",
                "ID": "id_b2",
                "Template": "Cobalt Group",
                "Timestamp": "2021-02-25 16:56:33.871000"
            },
            {
                "Agent": "info@cymulate.com",
                "ID": "id_b3",
                "Template": "Cobalt Group",
                "Timestamp": "2021-02-23 11:00:50.988000"
            }
        ]
    }
}

Human Readable Output

Displaying 3/3 Attack IDs

Agent ID Template Timestamp
Cymulate_agent_2 id_b1 Cobalt Group 2021-03-01 10:15:58.230000
Cymulate_agent_2 id_b2 Cobalt Group 2021-02-25 16:56:33.871000
info@cymulate.com id_b3 Cobalt Group 2021-02-23 11:00:50.988000

Configuration parameters

  • credentials
  • base_url — Base URL
  • isFetch — Fetch incidents
  • fetchCategory — Fetch category
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • first_fetch — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)
  • max_fetch — Max Fetch
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • api_key — API token (Deprecated)

Commands (29)

  • cymulate-agent-list

    Retrieve all agents.

  • cymulate-email-gateway-start

    Create a new email gateway assessment.

  • cymulate-email-gateway-status

    Get the email gateway assessment status.

  • cymulate-email-gateway-stop

    Stop a running exfiltration assessment.

  • cymulate-email-gateway-template-list

    Retrieve the email gateway template list.

  • cymulate-endpoint-security-start

    Create a new endpoint security assessment.

  • cymulate-endpoint-security-status

    Get the endpoint security assessment status.

  • cymulate-endpoint-security-stop

    Stop a running endpoint security assessment.

  • cymulate-endpoint-security-template-list

    Retrieve the endpoint security template list.

  • cymulate-exfiltration-start

    Create a new exfiltration assessment.

  • cymulate-exfiltration-status

    Get exfiltration assessment status.

  • cymulate-exfiltration-stop

    Stop a running exfiltration assessment.

  • cymulate-exfiltration-template-list

    Retrieve the exfiltration template list.

  • cymulate-immediate-threat-start

    Create a new immediate threats assessment.

  • cymulate-immediate-threat-status

    Get immediate threats assessment status.

  • cymulate-immediate-threat-stop

    Stop a running immediate threats assessment.

  • cymulate-lateral-movement-start

    Create a new lateral movement assessment.

  • cymulate-lateral-movement-status

    Get lateral movement assessment status.

  • cymulate-lateral-movement-stop

    Stop a running lateral movement assessment.

  • cymulate-lateral-movement-template-list

    Retrieve lateral movement template list.

  • cymulate-phishing-awareness-contacts-get

    Get contacts group using a group ID.

  • cymulate-phishing-awareness-contacts-group-create

    Create new contacts group.

  • cymulate-phishing-awareness-contacts-group-list

    Get a list of contact groups.

  • cymulate-simulations-id-list

    Retrieve a list of all simulations IDs.

  • cymulate-simulations-list

    Retrieve a list of all simulations by ID.

  • cymulate-waf-start

    Create a new web application firewall assessment.

  • cymulate-waf-status

    Get the web application firewall assessment status.

  • cymulate-waf-stop

    Stop a running web application firewall assessment.

  • cymulate-waf-template-list

    Retrieve the WAF template list.

import copy
import json
import traceback
from datetime import date
from typing import Any

import urllib3
from CommonServerPython import *  # pylint: disable=unused-wildcard-import

# Disable insecure warnings
urllib3.disable_warnings()

DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
CY_GENERAL_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
CY_UNIQUE_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"

MAX_INCIDENTS_TO_FETCH = 35
MAX_EVENTS_TO_DISPLAY = 20

SEVERITIES = ["Low", "Medium", "High", "Critical"]
ACCESSED_STATUS = ["penetrated", "accessed", "executed completely", "exfiltrated", "completed"]
ENDPOINT_DICT = {
    "web-gateway": "browsing",
    "exfiltration": "dlp",
    "email-gateway": "mail",
    "endpoint-security": "edr",
    "waf": "waf",
    "kill-chain": "apt",
    "immediate-threats": "immediate-threats",
    "phishing-awareness": "phishing",
    "lateral-movement": "hopper",
}


class Client(BaseClient):
    """
    Client for Cymulate RESTful API.

    Args:
          base_url (str): Cymulate server url.
          token (str): Cymulate access token.
          verify (bool): Whether the request should verify the SSL certificate.
          proxy (bool): specifies if to use XSOAR proxy settings.
    """

    def __init__(self, base_url: str, token: str, verify: bool, proxy: bool, **kwargs):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy, **kwargs)
        self.headers = {"x-token": token, "accept": "*/*", "Content-Type": "application/json"}

    def validate(self):
        """Helper function for test_module that creates a simple API call"""
        return self._http_request(method="GET", url_suffix="/user/modules", headers=self.headers, resp_type="response")

    def start_assessment(self, endpoint: str | None, data: dict):
        """Start new assessment.

        Args:
            endpoint (str): Cymulate's endpoint to start the assessment on.
            data (dict): Dictionary containing all relevant data for running new assessment.
        """
        return self._http_request(method="POST", url_suffix=f"{endpoint}/start", headers=self.headers, data=json.dumps(data))

    def stop_assessment(self, endpoint: str | None):
        """Stop a running assessment.

        Args:
            endpoint (str): The Cymulate endpoint to stop the assessment.
        """
        return self._http_request(method="POST", url_suffix=f"/{endpoint}/stop", headers=self.headers)

    def get_assessment_status(self, endpoint: str | None, assessment_id: str | None):
        """Retrieve an assessment status.

        Args:
            endpoint (str): Cymulate's endpoint to get the assessment status for.
            assessment_id (str): The assessment ID to get the status to.
        """
        return self._http_request(
            method="GET", url_suffix=f"/{endpoint}/status", headers=self.headers, params={"id": assessment_id}
        )

    def list_templates(self, endpoint: str | None):
        """Retrieve a list af all Cymulate templates to run the assessments on.

        Args:
            endpoint (str): The Cymulate endpoint templates list.
        """
        return self._http_request(method="GET", url_suffix=f"/{endpoint}/templates", headers=self.headers)

    def list_phishing_contacts(self):
        """Retrieve a list af all phishing contacts."""
        return self._http_request(method="GET", url_suffix="/phishing/contacts/groups", headers=self.headers)

    def get_phishing_contacts(self, group_id: str | None):
        """Retrieve a list of phishing contacts by group ID."""
        return self._http_request(
            method="GET", url_suffix="/phishing/contacts", headers=self.headers, params={"groupId": group_id}
        )

    def create_phishing_contacts(self, group_name: str | None):
        """Create phishing contacts group."""
        return self._http_request(
            method="POST", url_suffix="/phishing/contacts/group", headers=self.headers, params={"groupName": group_name}
        )

    def get_agents(self):
        """Retrieve a list af all agents."""
        return self._http_request(method="GET", url_suffix="/agents/get/", headers=self.headers, resp_type="json")

    def list_attacks(self, endpoint: str | None):
        """Retrieves attacks by module.

        Args:
            endpoint (str): Cymulate's endpoint to list attacks by module.
        """
        response = self._http_request(method="GET", url_suffix=f"/{endpoint}/attacks/technical", headers=self.headers)
        return response.get("data")

    def list_attack_ids_by_date(self, endpoint: str | None, from_date: str | None, to_date: str | None = None):
        """Retrieves attack IDs by their dates.

        Args:
            endpoint (str): Cymulate's endpoint to list attacks.
            from_date (str): From which date to fetch data.
            to_date (str): End date to fetch data. If no argument is given, value will be now.
        """
        to_date = to_date if to_date else date.today().strftime("%Y-%m-%d")
        response = self._http_request(
            method="GET",
            url_suffix=f"/{endpoint}/history/get-ids",
            params={"fromDate": from_date, "toDate": to_date},
            headers=self.headers,
        )
        return dict_safe_get(dict_object=response, keys=["data", "attack"], return_type=list)

    def list_immediate_threats_ids_by_date(self, from_date: str | None, to_date: str | None):
        """Retrieves immediate threats attack IDs by their dates.

        Args:
            from_date (str): From which date to fetch data.
            to_date (str): End date to fetch data. If no argument is given, value will be now.
        """
        response = self._http_request(
            method="GET",
            url_suffix="/immediate-threats/ids",
            params={"fromDate": from_date, "toDate": to_date},
            headers=self.headers,
        )
        return response.get("data")

    def list_attack_ids(self, endpoint: str | None):
        """Retrieves all attack IDs.

        Args:
            endpoint (str): The Cymulate endpoint to list attacks.
        """
        response = self._http_request(method="GET", url_suffix=f"/{endpoint}/ids", headers=self.headers)
        return response.get("data")

    def get_attack_by_id(self, endpoint: str | None, attack_id: str | None):
        """Retrieves data regarding an attack by the attack ID.

        Args:
            endpoint (str): The Cymulate endpoint to retrieve attacks data from.
            attack_id (str): ID of the attack to retrieve data to.
        """
        response = self._http_request(method="GET", url_suffix=f"/{endpoint}/attack/technical/{attack_id}", headers=self.headers)
        return response.get("data")

    def get_immediate_threat_assessment(self, attack_id: str | None):
        """Retrieves data regarding immediate threats attack by the attack ID.

        Args:
            attack_id (str): ID of the attack to retrieve data to.
        """
        response = self._http_request(
            method="GET", url_suffix=f"/immediate-threats/attack/technical/{attack_id}", headers=self.headers
        ).get("data")
        return response.get("payloads")

    def get_simulations_by_id(self, endpoint: str | None, attack_id: str | None):
        """Retrieves all event data."""
        return self._http_request(
            method="GET", url_suffix=f"{endpoint}/history/technical/{attack_id}", headers=self.headers, params={"id": attack_id}
        )


""" HELPER FUNCTIONS """


def extract_status_commands_output(result: dict) -> dict:
    """Parse the dictionary returned from the API call to a XSOAR output.

    Args:
        result (dict): API call result.
    """
    output = {
        "id": result.get("id") if result.get("id") else result.get("_id"),
        "inProgress": result.get("inProgress"),
        "progress": result.get("progress"),
        "categories": result.get("categories"),
        "addresses": result.get("addresses"),
    }
    return remove_empty_elements(output)


def extract_template_output(raw_response: dict):
    """Helper function to replace all the `_id` key to `id`.

    Args:
        raw_response (dict): Raw response returned from the API.

    Returns:
        list[Dict]: The raw response where the `_id` key has replaced with `id`.
    """
    outputs = copy.deepcopy(raw_response)
    for dictionary in outputs:
        dictionary["id"] = dictionary.pop("_id", None)
    return outputs


def validate_timestamp(timestamp: Any) -> bool:
    """
        Helper function to validate the input timestamp format. Cymulate API can return empty
        timestamp or an invalid string (for example the string 'no timestamp').

    Args:
        timestamp: input timestamp

    Returns:
        bool: True if the input is in valid format, else False.
    """
    try:
        if re.match(r"\d{4}-\d{2}-\d{2}", timestamp):
            return True
    except Exception:  # pylint: disable=broad-except
        return False
    return False


def get_alerts_by_module(client: Client, module_name: str, last_fetch: int) -> tuple[List[Any], int, int, int]:
    """Helper function to retrieves raw data from the API according to the module currently fetched,
    and using format_incidents() function to format the raw data into XSOAR incident format.

    Args:
        client (Client): Cymulate client.
        module_name (str): The module we are currently fetching.
        last_fetch (int): Timestamp in milliseconds on when to start fetching incidents.

    Returns:
        list: incidents,
        int: event_offset,
        str: alert_created_time,
        int: len(events)

    """

    raw_data = []
    timestamp_endpoint = None
    event_offset = get_integration_context().get("offset", 0)

    if module_name in ("web-gateway", "exfiltration", "endpoint-security"):
        raw_data = client.list_attacks(ENDPOINT_DICT.get(module_name))

    elif module_name == "email-gateway":
        raw_data = client.list_attacks(ENDPOINT_DICT.get(module_name))
        timestamp_endpoint = "Email_Received"

    elif module_name == "waf":
        id_data = client.list_attack_ids(module_name)

        # Extracting the attack data for each site ID.
        for cur_id in id_data:
            raw_data.extend(client.get_attack_by_id(ENDPOINT_DICT.get(module_name), cur_id.get("Id")))

    elif module_name == "kill-chain":
        id_data = client.list_attack_ids(ENDPOINT_DICT.get(module_name))

        # Kill Chain endpoint returns IDs from all times so filter by time before creating incidents
        relevant_id_list = []
        for cur_id in id_data:
            alert_created_time = int(date_to_timestamp(cur_id.get("Timestamp"), date_format=CY_GENERAL_DATE_FORMAT))
            if alert_created_time >= last_fetch:
                relevant_id_list.append(cur_id.get("Id"))

        # Extracting the attack data for each site ID.
        for cur_id in relevant_id_list:
            raw_data.extend(client.get_attack_by_id(ENDPOINT_DICT.get(module_name), cur_id))

    elif module_name == "immediate-threats":
        from_date = timestamp_to_datestring(last_fetch, "%Y-%m-%d")
        id_data = client.list_attack_ids_by_date(ENDPOINT_DICT.get(module_name), from_date)

        # Immediate threats endpoint returns IDs from all times so filter by time.
        relevant_id_list = []
        for cur_id in id_data:
            alert_created_time = int(date_to_timestamp(cur_id.get("Timestamp"), date_format=CY_UNIQUE_DATE_FORMAT))
            if alert_created_time >= last_fetch:
                relevant_id_list.append(cur_id.get("Id"))

        # Extracting the attack data for each site ID.
        site_id_list = format_id_list(id_data)

        for cur_id in site_id_list:
            raw_data.extend(client.get_immediate_threat_assessment(cur_id))

    elif module_name in ("phishing-awareness", "lateral-movement"):
        from_date = timestamp_to_datestring(last_fetch, "%Y-%m-%d")
        id_data = client.list_attack_ids_by_date(ENDPOINT_DICT.get(module_name), from_date)

        # Extracting the attack data for each site ID.
        site_id_list = format_id_list(id_data)

        for cur_id in site_id_list:
            raw_data.extend(client.get_attack_by_id(ENDPOINT_DICT.get(module_name), cur_id))

        if module_name == "phishing-awareness":
            timestamp_endpoint = "Campaign_Start_Timestamp"

    return format_incidents(raw_data, event_offset, last_fetch, module_name, timestamp_endpoint)


def format_id_list(id_data_dict: dict) -> list:
    """Helper function to create an ID list from an ID data dictionary.

    Args:
        id_data_dict (dict): Dictionary containing full IDs data.

    Returns:
        list: containing IDs only.
    """
    id_list = []

    for id_data in id_data_dict:
        # The API might return ID, Id or id as key, so converting dict keys to be lowercase.
        data = {k.lower(): v for k, v in id_data.items()}
        id_list.append(data.get("id", None))

    # Remove None objects from ID list, if exists.
    id_list = [id for id in id_list if id]
    return id_list


def format_incidents(
    events: list, event_offset: int, last_fetch: int, module_name: str, timestamp_endpoint: str = None
) -> tuple[List[Any], int, int, int]:
    """
    This function loops over the alerts list and create incidents from different events.
    For `Endpoint Security` and `Kill Chain` modules, if current event name is identical to previous
    event name, then both are part of the same incident and we will only update the existing
    incident description and won't create a new incident.

    Args:
        events (list): Events list to create incidents from.
        event_offset (int): Event offset.
        last_fetch (int): Timestamp in milliseconds on when to start fetching incidents.
        module_name (str): Module name.
        timestamp_endpoint(str): What API endpoint represent the event timestamp. If None is given,
                                timestamp endpoint from API can be 'Timestamp' or 'Attack_Timestamp'

    Returns:
        list: incidents,
        int: event_offset,
        str: alert_created_time,
        int: len(events)
    """
    incidents: List[Any] = []
    event_counter = 0
    offset = event_offset
    alert_created_time = last_fetch
    max_alert_created_time = alert_created_time
    for event in events[offset:]:
        # if current event name is identical to previous, then only update incident description.
        if (module_name in ("endpoint-security", "kill-chain")) and (
            incidents and extract_event_name(event, module_name) == incidents[-1].get("name")
        ):
            event_offset += 1
            last_incident = incidents[-1]
            alert_created_time = last_incident.get("occurred")

            if module_name == "endpoint-security":
                alert_created_time = int(date_to_timestamp(alert_created_time, date_format=DATE_FORMAT))
            elif module_name == "kill-chain":
                alert_created_time = int(date_to_timestamp(alert_created_time, date_format=CY_GENERAL_DATE_FORMAT))

            step_num = event.get("Scenario_Counter") if module_name == "endpoint-security" else event.get("Stage_Phase")

            data = json.loads(last_incident.get("rawJSON"))
            current_description = data.get("description")
            new_description = f"\nStep {step_num}:\n{extract_event_description(event)}"

            data["description"] = f"{current_description}{new_description}" if current_description else new_description

            # Insert new description to the previous incident.
            last_incident["rawJSON"] = json.dumps(data)
            incidents[-1] = last_incident

            # Keep track on the latest incident timestamp. Events that are part of an incident are
            # returned without timestamp so we only use the first the event's timestamp (First step)
            if alert_created_time > max_alert_created_time:
                max_alert_created_time = alert_created_time

        # The current event is new (has new name), then we need to build a new incident.
        else:
            if event_counter >= min(MAX_INCIDENTS_TO_FETCH, int(demisto.params().get("max_fetch", MAX_INCIDENTS_TO_FETCH))):
                break

            # Incrementing the event offset, regardless of whether new incident will be created.
            event_offset += 1

            # If attack status is identical to previous assessment status, or the current attack was
            # unsuccessful, we won't create incident.
            if not event_status_changed(event):
                continue

            if timestamp_endpoint is None:
                t_stamp = event.get("Timestamp") if event.get("Timestamp") else event.get("Attack_Timestamp")
            else:
                t_stamp = event.get(timestamp_endpoint)

            # Validate API timestamp.
            if validate_timestamp(t_stamp):
                try:
                    alert_created_time = date_to_timestamp(t_stamp, date_format=CY_GENERAL_DATE_FORMAT)
                except Exception:
                    alert_created_time = date_to_timestamp(t_stamp, date_format=CY_UNIQUE_DATE_FORMAT)

                # If current alert was created since last fetch time, create XS0AR incident.
                if alert_created_time >= last_fetch:
                    incidents.append(build_incident_dict(event, module_name, t_stamp))
                    event_counter += 1

                    # Keep track on the latest incident timestamp.
                    if alert_created_time > max_alert_created_time:
                        max_alert_created_time = alert_created_time

    return incidents, event_offset, max_alert_created_time, len(events)


def extract_event_name(event: dict, module_name: str) -> str:
    """Helper function to extract event name according to the event module.

    Args:
        event (dict): Full event data return from API.
        module_name (str): Module name.
    """
    event_name = f"Cymulate - {module_name} - "

    if module_name in ("web-gateway", "email-gateway", "immediate-threats"):
        event_name = f"{event_name}{event.get('Attack_Payload')}"

    elif module_name == "exfiltration":
        event_name = f"{event_name}{event.get('Phrase_Title')}-{event.get('Classification')}"

    elif module_name == "endpoint-security":
        event_name = f"{event_name}{event.get('Scenario_Title')}"

    elif module_name == "waf":
        event_name = f"{event_name}{event.get('Payload')}"

    elif module_name == "kill-chain":
        event_name = f"{event_name}{event.get('Template_Name')}"

    elif module_name == "phishing-awareness":
        event_name = f"{event_name}{event.get('User')}"

    elif module_name == "lateral-movement":
        event_name = f"{event_name}{event.get('Source_Hostname')}-{event.get('Destination_Hostname')}"

    return event_name


def extract_event_description(event) -> Any | None:
    """Helper function to extract event description.

    Args:
        event (dict): Full event data return from API.

    Returns:
        str: event description if exists, else: None.
    """
    event_description = None
    if event.get("Description"):
        event_description = event.get("Description")

    elif event.get("Phrase"):
        event_description = event.get("Phrase")

    elif event.get("Summery"):
        event_description = event.get("Summery")
    return event_description


def event_status_changed(event: dict) -> bool:
    """We only create incidents from penetrated attacks, that were not penetrated in the last
    attack scenario. This function checks if the event is penetrated for the first time.

    Args:
        event (dict): Event dictionary returned from API.

    Returns:
        bool: True if the event was penetrated for the first time, else False.
    """
    status_changed = True

    prev = event.get("PrevStatus") if event.get("PrevStatus") else event.get("Previous_Scenario_Status")
    if (prev and prev.lower() in ACCESSED_STATUS) or event.get("Previously_Phished"):
        status_changed = False

    cur = event.get("Status") if event.get("Status") else event.get("Step_Status")
    if cur and cur.lower() not in ACCESSED_STATUS:
        status_changed = False

    return status_changed


def build_incident_dict(event: dict, module_name: str, event_timestamp=None) -> dict:
    """Helper function for fetch incident that builds the incidents.

    Args:
        event (dict): Event dictionary.
        module_name (str): Module name.
        event_timestamp: Event timestamp.

    Returns:
        dict: XSOAR incident.
    """
    if event is None:
        return {}

    incident_data = {
        "cymulateStatus": event.get("Status"),
        "module": event.get("Module"),
        "user": event.get("User"),
        "lastAction": event.get("Last_Action"),
        "source": event.get("Source"),
        "testCase": event.get("Test_Case"),
        "attackType": event.get("Attack_Type"),
        "attackVector": event.get("Attack_Vector"),
        "templateName": event.get("Template_Name"),
        "inProgress": event.get("InProgress"),
        "url": event.get("Url"),
        "input": event.get("Input"),
        "sourceEmailAddress": event.get("Source_Email_Address"),
        "agentless": event.get("Agentless"),
        "analysis": event.get("Analysis"),
        "command": event.get("Command"),
        "description": extract_event_description(event),
        "md5": event.get("MD5") if event.get("MD5") else event.get("Md5"),
        "sha256": event.get("SHA256") if event.get("SHA256") else event.get("Sha256"),
        "sha1": event.get("SHA1") if event.get("SHA1") else event.get("Sha1"),
    }

    mitigation = event.get("Mitigation_Details") if event.get("Mitigation_Details") else event.get("Mitigation")
    if mitigation:
        incident_data["mitigationDetails"] = mitigation

    attack_payload = event.get("Attack_Payload")
    if attack_payload and attack_payload.startswith("http"):
        incident_data["url"] = attack_payload

    elif attack_payload:
        incident_data["attackType"] = attack_payload

    incident = {
        "name": extract_event_name(event, module_name),
        "occurred": event_timestamp,
        "severity": convert_to_xsoar_severity(event.get("Risk")),
        "rawJSON": json.dumps(remove_empty_elements(incident_data)),
    }

    if not event_timestamp:
        event_timestamp = event.get("Timestamp") if event.get("Timestamp") else event.get("Attack_Timestamp")
    if validate_timestamp(event_timestamp):
        try:
            occurred = datetime.strptime(event_timestamp, CY_GENERAL_DATE_FORMAT).strftime(DATE_FORMAT)  # CHANGED
        except Exception:
            occurred = datetime.strptime(event_timestamp, CY_UNIQUE_DATE_FORMAT).strftime(DATE_FORMAT)  # CHANGED

        incident["occurred"] = occurred

    return incident


def convert_to_xsoar_severity(severity: Any | None) -> int:
    """Maps Cymulate severity to Cortex XSOAR severity.

    Args:
        severity(str): severity as returned from Cymulate API. If API does not return severity,
                       The function will return 0.

    Returns:
        int: Cortex XSOAR Severity (1 to 4)
    """
    if severity:
        return {
            "low": IncidentSeverity.LOW,
            "medium": IncidentSeverity.MEDIUM,
            "high": IncidentSeverity.HIGH,
            "critical": IncidentSeverity.CRITICAL,
        }.get(severity.lower(), IncidentSeverity.UNKNOWN)
    return IncidentSeverity.UNKNOWN


""" COMMAND FUNCTIONS """


def test_module(client: Client) -> str:
    """Tests API connectivity and authentication. Returning 'ok' indicates that the integration
    works like it is supposed to. Connection to the service is successful.

    Args:
        client (Client): Cymulate client.
    """
    test_message = "ok"
    try:
        response = client.validate()
        if not response.ok:
            raise DemistoException("Authorization Error: make sure API Key is correctly set.", res=response)
    except DemistoException as err:
        if "Unauthorized" in str(err):
            test_message = f"Authorization Error: make sure API Key is correctly set.\n\n{err}"
        else:
            raise

    return test_message


def list_exfiltration_template_command(client: Client) -> CommandResults:
    """Retrieve a list of all exfiltration templates.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all exfiltration templates.

    """
    raw_response = client.list_templates(ENDPOINT_DICT.get("exfiltration"))
    outputs = extract_template_output(raw_response.get("data"))

    readable_output = tableToMarkdown("Exfiltration templates list:", outputs, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.Exfiltration.Template",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def start_exfiltration_assessment_command(
    client: Client, template_id: str, agent_name: str, schedule: bool, schedule_loop: str, agent_profile_name: str = None
) -> CommandResults:
    """Start a new exfiltration assessment.

    Args:
        client (Client): Cymulate client.
        template_id (str): The ID of the template to run the exfiltration Assessment with.
        agent_name (str): Agent name to run simulation attacks.
        agent_profile_name (str): Agent profile name to run simulation attacks on.
        schedule (bool): Whether to schedule the automated assessment periodically.
        schedule_loop (str): Loop size of the scheduled agent.
                             For example: to run the agent only once, use the value 'one-time'.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the start assessment data.

    """
    if agent_profile_name is not None:
        agent_profile_name = agent_profile_name.replace('"', "")
    params = {
        "templateID": template_id,
        "agentName": agent_name,
        "agentProfileName": agent_profile_name,
        "schedule": schedule,
        "scheduleLoop": schedule_loop,
    }

    raw_response = client.start_assessment(ENDPOINT_DICT.get("exfiltration"), params)
    output = {
        "id": raw_response.get("data"),
        "success": raw_response.get("success"),
    }

    readable_output = tableToMarkdown("Starting exfiltration assessment:", output)
    command_results = CommandResults(
        outputs_prefix="Cymulate.Exfiltration",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def stop_exfiltration_assessment_command(client: Client) -> CommandResults:
    """Stop a running exfiltration assessment.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the stop assessment data.
    """
    raw_response = client.stop_assessment(ENDPOINT_DICT.get("exfiltration"))
    readable_output = tableToMarkdown("Stopping exfiltration assessment:", raw_response)

    command_results = CommandResults(
        outputs_prefix="Cymulate.Exfiltration",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def get_exfiltration_assessment_status_command(client: Client, assessment_id: str) -> CommandResults:
    """Retrieve exfiltration assessment status.

    Args:
        client (Client): Cymulate client.
        assessment_id (str): The ID of the assessment to retrieve the status to.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the assessment status.

    """
    raw_response = client.get_assessment_status(ENDPOINT_DICT.get("exfiltration"), assessment_id)
    outputs = extract_status_commands_output(raw_response)
    readable_output = tableToMarkdown("Exfiltration assessment status:", outputs, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.Exfiltration",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def list_email_gateway_template_command(client: Client) -> CommandResults:
    """Retrieve a list of all email gateway templates.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all email-gateway templates.
    """
    raw_response = client.list_templates(ENDPOINT_DICT.get("email-gateway"))
    outputs = extract_template_output(raw_response)
    readable_output = tableToMarkdown("Email gateway templates list:", outputs, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.EmailGateway.Template",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def start_email_gateway_assessment_command(
    client: Client, template_id: str, agent_email: str, schedule: bool, schedule_loop: str
) -> CommandResults:
    """Start a new email gateway assessment.

    Args:
        client (Client): Cymulate client.
        template_id (str): The ID of the template to run the assessment with.
        agent_email (str): Agent email to run simulation attacks.
        schedule (bool): Whether to schedule the automated assessment periodically.
        schedule_loop (str): Loop size of the scheduled agent.
                             For example: to run the agent only once, use the value 'one-time'.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the start assessment data.
    """

    params = {"templateID": template_id, "agentEmail": agent_email, "schedule": schedule, "scheduleLoop": schedule_loop}

    raw_response = client.start_assessment(ENDPOINT_DICT.get("email-gateway"), params)
    output = {"id": raw_response.get("data"), "success": raw_response.get("success")}

    readable_output = tableToMarkdown("Starting email gateway assessment:", output)
    command_results = CommandResults(
        outputs_prefix="Cymulate.EmailGateway",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def stop_email_gateway_assessment_command(client: Client) -> CommandResults:
    """Stop a running email gateway assessment.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the stop assessment data.
    """
    raw_response = client.stop_assessment(ENDPOINT_DICT.get("email-gateway"))
    readable_output = tableToMarkdown("Stopping email gateway assessment:", raw_response)

    command_results = CommandResults(
        outputs_prefix="Cymulate.EmailGateway",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def get_email_gateway_assessment_status_command(client: Client, assessment_id: str) -> CommandResults:
    """Retrieve email gateway assessment status.

    Args:
        client (Client): Cymulate client.
        assessment_id (str): The ID of the assessment to retrieve the status to.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the email gateway assessment status.
    """
    outputs = {}
    raw_response = client.get_assessment_status(ENDPOINT_DICT.get("email-gateway"), assessment_id)
    output = copy.deepcopy(raw_response).get("data")
    if output:
        outputs = extract_status_commands_output(output[0])

    readable_output = tableToMarkdown("Email gateway assessment status:", outputs, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.EmailGateway",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def list_endpoint_security_template_command(client: Client) -> CommandResults:
    """Retrieve a list of all endpoint security templates.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all endpoint security templates.
    """
    raw_response = client.list_templates(ENDPOINT_DICT.get("endpoint-security"))
    outputs = extract_template_output(raw_response)

    readable_output = tableToMarkdown("Endpoint security templates list:", outputs, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.EndpointSecurity.Template",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def start_endpoint_security_assessment_command(
    client: Client, template_id: str, agent_name: str, schedule: bool, schedule_loop: str, agent_profile_name: str = None
) -> CommandResults:
    """Start a new endpoint security assessment.

    Args:
        client (Client): Cymulate client.
        template_id (str): The ID of the template to run the assessment with.
        agent_name (str): Agent's name to run simulation attacks with.
        agent_profile_name (str): Agent profile name to run simulation attacks on
        schedule (bool): Whether to schedule the automated assessment periodically.
        schedule_loop (str): Loop size of the scheduled agent.
                             For example: to run the agent only once, use the value 'one-time'.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the start assessment data.
    """
    if agent_profile_name is not None:
        agent_profile_name = agent_profile_name.replace('"', "")
    params = {
        "templateID": template_id,
        "agentName": agent_name,
        "agentProfileName": agent_profile_name,
        "schedule": schedule,
        "scheduleLoop": schedule_loop,
    }

    raw_response = client.start_assessment(ENDPOINT_DICT.get("endpoint-security"), params)
    output = {"id": raw_response.get("data"), "success": raw_response.get("success")}

    readable_output = tableToMarkdown("Starting endpoint security assessment:", output)
    command_results = CommandResults(
        outputs_prefix="Cymulate.EndpointSecurity",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def stop_endpoint_security_assessment_command(client: Client) -> CommandResults:
    """Stop a running endpoint security assessment.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the stop assessment data.

    """

    raw_response = client.stop_assessment(ENDPOINT_DICT.get("endpoint-security"))
    readable_output = tableToMarkdown("Stopping endpoint security assessment:", raw_response)

    command_results = CommandResults(
        outputs_prefix="Cymulate.EndpointSecurity",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def get_endpoint_security_assessment_status_command(client: Client, assessment_id: str) -> CommandResults:
    """Retrieve endpoint security assessment status.

    Args:
        client (Client): Cymulate client.
        assessment_id (str): The ID of the assessment to retrieve the status to.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the assessment status.

    """
    endpoint = ENDPOINT_DICT.get("endpoint-security")
    raw_response = client.get_assessment_status(endpoint, assessment_id)
    output = extract_status_commands_output(raw_response)

    readable_output = tableToMarkdown("Endpoint security assessment status:", output, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.EndpointSecurity",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def list_waf_template_command(client: Client) -> CommandResults:
    """Retrieve a list of all WAF templates.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all WAF templates.

    """

    raw_response = client.list_templates(ENDPOINT_DICT.get("waf"))
    output = copy.deepcopy(raw_response).get("data")
    for dict in output:
        dict["id"] = dict.pop("_id")
    readable_output = tableToMarkdown("WAF templates list:", output, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.WAF.Template",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def start_waf_assessment_command(client: Client, template_id: str, sites, schedule: bool, schedule_loop: str) -> CommandResults:
    """Start a new WAF assessment.

    Args:
        client (Client): Cymulate client.
        template_id (str): The ID of the template to run the assessment with.
        sites (list[str] or str): Sites to run the assessment on.
        schedule (bool): Whether to schedule the automated assessment periodically.
        schedule_loop (str): Loop size of the scheduled agent.
                             For example: to run the agent only once, use the value 'one-time'.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the start assessment data.

    """
    sites = argToList(sites)
    params = {"templateID": template_id, "sites": sites, "schedule": schedule, "scheduleLoop": schedule_loop}

    raw_response = client.start_assessment(ENDPOINT_DICT.get("waf"), params)
    output = {"id": raw_response.get("data"), "success": raw_response.get("success")}

    readable_output = tableToMarkdown("Starting WAF assessment:", output)
    command_results = CommandResults(
        outputs_prefix="Cymulate.WAF",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def stop_waf_assessment_command(client: Client) -> CommandResults:
    """Stop a running WAF assessment.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the stop assessment data.

    """

    raw_response = client.stop_assessment(ENDPOINT_DICT.get("waf"))
    readable_output = tableToMarkdown("Stopping WAF assessment:", raw_response)

    command_results = CommandResults(
        outputs_prefix="Cymulate.WAF",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def get_waf_assessment_status_command(client: Client, assessment_id: str) -> CommandResults:
    """Retrieve WAF assessment status.

    Args:
        client (Client): Cymulate client.
        assessment_id (str): The ID of the assessment to retrieve the status to.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the assessment status.

    """
    endpoint = ENDPOINT_DICT.get("waf")
    raw_response = client.get_assessment_status(endpoint, assessment_id)
    output = extract_status_commands_output(raw_response)

    readable_output = tableToMarkdown("WAF assessment status:", output, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.WAF",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def start_immediate_threat_assessment_command(
    client: Client,
    template_id: str,
    browsing_address: str = "",
    browsing_address_profile_name: str = "",
    mail_address: str = "",
    edr_address_profile_name: str = "",
    edr_address: str = "",
) -> CommandResults:
    """Start a new immediate threats assessment.

    Args:
        client (Client): Cymulate client.
        template_id (str): The ID of the template to run the assessment with.
        browsing_address (str): Browsing address.
        browsing_address_profile_name (str): browsing Agent profile name (Optional - required on SBA only)
        mail_address (str): Agent email address.
        edr_address_profile_name (str): EDR Agent profile name (Optional - required on SBA only)
        edr_address (str): EDR Agent address.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the start assessment data.

    """
    if browsing_address == "" and mail_address == "" and edr_address == "":
        raise ValueError("At least one of the addresses arguments should be filled.")

    params = {
        "templateID": template_id,
        "mailAddress": mail_address,
        "browsingAddress": browsing_address,
        "browsingAddressProfileName": browsing_address_profile_name,
        "edrAddressProfileName": edr_address_profile_name,
        "edrAddress": edr_address,
    }

    raw_response = client.start_assessment(ENDPOINT_DICT.get("immediate-threats"), params)
    output = {"id": raw_response.get("data"), "success": raw_response.get("success")}

    readable_output = tableToMarkdown("Starting immediate-threats assessment:", output)
    command_results = CommandResults(
        outputs_prefix="Cymulate.ImmediateThreats",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def stop_immediate_threat_assessment_command(client: Client) -> CommandResults:
    """Stop the immediate threats assessment.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the stop assessment data.
    """
    raw_response = client.stop_assessment(ENDPOINT_DICT.get("immediate-threats"))

    readable_output = tableToMarkdown("Stop immediate-threats assessment:", raw_response)
    command_results = CommandResults(
        outputs_prefix="Cymulate.ImmediateThreats",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def get_immediate_threat_assessment_status_command(client: Client, assessment_id: str) -> CommandResults:
    """Retrieve the immediate threats status.

    Args:
        client (Client): Cymulate client.
        assessment_id (str): The ID of the assessment to retrieve the status to.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the assessment status.
    """
    endpoint = ENDPOINT_DICT.get("immediate-threats")
    raw_response = client.get_assessment_status(endpoint, assessment_id)

    output = extract_status_commands_output(raw_response)
    readable_output = tableToMarkdown("Immediate-threats assessment status:", output, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.ImmediateThreats",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def list_lateral_movement_template_command(client: Client) -> CommandResults:
    """Retrieve a list of all lateral movement templates.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all lateral movement templates.
    """

    raw_response = client.list_templates(ENDPOINT_DICT.get("lateral-movement"))
    outputs = extract_template_output(raw_response.get("data"))

    readable_output = tableToMarkdown("Lateral movement templates list:", outputs, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.LateralMovement.Template",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def start_lateral_movement_assessment_command(
    client: Client,
    agent_name: str,
    template_id: str,
    upload_to_cymulate: bool,
    schedule: bool,
    schedule_loop: str,
    agent_profile_name: str = None,
) -> CommandResults:
    """Start a new lateral movement assessment.

    Args:
        client (Client): Cymulate client.
        agent_name (str): Agent name to run the assessment with.
        agent_profile_name (str): Agent profile name to run simulation attacks on.
        template_id (str): The ID of the template to run the lateral movement with.
        upload_to_cymulate (bool): Whether to upload the result to Cymulate.
        schedule (bool): Whether to schedule the automated assessment periodically.
        schedule_loop (str): Loop size of the scheduled agent.
                             For example: to run the agent only once, use the value 'one-time'.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the start assessment data.

    """
    if agent_profile_name is not None:
        agent_profile_name = agent_profile_name.replace('"', "")
    params = {
        "agentName": agent_name,
        "agentProfileName": agent_profile_name,
        "templateID": template_id,
        "uploadResultsToCymulate": upload_to_cymulate,
        "schedule": schedule,
        "scheduleLoop": schedule_loop,
    }

    raw_response = client.start_assessment(ENDPOINT_DICT.get("lateral-movement"), params)
    output = {"id": raw_response.get("data"), "success": raw_response.get("success")}

    readable_output = tableToMarkdown("Starting lateral movement assessment:", output)
    command_results = CommandResults(
        outputs_prefix="Cymulate.LateralMovement",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def stop_lateral_movement_assessment_command(client: Client) -> CommandResults:
    """Stop a running lateral movement assessment.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the stop assessment data.

    """

    raw_response = client.stop_assessment(ENDPOINT_DICT.get("lateral-movement"))
    readable_output = tableToMarkdown("Stopping lateral movement assessment:", raw_response)

    command_results = CommandResults(
        outputs_prefix="Cymulate.LateralMovement",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def get_lateral_movement_assessment_status_command(client: Client, assessment_id: str) -> CommandResults:
    """Retrieve lateral movement assessment status.

    Args:
        client (Client): Cymulate client.
        assessment_id (str): The ID of the assessment to retrieve the status to.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the assessment status.

    """
    endpoint = ENDPOINT_DICT.get("lateral-movement")
    raw_response = client.get_assessment_status(endpoint, assessment_id)
    output = extract_status_commands_output(raw_response.get("data"))

    readable_output = tableToMarkdown("Lateral movement assessment status:", output, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.LateralMovement",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=output,
        raw_response=raw_response,
    )
    return command_results


def list_phishing_awareness_contact_groups_command(client: Client) -> CommandResults:
    """Retrieve a list of all phishing awareness contact groups.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all contact groups.

    """
    raw_response = client.list_phishing_contacts()
    outputs = extract_template_output(raw_response)

    readable_output = tableToMarkdown("Phishing awareness contact groups:", outputs, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.Phishing.Groups",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def get_phishing_awareness_contact_groups_command(client: Client, group_id: str) -> CommandResults:
    """Retrieve a list of all phishing awareness contacts list by contact group ID.

    Args:
        client (Client): Cymulate client.
        group_id (str): Group ID.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of contact list by contact group ID.

    """
    raw_response = client.get_phishing_contacts(group_id)
    outputs = extract_template_output(raw_response)

    readable_output = tableToMarkdown("Phishing awareness contact groups:", outputs, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.Phishing.Groups",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def add_phishing_awareness_contact_groups_command(client: Client, group_name: str) -> CommandResults:
    """Create phishing awareness contact.

    Args:
        client (Client): Cymulate client.
        group_name (str): New group's name.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of contact list by contact group ID.
    """
    raw_response = client.create_phishing_contacts(group_name)
    readable_output = tableToMarkdown("Phishing awareness contact group created:", raw_response, removeNull=True)
    command_results = CommandResults(
        outputs_prefix="Cymulate.Phishing.Groups",
        outputs_key_field="id",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def list_agents_command(client: Client) -> CommandResults:
    """Retrieve a list of all agents.

    Args:
        client (Client): Cymulate client.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all agents connected to the current user.

    """
    raw_response = client.get_agents()
    headers = ["agentAddress", "agentMethod", "agentName", "comment"]
    readable_output = tableToMarkdown("Agents list:", raw_response, headers=headers, removeNull=True)

    command_results = CommandResults(
        outputs_prefix="Cymulate.Agent",
        outputs_key_field="",
        readable_output=readable_output,
        outputs=raw_response,
        raw_response=raw_response,
    )
    return command_results


def list_attack_simulations_command(client: Client, module: str, from_date: str, to_date: str = None) -> CommandResults:
    """Retrieve a list of all simulations IDs.

    Args:
        client (Client): Cymulate client.
        module (str): Module to retrieve simulations IDs to.
        from_date (str): From which date to fetch data.
        to_date (str): End date to fetch data. If no argument is given, value will be now.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all module simulations from a specific ID.
    """
    outputs = []
    if not validate_timestamp(from_date) or (to_date and not validate_timestamp(to_date)):
        raise ValueError("Wrong date format. Year-Month-Day, for example: March 1st 2021 should be written: 2021-03-01.")
    raw_response = client.list_attack_ids_by_date(ENDPOINT_DICT.get(module), from_date, to_date)
    num_simulations_to_display = min(MAX_EVENTS_TO_DISPLAY, len(raw_response))

    if raw_response:
        for simulation_id_data in raw_response:
            data = simulation_id_data
            data["Timestamp"] = str(parse_date_string(simulation_id_data.get("Timestamp")))
            outputs.append(data)

    readable_output = tableToMarkdown(
        f"Displaying {num_simulations_to_display}/{len(raw_response)} Attack IDs:", outputs[:num_simulations_to_display]
    )

    command_results = CommandResults(
        outputs_prefix="Cymulate.Simulations",
        outputs_key_field="ID",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def list_simulations_command(client: Client, module: str, attack_id: str):
    """Retrieve a list of all simulations by ID.

    Args:
        client (Client): Cymulate client.
        module (str): Module to retrieve simulations to.
        attack_id (str): Attack ID.

    Returns:
        CommandResults: A CommandResults object that is then passed to 'return_results',
                        containing the list of all module simulations from a specific ID.
    """
    outputs = []
    raw_response = client.get_simulations_by_id(ENDPOINT_DICT.get(module), attack_id)

    raw_response = raw_response.get("data")
    num_simulations_to_display = min(MAX_EVENTS_TO_DISPLAY, len(raw_response))

    if raw_response:
        outputs = raw_response
        # Adding the attack ID to the simulation data.
        for simulation in outputs:
            simulation["Id"] = attack_id

    readable_output = tableToMarkdown(
        f"Displaying {num_simulations_to_display}/{len(raw_response)} simulations:",
        outputs[:num_simulations_to_display],
        removeNull=True,
    )

    command_results = CommandResults(
        outputs_prefix="Cymulate.Simulations",
        outputs_key_field="Id",
        readable_output=readable_output,
        outputs=outputs,
        raw_response=raw_response,
    )
    return command_results


def fetch_incidents(
    client: Client, last_run: dict[str, int], first_fetch_time: int, fetch_categories: list
) -> tuple[dict[str, int], List[dict]]:
    """
    Retrieves new incidents every interval (default is 1 minute). The function will retrieve
    incidents from all selected modules chosen in the configuration page by the user.
    the next run will be calculated by the latest timestamp of all modules, to avoid duplications.
    NOTE: We fetch only one module per fetch call.

    Args:
        client (Client): Cymulate client object
        last_run (Optional[Dict[str, int]]): Dictionary with a key containing the latest incident
                                        created time we got from last fetch.
        first_fetch_time (Optional[int]): If last_run is None (first time we are fetching), it
                                        contains the timestamp in milliseconds on when to start
                                        fetching incidents.
        fetch_categories (list): a list of selected modules chosen in the configuration page.

    Returns:
        A tuple containing two elements:
                next_run (``Dict[str, int]``): Contains the timestamp that will be used in
                        ``last_run`` on the next fetch.
                incidents (``List[dict]``): List of incidents that will be created in XSOAR
    """
    last_fetch = last_run.get("last_fetch", None)
    last_fetch = int(last_fetch) if last_fetch else first_fetch_time * 1000
    next_run = last_fetch
    context = get_integration_context()
    current_module = context.get("current_module", None)

    if current_module:
        incidents, offset, creation_time, total_simulated_events = get_alerts_by_module(client, current_module, last_fetch)

    else:
        incidents, offset, creation_time, total_simulated_events = [], 0, last_fetch, 0

    # current_time will help us save current's module time, and update next_run accordingly.
    if creation_time > context.get("current_time", last_fetch):
        current_time = creation_time
    else:
        current_time = context.get("current_time", last_fetch)

    # There are alerts left to fetch
    if total_simulated_events > offset:
        integration_context = {
            "offset": offset,
            "current_module": context.get("current_module"),
            "modules": context.get("modules"),
            "current_time": current_time,
        }
        demisto.info(f"Fetching {current_module} module. Offset: {offset}/{total_simulated_events}")

    # Finished fetching current module, checking if there are more modules to fetch.
    else:
        modules = context.get("modules", None)
        if modules:
            integration_context = {
                "offset": 0,
                "current_module": modules[0],
                "modules": modules[1:],
                "current_time": current_time,
            }

        # Finished fetching all modules, re-initializing integration context.
        else:
            integration_context = {"offset": 0, "current_module": None, "modules": fetch_categories, "current_time": current_time}

            # Updating next run time after finish fetching all modules, if needed.
            if current_time >= next_run:
                next_run = current_time + 1000
            else:
                # Increment by 1 second, only if new incidents were fetched.
                if incidents:
                    next_run = next_run + 1000

    set_integration_context(integration_context)
    return {"last_fetch": next_run}, incidents


def main() -> None:
    """main function, parses params and runs command functions"""
    params = demisto.params()

    api_key = (params.get("credentials") or {}).get("password") or params.get("api_key")
    if not api_key:
        raise Exception("API Token must be provided.")
    base_url = params.get("base_url")

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

    # How much time before the first fetch to retrieve incidents
    first_fetch_time = arg_to_datetime(arg=params.get("first_fetch", "3 days"), arg_name="First fetch time", required=True)

    first_fetch_timestamp = int(first_fetch_time.timestamp()) if first_fetch_time else None
    assert isinstance(first_fetch_timestamp, int)

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

    try:
        client = Client(
            base_url=base_url,
            token=api_key,  # type: ignore[arg-type]
            verify=verify_certificate,
            proxy=proxy,
        )

        if command == "test-module":
            return_results(test_module(client))

        elif command == "fetch-incidents":
            fetch_categories = params.get("fetchCategory", None)

            # Handles first fetch
            if get_integration_context() is None:
                set_integration_context(
                    {
                        "offset": 0,
                        "current_module": None,
                        "modules": fetch_categories,
                    }
                )

            next_run, incidents = fetch_incidents(
                client=client,
                last_run=demisto.getLastRun(),
                first_fetch_time=first_fetch_timestamp,
                fetch_categories=fetch_categories,
            )
            demisto.setLastRun(next_run)
            demisto.incidents(incidents)

        elif command == "cymulate-simulations-id-list":
            return_results(list_attack_simulations_command(client, **demisto.args()))

        elif command == "cymulate-simulations-list":
            return_results(list_simulations_command(client, **demisto.args()))

        # exfiltration
        elif command == "cymulate-exfiltration-template-list":
            return_results(list_exfiltration_template_command(client))

        elif command == "cymulate-exfiltration-start":
            return_results(start_exfiltration_assessment_command(client, **demisto.args()))

        elif command == "cymulate-exfiltration-stop":
            return_results(stop_exfiltration_assessment_command(client))

        elif command == "cymulate-exfiltration-status":
            return_results(get_exfiltration_assessment_status_command(client, **demisto.args()))

        # email-gateway
        elif command == "cymulate-email-gateway-template-list":
            return_results(list_email_gateway_template_command(client))

        elif command == "cymulate-email-gateway-start":
            return_results(start_email_gateway_assessment_command(client, **demisto.args()))

        elif command == "cymulate-email-gateway-stop":
            return_results(stop_email_gateway_assessment_command(client))

        elif command == "cymulate-email-gateway-status":
            return_results(get_email_gateway_assessment_status_command(client, **demisto.args()))

        # endpoint-security
        elif command == "cymulate-endpoint-security-template-list":
            return_results(list_endpoint_security_template_command(client))

        elif command == "cymulate-endpoint-security-start":
            return_results(start_endpoint_security_assessment_command(client, **demisto.args()))

        elif command == "cymulate-endpoint-security-stop":
            return_results(stop_endpoint_security_assessment_command(client))

        elif command == "cymulate-endpoint-security-status":
            return_results(get_endpoint_security_assessment_status_command(client, **demisto.args()))

        # waf
        elif command == "cymulate-waf-template-list":
            return_results(list_waf_template_command(client))

        elif command == "cymulate-waf-start":
            return_results(start_waf_assessment_command(client, **demisto.args()))

        elif command == "cymulate-waf-stop":
            return_results(stop_waf_assessment_command(client))

        elif command == "cymulate-waf-status":
            return_results(get_waf_assessment_status_command(client, **demisto.args()))

        # immediate_threat
        elif command == "cymulate-immediate-threat-start":
            return_results(start_immediate_threat_assessment_command(client, **demisto.args()))

        elif command == "cymulate-immediate-threat-stop":
            return_results(stop_immediate_threat_assessment_command(client))

        elif command == "cymulate-immediate-threat-status":
            return_results(get_immediate_threat_assessment_status_command(client, **demisto.args()))

        # lateral_movement
        elif command == "cymulate-lateral-movement-template-list":
            return_results(list_lateral_movement_template_command(client))

        elif command == "cymulate-lateral-movement-start":
            return_results(start_lateral_movement_assessment_command(client, **demisto.args()))

        elif command == "cymulate-lateral-movement-stop":
            return_results(stop_lateral_movement_assessment_command(client))

        elif command == "cymulate-lateral-movement-status":
            return_results(get_lateral_movement_assessment_status_command(client, **demisto.args()))

        # phishing_awareness
        elif command == "cymulate-phishing-awareness-contacts-group-list":
            return_results(list_phishing_awareness_contact_groups_command(client))

        elif command == "cymulate-phishing-awareness-contacts-get":
            return_results(get_phishing_awareness_contact_groups_command(client, **demisto.args()))

        elif command == "cymulate-phishing-awareness-contacts-group-create":
            return_results(add_phishing_awareness_contact_groups_command(client, **demisto.args()))

        # General
        elif command == "cymulate-agent-list":
            return_results(list_agents_command(client))

    except Exception as error:
        demisto.error(traceback.format_exc())

        if "unauthorized" in str(error):
            return_error(
                f"Failed to execute {command} command.\nAuthorization Error: make sure API"
                f" Key is correctly set.\n\nFull error message:\n{error}"
            )

        if "invalid attack id" in str(error):
            return_error(
                f"Failed to execute {command} command.\nPlease make sure you "
                f"entered the correct assessment id.\n\nFull error message:\n{error!s}"
            )

        return_error(f"Failed to execute {command} command.\n\nFull error message:\n{error!s}")


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